[RFC 0/3] git-svn: Add support for cherry-pick merges

4 messages, 1 author, 2016-06-15 · open the first message on its own page

[RFC 0/3] git-svn: Add support for cherry-pick merges

From: Andrew Wong <hidden>
Date: 2016-06-15 22:59:20

This is a work-in-progress for adding support for cherry-pick merges.

When using git-svn, cherry-picked commits from another git-svn branch are now
being treated as simple commits. But in SVN, if the user uses "svn merge -c" to
cherry-pick commits from another SVN branch, SVN records that information in
svn:mergeinfo. These patches will enable git-svn to do the same.

Andrew Wong (3):
  git-svn: Generate mergeinfo for every commit
  git-svn: Support cherry-pick merges
  git-svn: Add config to control the path of mergeinfo

 git-svn.perl                      | 79 ++++++++++++++++++++++++++++++++++-----
 perl/Git/SVN/Editor.pm            |  5 ++-
 t/t9161-git-svn-mergeinfo-push.sh | 67 +++++++++++++++++++++++++++++++++
 3 files changed, 141 insertions(+), 10 deletions(-)

-- 
1.8.5.rc3.5.g96ccada

[RFC 1/3] git-svn: Generate mergeinfo for every commit

From: Andrew Wong <hidden>
Date: 2016-06-15 22:59:20

The previous behavior would only generate mergeinfo once using the first
commit, and use that mergeinfo for all remaining commits. The new behavior will
generate it once for every commit.
---
 git-svn.perl | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/git-svn.perl b/git-svn.perl
