{"thread":{"id":"35421","subject":"[RFC 1/3] git-svn: Generate mergeinfo for every commit","startedAt":"2013-11-28T15:52:14Z","lastAt":"2013-11-28T15:52:17Z","messageCount":4,"participants":["Andrew Wong"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"231246","messageId":"1385653937-29595-1-git-send-email-andrew.kw.w@gmail.com","threadId":"35421","inReplyTo":null,"subject":"[RFC 0/3] git-svn: Add support for cherry-pick merges","fromName":"Andrew Wong","fromEmail":"andrew.kw.w@gmail.com","sentAt":"2013-11-28T15:52:14Z","receivedAt":"2013-11-28T15:52:14Z","isPatch":false,"sender":{"key":"andrew.kw.w@gmail.com","avatar":"https://avatars.githubusercontent.com/u/489311?v=4"},"body":"This is a work-in-progress for adding support for cherry-pick merges.\n\nWhen using git-svn, cherry-picked commits from another git-svn branch are now\nbeing treated as simple commits. But in SVN, if the user uses \"svn merge -c\" to\ncherry-pick commits from another SVN branch, SVN records that information in\nsvn:mergeinfo. These patches will enable git-svn to do the same.\n\nAndrew Wong (3):\n  git-svn: Generate mergeinfo for every commit\n  git-svn: Support cherry-pick merges\n  git-svn: Add config to control the path of mergeinfo\n\n git-svn.perl                      | 79 ++++++++++++++++++++++++++++++++++-----\n perl/Git/SVN/Editor.pm            |  5 ++-\n t/t9161-git-svn-mergeinfo-push.sh | 67 +++++++++++++++++++++++++++++++++\n 3 files changed, 141 insertions(+), 10 deletions(-)\n\n-- \n1.8.5.rc3.5.g96ccada\n"},{"id":"231245","messageId":"1385653937-29595-2-git-send-email-andrew.kw.w@gmail.com","threadId":"35421","inReplyTo":"1385653937-29595-1-git-send-email-andrew.kw.w@gmail.com","subject":"[RFC 1/3] git-svn: Generate mergeinfo for every commit","fromName":"Andrew Wong","fromEmail":"andrew.kw.w@gmail.com","sentAt":"2013-11-28T15:52:15Z","receivedAt":"2013-11-28T15:52:15Z","isPatch":false,"sender":{"key":"andrew.kw.w@gmail.com","avatar":"https://avatars.githubusercontent.com/u/489311?v=4"},"body":"The previous behavior would only generate mergeinfo once using the first\ncommit, and use that mergeinfo for all remaining commits. The new behavior will\ngenerate it once for every commit.\n---\n git-svn.perl | 10 +++++++---\n 1 file changed, 7 insertions(+), 3 deletions(-)\n\ndiff --git a/git-svn.perl b/git-svn.perl\nindex 7349ffe..9ddeaf4 100755\n--- a/git-svn.perl\n+++ b/git-svn.perl\n@@ -974,8 +974,12 @@ sub cmd_dcommit {\n \t\t} else {\n \t\t\tmy $cmt_rev;\n \n-\t\t\tunless (defined($_merge_info) || ! $push_merge_info) {\n-\t\t\t\t$_merge_info = populate_merge_info($d, $gs,\n+\t\t\tmy $rev_merge_info;\n+\t\t\tif (defined($_merge_info)) {\n+\t\t\t\t$rev_merge_info = $_merge_info;\n+\t\t\t}\n+\t\t\tunless (defined($rev_merge_info) || ! $push_merge_info) {\n+\t\t\t\t$rev_merge_info = populate_merge_info($d, $gs,\n \t\t\t\t                             $uuid,\n \t\t\t\t                             $linear_refs,\n \t\t\t\t                             $rewritten_parent);\n@@ -993,7 +997,7 @@ sub cmd_dcommit {\n \t\t\t                       print \"Committed r$_[0]\\n\";\n \t\t\t                       $cmt_rev = $_[0];\n \t\t\t                },\n-\t\t\t\t\tmergeinfo => $_merge_info,\n+\t\t\t\t\tmergeinfo => $rev_merge_info,\n \t\t\t                svn_path => '');\n \n \t\t\tmy $err_handler = $SVN::Error::handler;\n-- \n1.8.5.rc3.5.g96ccada\n"},{"id":"231248","messageId":"1385653937-29595-3-git-send-email-andrew.kw.w@gmail.com","threadId":"35421","inReplyTo":"1385653937-29595-1-git-send-email-andrew.kw.w@gmail.com","subject":"[RFC 2/3] git-svn: Support cherry-pick merges","fromName":"Andrew Wong","fromEmail":"andrew.kw.w@gmail.com","sentAt":"2013-11-28T15:52:16Z","receivedAt":"2013-11-28T15:52:16Z","isPatch":false,"sender":{"key":"andrew.kw.w@gmail.com","avatar":"https://avatars.githubusercontent.com/u/489311?v=4"},"body":"Detect a cherry-pick merge if there's only one parent and the git-svn-id\nmetadata exists. Then, get the parent's mergeinfo and merge this commit's\nmergeinfo.\n---\n git-svn.perl                      | 52 +++++++++++++++++++++++++++++++++++++--\n t/t9161-git-svn-mergeinfo-push.sh | 30 ++++++++++++++++++++++\n 2 files changed, 80 insertions(+), 2 deletions(-)\n\ndiff --git a/git-svn.perl b/git-svn.perl\nindex 9ddeaf4..b04cac7 100755\n--- a/git-svn.perl\n+++ b/git-svn.perl\n@@ -698,12 +698,14 @@ sub populate_merge_info {\n \tmy %parentshash;\n \tread_commit_parents(\\%parentshash, $d);\n \tmy @parents = @{$parentshash{$d}};\n+\n+        my $rooturl = $gs->repos_root;\n+        my ($target_branch) = $gs->full_pushurl =~ /^\\Q$rooturl\\E(.*)/;\n+\n \tif ($#parents > 0) {\n \t\t# Merge commit\n \t\tmy $all_parents_ok = 1;\n \t\tmy $aggregate_mergeinfo = '';\n-\t\tmy $rooturl = $gs->repos_root;\n-\t\tmy ($target_branch) = $gs->full_pushurl =~ /^\\Q$rooturl\\E(.*)/;\n \n \t\tif (defined($rewritten_parent)) {\n \t\t\t# Replace first parent with newly-rewritten version\n@@ -785,6 +787,52 @@ sub populate_merge_info {\n \t\tif ($all_parents_ok and $aggregate_mergeinfo) {\n \t\t\treturn $aggregate_mergeinfo;\n \t\t}\n+\t} elsif ($#parents == 0) {\n+\t\t# cherry-pick merge\n+\t\tmy ($cherry_branchurl, $cherry_svnrev, $cherry_paruuid) =\n+\t\t\tcmt_metadata($d);\n+\n+\t\tif(defined $cherry_branchurl && defined $cherry_svnrev && defined $cherry_paruuid)\n+\t\t{\n+\t\t\tif (defined($rewritten_parent)) {\n+\t\t\t\t# Replace first parent with newly-rewritten version\n+\t\t\t\tshift @parents;\n+\t\t\t\tunshift @parents, $rewritten_parent;\n+\t\t\t}\n+\n+\t\t\tmy $aggregate_mergeinfo = '';\n+\n+\t\t\t# parent mergeinfo\n+\t\t\tmy ($branchurl, $svnrev, $paruuid) =\n+\t\t\t\tcmt_metadata($parents[0]);\n+\n+\t\t\tmy $ra = Git::SVN::Ra->new($branchurl);\n+\t\t\tmy (undef, undef, $props) =\n+\t\t\t\t$ra->get_dir(canonicalize_path(\".\"), $svnrev);\n+\t\t\tmy $parent_mergeinfo = $props->{'svn:mergeinfo'};\n+\t\t\tunless (defined $parent_mergeinfo) {\n+\t\t\t\t$parent_mergeinfo = '';\n+\t\t\t}\n+\n+\t\t\t$aggregate_mergeinfo = merge_merge_info($aggregate_mergeinfo,\n+\t\t\t\t\t$parent_mergeinfo,\n+\t\t\t\t\t$target_branch);\n+\n+\t\t\t# cherry-pick mergeinfo\n+\t\t\tunless ($cherry_branchurl =~ /^\\Q$rooturl\\E(.*)/) {\n+\t\t\t\tfatal \"commit $d git-svn metadata changed mid-run!\";\n+\t\t\t}\n+\t\t\tmy $cherry_branchpath = $1;\n+\n+\t\t\tmy $cherry_pick_mergeinfo = canonicalize_path(\"$cherry_branchpath\")\n+\t\t\t\t. \":$cherry_svnrev\";\n+\n+\t\t\t$aggregate_mergeinfo = merge_merge_info($aggregate_mergeinfo,\n+\t\t\t\t\t$cherry_pick_mergeinfo,\n+\t\t\t\t\t$target_branch);\n+\n+\t\t\treturn $aggregate_mergeinfo;\n+\t\t}\n \t}\n \n \treturn undef;\ndiff --git a/t/t9161-git-svn-mergeinfo-push.sh b/t/t9161-git-svn-mergeinfo-push.sh\nindex 1eab701..f348392 100755\n--- a/t/t9161-git-svn-mergeinfo-push.sh\n+++ b/t/t9161-git-svn-mergeinfo-push.sh\n@@ -91,6 +91,36 @@ test_expect_success 'check reintegration mergeinfo' '\n /branches/svnb5:6,11\"\n \t'\n \n+test_expect_success 'make further commits to branch' '\n+\tgit checkout svnb2 &&\n+\ttouch newb2file-3 &&\n+\tgit add newb2file-3 &&\n+\tgit commit -m \"later b2 commit 3\" &&\n+\ttouch newb2file-4 &&\n+\tgit add newb2file-4 &&\n+\tgit commit -m \"later b2 commit 4\" &&\n+\ttouch newb2file-5 &&\n+\tgit add newb2file-5 &&\n+\tgit commit -m \"later b2 commit 5\" &&\n+\tgit svn dcommit\n+\t'\n+\n+test_expect_success 'cherry-pick merge' '\n+\tgit checkout svnb1 &&\n+\tgit cherry-pick svnb2 &&\n+\tgit cherry-pick svnb2^ &&\n+\tgit cherry-pick svnb2^^ &&\n+\tgit svn dcommit\n+\t'\n+\n+test_expect_success 'check cherry-pick mergeinfo' '\n+\tmergeinfo=$(svn_cmd propget svn:mergeinfo \"$svnrepo\"/branches/svnb1)\n+\ttest \"$mergeinfo\" = \"/branches/svnb2:3,8,16-17,20-22\n+/branches/svnb3:4,9\n+/branches/svnb4:5-6,10-12\n+/branches/svnb5:6,11\"\n+\t'\n+\n test_expect_success 'dcommit a merge at the top of a stack' '\n \tgit checkout svnb1 &&\n \ttouch anotherfile &&\n-- \n1.8.5.rc3.5.g96ccada\n"},{"id":"231247","messageId":"1385653937-29595-4-git-send-email-andrew.kw.w@gmail.com","threadId":"35421","inReplyTo":"1385653937-29595-1-git-send-email-andrew.kw.w@gmail.com","subject":"[RFC 3/3] git-svn: Add config to control the path of mergeinfo","fromName":"Andrew Wong","fromEmail":"andrew.kw.w@gmail.com","sentAt":"2013-11-28T15:52:17Z","receivedAt":"2013-11-28T15:52:17Z","isPatch":false,"sender":{"key":"andrew.kw.w@gmail.com","avatar":"https://avatars.githubusercontent.com/u/489311?v=4"},"body":"Instead of always storing mergeinfo at the root, give an option to store the\nmerge info in a subdirectory. The subdirectory must exist before we try to set\nits property.\n---\n git-svn.perl                      | 21 +++++++++++++++------\n perl/Git/SVN/Editor.pm            |  5 ++++-\n t/t9161-git-svn-mergeinfo-push.sh | 37 +++++++++++++++++++++++++++++++++++++\n 3 files changed, 56 insertions(+), 7 deletions(-)\n\ndiff --git a/git-svn.perl b/git-svn.perl\nindex b04cac7..bfae579 100755\n--- a/git-svn.perl\n+++ b/git-svn.perl\n@@ -693,7 +693,7 @@ sub merge_merge_info {\n }\n \n sub populate_merge_info {\n-\tmy ($d, $gs, $uuid, $linear_refs, $rewritten_parent) = @_;\n+\tmy ($d, $gs, $uuid, $linear_refs, $rewritten_parent, $merge_info_path) = @_;\n \n \tmy %parentshash;\n \tread_commit_parents(\\%parentshash, $d);\n@@ -729,7 +729,7 @@ sub populate_merge_info {\n \n \t\t\tmy $ra = Git::SVN::Ra->new($branchurl);\n \t\t\tmy (undef, undef, $props) =\n-\t\t\t\t$ra->get_dir(canonicalize_path(\".\"), $svnrev);\n+\t\t\t\t$ra->get_dir(canonicalize_path($merge_info_path), $svnrev);\n \t\t\tmy $par_mergeinfo = $props->{'svn:mergeinfo'};\n \t\t\tunless (defined $par_mergeinfo) {\n \t\t\t\t$par_mergeinfo = '';\n@@ -778,7 +778,8 @@ sub populate_merge_info {\n \t\t\t# We now have a list of all SVN revnos which are\n \t\t\t# merged by this particular parent. Integrate them.\n \t\t\tnext if $#revsin == -1;\n-\t\t\tmy $newmergeinfo = \"$branchpath:\" . join(',', @revsin);\n+\t\t\tmy $newmergeinfo = canonicalize_path(\"$branchpath/$merge_info_path\")\n+\t\t\t\t. \":\" . join(',', @revsin);\n \t\t\t$aggregate_mergeinfo =\n \t\t\t\tmerge_merge_info($aggregate_mergeinfo,\n \t\t\t\t\t\t\t\t$newmergeinfo,\n@@ -808,7 +809,7 @@ sub populate_merge_info {\n \n \t\t\tmy $ra = Git::SVN::Ra->new($branchurl);\n \t\t\tmy (undef, undef, $props) =\n-\t\t\t\t$ra->get_dir(canonicalize_path(\".\"), $svnrev);\n+\t\t\t\t$ra->get_dir(canonicalize_path($merge_info_path), $svnrev);\n \t\t\tmy $parent_mergeinfo = $props->{'svn:mergeinfo'};\n \t\t\tunless (defined $parent_mergeinfo) {\n \t\t\t\t$parent_mergeinfo = '';\n@@ -824,7 +825,7 @@ sub populate_merge_info {\n \t\t\t}\n \t\t\tmy $cherry_branchpath = $1;\n \n-\t\t\tmy $cherry_pick_mergeinfo = canonicalize_path(\"$cherry_branchpath\")\n+\t\t\tmy $cherry_pick_mergeinfo = canonicalize_path(\"$cherry_branchpath/$merge_info_path\")\n \t\t\t\t. \":$cherry_svnrev\";\n \n \t\t\t$aggregate_mergeinfo = merge_merge_info($aggregate_mergeinfo,\n@@ -1008,6 +1009,12 @@ sub cmd_dcommit {\n \tif (defined($_merge_info)) {\n \t\t$_merge_info =~ tr{ }{\\n};\n \t}\n+\tmy $merge_info_path = eval {\n+\t\tcommand_oneline(qw/config --get svn.mergeinfopath/)\n+\t\t};\n+\tif (not defined($merge_info_path)) {\n+\t\t$merge_info_path = \"\";\n+\t}\n \twhile (1) {\n \t\tmy $d = shift @$linear_refs or last;\n \t\tunless (defined $last_rev) {\n@@ -1030,7 +1037,8 @@ sub cmd_dcommit {\n \t\t\t\t$rev_merge_info = populate_merge_info($d, $gs,\n \t\t\t\t                             $uuid,\n \t\t\t\t                             $linear_refs,\n-\t\t\t\t                             $rewritten_parent);\n+\t\t\t\t                             $rewritten_parent,\n+\t\t\t\t\t\t\t     $merge_info_path);\n \t\t\t}\n \n \t\t\tmy %ed_opts = ( r => $last_rev,\n@@ -1046,6 +1054,7 @@ sub cmd_dcommit {\n \t\t\t                       $cmt_rev = $_[0];\n \t\t\t                },\n \t\t\t\t\tmergeinfo => $rev_merge_info,\n+\t\t\t\t\tmergeinfopath => $merge_info_path,\n \t\t\t                svn_path => '');\n \n \t\t\tmy $err_handler = $SVN::Error::handler;\ndiff --git a/perl/Git/SVN/Editor.pm b/perl/Git/SVN/Editor.pm\nindex b3bcd47..dcbb8a0 100644\n--- a/perl/Git/SVN/Editor.pm\n+++ b/perl/Git/SVN/Editor.pm\n@@ -42,6 +42,7 @@ sub new {\n \t                       \"$self->{svn_path}/\" : '';\n \t$self->{config} = $opts->{config};\n \t$self->{mergeinfo} = $opts->{mergeinfo};\n+\t$self->{mergeinfopath} = $opts->{mergeinfopath};\n \treturn $self;\n }\n \n@@ -484,7 +485,9 @@ sub apply_diff {\n \t}\n \n \tif (defined($self->{mergeinfo})) {\n-\t\t$self->change_dir_prop($self->{bat}{''}, \"svn:mergeinfo\",\n+\t\tmy $pbat = $self->ensure_path($self->{mergeinfopath}, \\%deletions);\n+\t\t$self->change_dir_prop($pbat,\n+\t\t\t\t       \"svn:mergeinfo\",\n \t\t\t               $self->{mergeinfo});\n \t}\n \t$self->rmdirs if $_rmdir;\ndiff --git a/t/t9161-git-svn-mergeinfo-push.sh b/t/t9161-git-svn-mergeinfo-push.sh\nindex f348392..8a87ad7 100755\n--- a/t/t9161-git-svn-mergeinfo-push.sh\n+++ b/t/t9161-git-svn-mergeinfo-push.sh\n@@ -121,6 +121,43 @@ test_expect_success 'check cherry-pick mergeinfo' '\n /branches/svnb5:6,11\"\n \t'\n \n+test_expect_success 'make further commits to branch' '\n+\tgit checkout svnb1 &&\n+\tmkdir sub_directory &&\n+\ttouch sub_directory/newb1file &&\n+\tgit add sub_directory/newb1file &&\n+\tgit commit -m \"sub directory b1 commit\" &&\n+\tgit svn dcommit &&\n+\tgit checkout svnb2 &&\n+\tmkdir sub_directory &&\n+\ttouch sub_directory/newb2file &&\n+\tgit add sub_directory/newb2file &&\n+\tgit commit -m \"sub directory b2 commit\" &&\n+\ttouch sub_directory/newb2file2 &&\n+\tgit add sub_directory/newb2file2 &&\n+\tgit commit -m \"sub directory b2 commit 2\" &&\n+\tgit svn dcommit\n+\t'\n+\n+test_expect_success 'cherry-pick mergeinfo sub directory' '\n+\tgit config svn.mergeinfopath sub_directory &&\n+\tgit checkout svnb1 &&\n+\tgit cherry-pick svnb2 &&\n+\tgit cherry-pick svnb2^ &&\n+\tgit svn dcommit &&\n+\tgit config --unset svn.mergeinfopath\n+\t'\n+\n+test_expect_success 'check cherry-pick mergeinfo sub directory' '\n+\tmergeinfo=$(svn_cmd propget svn:mergeinfo \"$svnrepo\"/branches/svnb1)\n+\ttest \"$mergeinfo\" = \"/branches/svnb2:3,8,16-17,20-22\n+/branches/svnb3:4,9\n+/branches/svnb4:5-6,10-12\n+/branches/svnb5:6,11\" &&\n+\tmergeinfo=$(cd sub_directory && svn_cmd propget svn:mergeinfo \"$svnrepo\"/branches/svnb1/sub_directory) &&\n+\ttest \"$mergeinfo\" = \"/branches/svnb2/sub_directory:27-28\"\n+\t'\n+\n test_expect_success 'dcommit a merge at the top of a stack' '\n \tgit checkout svnb1 &&\n \ttouch anotherfile &&\n-- \n1.8.5.rc3.5.g96ccada\n"}]}