index 7349ffe..9ddeaf4 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -974,8 +974,12 @@ sub cmd_dcommit {
 		} else {
 			my $cmt_rev;
 
-			unless (defined($_merge_info) || ! $push_merge_info) {
-				$_merge_info = populate_merge_info($d, $gs,
+			my $rev_merge_info;
+			if (defined($_merge_info)) {
+				$rev_merge_info = $_merge_info;
+			}
+			unless (defined($rev_merge_info) || ! $push_merge_info) {
+				$rev_merge_info = populate_merge_info($d, $gs,
 				                             $uuid,
 				                             $linear_refs,
 				                             $rewritten_parent);
@@ -993,7 +997,7 @@ sub cmd_dcommit {
 			                       print "Committed r$_[0]\n";
 			                       $cmt_rev = $_[0];
 			                },
-					mergeinfo => $_merge_info,
+					mergeinfo => $rev_merge_info,
 			                svn_path => '');
 
 			my $err_handler = $SVN::Error::handler;
-- 
1.8.5.rc3.5.g96ccada

[RFC 3/3] git-svn: Add config to control the path of mergeinfo

From: Andrew Wong <hidden>
Date: 2016-06-15 22:59:20

Instead of always storing mergeinfo at the root, give an option to store the
merge info in a subdirectory. The subdirectory must exist before we try to set
its property.
---
 git-svn.perl                      | 21 +++++++++++++++------
 perl/Git/SVN/Editor.pm            |  5 ++++-
 t/t9161-git-svn-mergeinfo-push.sh | 37 +++++++++++++++++++++++++++++++++++++
 3 files changed, 56 insertions(+), 7 deletions(-)
diff --git a/git-svn.perl b/git-svn.perl
index b04cac7..bfae579 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -693,7 +693,7 @@ sub merge_merge_info {
 }
 
 sub populate_merge_info {
-	my ($d, $gs, $uuid, $linear_refs, $rewritten_parent) = @_;
+	my ($d, $gs, $uuid, $linear_refs, $rewritten_parent, $merge_info_path) = @_;
 
 	my %parentshash;
 	read_commit_parents(\%parentshash, $d);
@@ -729,7 +729,7 @@ sub populate_merge_info {
 
 			my $ra = Git::SVN::Ra->new($branchurl);
 			my (undef, undef, $props) =
-				$ra->get_dir(canonicalize_path("."), $svnrev);
+				$ra->get_dir(canonicalize_path($merge_info_path), $svnrev);
 			my $par_mergeinfo = $props->{'svn:mergeinfo'};
 			unless (defined $par_mergeinfo) {
 				$par_mergeinfo = '';
@@ -778,7 +778,8 @@ sub populate_merge_info {
 			# We now have a list of all SVN revnos which are
 			# merged by this particular parent. Integrate them.
 			next if $#revsin == -1;
-			my $newmergeinfo = "$branchpath:" . join(',', @revsin);
+			my $newmergeinfo = canonicalize_path("$branchpath/$merge_info_path")
+				. ":" . join(',', @revsin);
 			$aggregate_mergeinfo =
 				merge_merge_info($aggregate_mergeinfo,
 								$newmergeinfo,
@@ -808,7 +809,7 @@ sub populate_merge_info {
 
 			my $ra = Git::SVN::Ra->new($branchurl);
 			my (undef, undef, $props) =
-				$ra->get_dir(canonicalize_path("."), $svnrev);
+				$ra->get_dir(canonicalize_path($merge_info_path), $svnrev);
 			my $parent_mergeinfo = $props->{'svn:mergeinfo'};
 			unless (defined $parent_mergeinfo) {
 				$parent_mergeinfo = '';
@@ -824,7 +825,7 @@ sub populate_merge_info {
 			}
 			my $cherry_branchpath = $1;
 
-			my $cherry_pick_mergeinfo = canonicalize_path("$cherry_branchpath")
+			my $cherry_pick_mergeinfo = canonicalize_path("$cherry_branchpath/$merge_info_path")
 				. ":$cherry_svnrev";
 
 			$aggregate_mergeinfo = merge_merge_info($aggregate_mergeinfo,
@@ -1008,6 +1009,12 @@ sub cmd_dcommit {
 	if (defined($_merge_info)) {
 		$_merge_info =~ tr{ }{\n};
 	}
+	my $merge_info_path = eval {
+		command_oneline(qw/config --get svn.mergeinfopath/)
+		};
+	if (not defined($merge_info_path)) {
+		$merge_info_path = "";
+	}
 	while (1) {
 		my $d = shift @$linear_refs or last;
 		unless (defined $last_rev) {
@@ -1030,7 +1037,8 @@ sub cmd_dcommit {
 				$rev_merge_info = populate_merge_info($d, $gs,
 				                             $uuid,
 				                             $linear_refs,
-				                             $rewritten_parent);
+				                             $rewritten_parent,
+							     $merge_info_path);
 			}
 
 			my %ed_opts = ( r => $last_rev,
@@ -1046,6 +1054,7 @@ sub cmd_dcommit {
 			                       $cmt_rev = $_[0];
 			                },
 					mergeinfo => $rev_merge_info,
+					mergeinfopath => $merge_info_path,
 			                svn_path => '');
 
 			my $err_handler = $SVN::Error::handler;
diff --git a/perl/Git/SVN/Editor.pm b/perl/Git/SVN/Editor.pm
index b3bcd47..dcbb8a0 100644
--- a/perl/Git/SVN/Editor.pm
+++ b/perl/Git/SVN/Editor.pm
@@ -42,6 +42,7 @@ sub new {
 	                       "$self->{svn_path}/" : '';
 	$self->{config} = $opts->{config};
 	$self->{mergeinfo} = $opts->{mergeinfo};
+	$self->{mergeinfopath} = $opts->{mergeinfopath};
 	return $self;
 }
 
@@ -484,7 +485,9 @@ sub apply_diff {
 	}
 
 	if (defined($self->{mergeinfo})) {
-		$self->change_dir_prop($self->{bat}{''}, "svn:mergeinfo",
+		my $pbat = $self->ensure_path($self->{mergeinfopath}, \%deletions);
+		$self->change_dir_prop($pbat,
+				       "svn:mergeinfo",
 			               $self->{mergeinfo});
 	}
 	$self->rmdirs if $_rmdir;
diff --git a/t/t9161-git-svn-mergeinfo-push.sh b/t/t9161-git-svn-mergeinfo-push.sh
index f348392..8a87ad7 100755
--- a/t/t9161-git-svn-mergeinfo-push.sh
+++ b/t/t9161-git-svn-mergeinfo-push.sh
@@ -121,6 +121,43 @@ test_expect_success 'check cherry-pick mergeinfo' '
 /branches/svnb5:6,11"
 	'
 
+test_expect_success 'make further commits to branch' '
+	git checkout svnb1 &&
+	mkdir sub_directory &&
+	touch sub_directory/newb1file &&
+	git add sub_directory/newb1file &&
+	git commit -m "sub directory b1 commit" &&
+	git svn dcommit &&
+	git checkout svnb2 &&
+	mkdir sub_directory &&
+	touch sub_directory/newb2file &&
+	git add sub_directory/newb2file &&
+	git commit -m "sub directory b2 commit" &&
+	touch sub_directory/newb2file2 &&
+	git add sub_directory/newb2file2 &&
+	git commit -m "sub directory b2 commit 2" &&
+	git svn dcommit
+	'
+
+test_expect_success 'cherry-pick mergeinfo sub directory' '
+	git config svn.mergeinfopath sub_directory &&
+	git checkout svnb1 &&
+	git cherry-pick svnb2 &&
+	git cherry-pick svnb2^ &&
+	git svn dcommit &&
+	git config --unset svn.mergeinfopath
+	'
+
+test_expect_success 'check cherry-pick mergeinfo sub directory' '
+	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1)
+	test "$mergeinfo" = "/branches/svnb2:3,8,16-17,20-22
+/branches/svnb3:4,9
+/branches/svnb4:5-6,10-12
+/branches/svnb5:6,11" &&
+	mergeinfo=$(cd sub_directory && svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1/sub_directory) &&
+	test "$mergeinfo" = "/branches/svnb2/sub_directory:27-28"
+	'
+
 test_expect_success 'dcommit a merge at the top of a stack' '
 	git checkout svnb1 &&
 	touch anotherfile &&
-- 
1.8.5.rc3.5.g96ccada

[RFC 2/3] git-svn: Support cherry-pick merges

From: Andrew Wong <hidden>
Date: 2016-06-15 22:59:20

Detect a cherry-pick merge if there's only one parent and the git-svn-id
metadata exists. Then, get the parent's mergeinfo and merge this commit's
mergeinfo.
---
 git-svn.perl                      | 52 +++++++++++++++++++++++++++++++++++++--
 t/t9161-git-svn-mergeinfo-push.sh | 30 ++++++++++++++++++++++
 2 files changed, 80 insertions(+), 2 deletions(-)
diff --git a/git-svn.perl b/git-svn.perl
index 9ddeaf4..b04cac7 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -698,12 +698,14 @@ sub populate_merge_info {
 	my %parentshash;
 	read_commit_parents(\%parentshash, $d);
 	my @parents = @{$parentshash{$d}};
+
+        my $rooturl = $gs->repos_root;
+        my ($target_branch) = $gs->full_pushurl =~ /^\Q$rooturl\E(.*)/;
+
 	if ($#parents > 0) {
 		# Merge commit
 		my $all_parents_ok = 1;
 		my $aggregate_mergeinfo = '';
-		my $rooturl = $gs->repos_root;
-		my ($target_branch) = $gs->full_pushurl =~ /^\Q$rooturl\E(.*)/;
 
 		if (defined($rewritten_parent)) {
 			# Replace first parent with newly-rewritten version
@@ -785,6 +787,52 @@ sub populate_merge_info {
 		if ($all_parents_ok and $aggregate_mergeinfo) {
 			return $aggregate_mergeinfo;
 		}
+	} elsif ($#parents == 0) {
+		# cherry-pick merge
+		my ($cherry_branchurl, $cherry_svnrev, $cherry_paruuid) =
+			cmt_metadata($d);
+
+		if(defined $cherry_branchurl && defined $cherry_svnrev && defined $cherry_paruuid)
+		{
+			if (defined($rewritten_parent)) {
+				# Replace first parent with newly-rewritten version
+				shift @parents;
+				unshift @parents, $rewritten_parent;
+			}
+
+			my $aggregate_mergeinfo = '';
+
+			# parent mergeinfo
+			my ($branchurl, $svnrev, $paruuid) =
+				cmt_metadata($parents[0]);
+
+			my $ra = Git::SVN::Ra->new($branchurl);
+			my (undef, undef, $props) =
+				$ra->get_dir(canonicalize_path("."), $svnrev);
+			my $parent_mergeinfo = $props->{'svn:mergeinfo'};
+			unless (defined $parent_mergeinfo) {
+				$parent_mergeinfo = '';
+			}
+
+			$aggregate_mergeinfo = merge_merge_info($aggregate_mergeinfo,
+					$parent_mergeinfo,
+					$target_branch);
+
+			# cherry-pick mergeinfo
+			unless ($cherry_branchurl =~ /^\Q$rooturl\E(.*)/) {
+				fatal "commit $d git-svn metadata changed mid-run!";
+			}
+			my $cherry_branchpath = $1;
+
+			my $cherry_pick_mergeinfo = canonicalize_path("$cherry_branchpath")
+				. ":$cherry_svnrev";
+
+			$aggregate_mergeinfo = merge_merge_info($aggregate_mergeinfo,
+					$cherry_pick_mergeinfo,
+					$target_branch);
+
+			return $aggregate_mergeinfo;
+		}
 	}
 
 	return undef;
diff --git a/t/t9161-git-svn-mergeinfo-push.sh b/t/t9161-git-svn-mergeinfo-push.sh
index 1eab701..f348392 100755
--- a/t/t9161-git-svn-mergeinfo-push.sh
+++ b/t/t9161-git-svn-mergeinfo-push.sh
@@ -91,6 +91,36 @@ test_expect_success 'check reintegration mergeinfo' '
 /branches/svnb5:6,11"
 	'
 
+test_expect_success 'make further commits to branch' '
+	git checkout svnb2 &&
+	touch newb2file-3 &&
+	git add newb2file-3 &&
+	git commit -m "later b2 commit 3" &&
+	touch newb2file-4 &&
+	git add newb2file-4 &&
+	git commit -m "later b2 commit 4" &&
+	touch newb2file-5 &&
+	git add newb2file-5 &&
+	git commit -m "later b2 commit 5" &&
+	git svn dcommit
+	'
+
+test_expect_success 'cherry-pick merge' '
+	git checkout svnb1 &&
+	git cherry-pick svnb2 &&
+	git cherry-pick svnb2^ &&
+	git cherry-pick svnb2^^ &&
+	git svn dcommit
+	'
+
+test_expect_success 'check cherry-pick mergeinfo' '
+	mergeinfo=$(svn_cmd propget svn:mergeinfo "$svnrepo"/branches/svnb1)
+	test "$mergeinfo" = "/branches/svnb2:3,8,16-17,20-22
+/branches/svnb3:4,9
+/branches/svnb4:5-6,10-12
+/branches/svnb5:6,11"
+	'
+
 test_expect_success 'dcommit a merge at the top of a stack' '
 	git checkout svnb1 &&
 	touch anotherfile &&
-- 
1.8.5.rc3.5.g96ccada
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help