{"thread":{"id":"4551","subject":"[PATCH] rebase: Allow merge strategies to be used when rebasing","startedAt":"2006-06-18T03:02:15Z","lastAt":"2006-06-21T11:01:35Z","messageCount":11,"participants":["Eric Wong","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"21986","messageId":"1150599735483-git-send-email-normalperson@yhbt.net","threadId":"4551","inReplyTo":null,"subject":"[PATCH] rebase: Allow merge strategies to be used when rebasing","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-06-18T03:02:15Z","receivedAt":"2006-06-18T03:02:15Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"This solves the problem of rebasing local commits against an\nupstream that has renamed files.\n\nSigned-off-by: Eric Wong <normalperson@yhbt.net>\n---\n Documentation/git-rebase.txt |   20 +++++\n git-rebase.sh                |  176 ++++++++++++++++++++++++++++++++++++++++--\n 2 files changed, 186 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 08ee4aa..c339c45 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -7,7 +7,7 @@ git-rebase - Rebase local commits to a n\n \n SYNOPSIS\n --------\n-'git-rebase' [--onto <newbase>] <upstream> [<branch>]\n+'git-rebase' [--merge] [--onto <newbase>] <upstream> [<branch>]\n \n 'git-rebase' --continue | --skip | --abort\n \n@@ -106,6 +106,24 @@ OPTIONS\n --abort::\n \tRestore the original branch and abort the rebase operation.\n \n+--skip::\n+\tRestart the rebasing process by skipping the current patch.\n+\tThis does not work with the --merge option.\n+\n+--merge::\n+\tUse merging strategies to rebase.  When the recursive (default) merge\n+\tstrategy is used, this allows rebase to be aware of renames on the\n+\tupstream side.\n+\n+-s <strategy>, \\--strategy=<strategy>::\n+\tUse the given merge strategy; can be supplied more than\n+\tonce to specify them in the order they should be tried.\n+\tIf there is no `-s` option, a built-in list of strategies\n+\tis used instead (`git-merge-recursive` when merging a single\n+\thead, `git-merge-octopus` otherwise).  This implies --merge.\n+\n+include::merge-strategies.txt[]\n+\n NOTES\n -----\n When you rebase a branch, you are changing its history in a way that\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex e6b57b8..68bddfb 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -34,7 +34,81 @@ When you have resolved this problem run \n If you would prefer to skip this patch, instead run \\\"git rebase --skip\\\".\n To restore the original branch and stop rebasing run \\\"git rebase --abort\\\".\n \"\n+\n+MRESOLVEMSG=\"\n+When you have resolved this problem run \\\"git rebase --continue\\\".\n+To restore the original branch and stop rebasing run \\\"git rebase --abort\\\".\n+\"\n unset newbase\n+strategy_args=\n+do_merge=\n+dotest=$GIT_DIR/.dotest-merge\n+prec=4\n+\n+continue_merge () {\n+\ttest -n \"$prev_head\" || die \"prev_head must be defined\"\n+\ttest -d \"$dotest\" || die \"$dotest directory does not exist\"\n+\n+\tunmerged=$(git-ls-files -u)\n+\tif test -n \"$unmerged\"\n+\tthen\n+\t\techo \"You still have unmerged paths in your index\"\n+\t\techo \"did you forget update-index?\"\n+\t\tdie \"$MRESOLVEMSG\"\n+\tfi\n+\n+\tmh=\"$GIT_DIR/MERGE_HEAD\"\n+\tmm=\"$GIT_DIR/MERGE_MSG\"\n+\tif test -f \"$mh\" && test -f \"$mm\"\n+\tthen\n+\t\tgit-commit -F \"$mm\" || die \"commit failed: $MRESOLVEMSG\"\n+\telse\n+\t\techo \"Previous merge succeeded automatically\"\n+\tfi\n+\n+\tprev_head=`git-rev-parse HEAD^0`\n+\n+\t# save the resulting commit so we can read-tree on it later\n+\techo \"$prev_head\" > \"$dotest/`printf %0${prec}d $msgnum`.result\"\n+\techo \"$prev_head\" > \"$dotest/prev_head\"\n+\n+\t# onto the next patch:\n+\tmsgnum=$(($msgnum + 1))\n+\tprintf \"%0${prec}d\" \"$msgnum\" > \"$dotest/msgnum\"\n+}\n+\n+call_merge () {\n+\tcmt=\"$(cat $dotest/`printf %0${prec}d $1`)\"\n+\techo \"$cmt\" > \"$dotest/current\"\n+\tgit-merge $strategy_args \"rebase-merge: $cmt\" HEAD \"$cmt\" \\\n+\t\t\t|| die \"$MRESOLVEMSG\"\n+}\n+\n+finish_rb_merge () {\n+\tset -e\n+\n+\tmsgnum=1\n+\techo \"Finalizing rebased commits...\"\n+\tgit-reset --hard \"`cat $dotest/upstream`\"\n+\tend=\"`cat $dotest/end`\"\n+\twhile test \"$msgnum\" -le \"$end\"\n+\tdo\n+\t\tmsgnum=`printf \"%0${prec}d\" \"$msgnum\"`\n+\t\tprintf \"%0${prec}d\" \"$msgnum\" > \"$dotest/msgnum\"\n+\n+\t\tgit-read-tree `cat \"$dotest/$msgnum.result\"`\n+\t\tgit-checkout-index -q -f -u -a\n+\t\tgit-commit -C \"`cat $dotest/$msgnum`\"\n+\n+\t\techo \"Committed $msgnum\"\n+\t\techo '    '`git-rev-list --pretty=oneline -1 HEAD | \\\n+\t\t\t\t\tsed 's/^[a-f0-9]\\+ //'`\n+\t\tmsgnum=$(($msgnum + 1))\n+\tdone\n+\trm -r \"$dotest\"\n+\techo \"All done.\"\n+}\n+\n while case \"$#\" in 0) break ;; esac\n do\n \tcase \"$1\" in\n@@ -46,17 +120,42 @@ do\n \t\t\texit 1\n \t\t\t;;\n \t\tesac\n+\t\tif test -d \"$dotest\"\n+\t\tthen\n+\t\t\tprev_head=\"`cat $dotest/prev_head`\"\n+\t\t\tend=\"`cat $dotest/end`\"\n+\t\t\tmsgnum=\"`cat $dotest/msgnum`\"\n+\t\t\tcontinue_merge\n+\t\t\twhile test \"$msgnum\" -le \"$end\"\n+\t\t\tdo\n+\t\t\t\tcall_merge \"$msgnum\"\n+\t\t\t\tcontinue_merge\n+\t\t\tdone\n+\t\t\tfinish_rb_merge\n+\t\t\texit\n+\t\tfi\n \t\tgit am --resolved --3way --resolvemsg=\"$RESOLVEMSG\"\n \t\texit\n \t\t;;\n \t--skip)\n+\t\tif test -d \"$dotest\"\n+\t\tthen\n+\t\t\tdie \"--skip is not supported when using --merge\"\n+\t\tfi\n \t\tgit am -3 --skip --resolvemsg=\"$RESOLVEMSG\"\n \t\texit\n \t\t;;\n \t--abort)\n-\t\t[ -d .dotest ] || die \"No rebase in progress?\"\n+\t\tif test -d \"$dotest\"\n+\t\tthen\n+\t\t\trm -r \"$dotest\"\n+\t\telif test -d .dotest\n+\t\tthen\n+\t\t\trm -r .dotest\n+\t\telse\n+\t\t\tdie \"No rebase in progress?\"\n+\t\tfi\n \t\tgit reset --hard ORIG_HEAD\n-\t\trm -r .dotest\n \t\texit\n \t\t;;\n \t--onto)\n@@ -64,6 +163,24 @@ do\n \t\tnewbase=\"$2\"\n \t\tshift\n \t\t;;\n+\t-M|-m|--m|--me|--mer|--merg|--merge)\n+\t\tdo_merge=t\n+\t\t;;\n+\t-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\\\n+\t\t--strateg=*|--strategy=*|\\\n+\t-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)\n+\t\tcase \"$#,$1\" in\n+\t\t*,*=*)\n+\t\t\tstrategy=`expr \"$1\" : '-[^=]*=\\(.*\\)'` ;;\n+\t\t1,*)\n+\t\t\tusage ;;\n+\t\t*)\n+\t\t\tstrategy=\"$2\"\n+\t\t\tshift ;;\n+\t\tesac\n+\t\tdo_merge=t\n+\t\tstrategy_args=\"${strategy_args}-s $strategy \"\n+\t\t;;\n \t-*)\n \t\tusage\n \t\t;;\n@@ -75,16 +192,25 @@ do\n done\n \n # Make sure we do not have .dotest\n-if mkdir .dotest\n+if test -z \"$do_merge\"\n then\n-\trmdir .dotest\n-else\n-\techo >&2 '\n+\tif mkdir .dotest\n+\tthen\n+\t\trmdir .dotest\n+\telse\n+\t\techo >&2 '\n It seems that I cannot create a .dotest directory, and I wonder if you\n are in the middle of patch application or another rebase.  If that is not\n the case, please rm -fr .dotest and run me again.  I am stopping in case\n you still have something valuable there.'\n-\texit 1\n+\t\texit 1\n+\tfi\n+else\n+\tif test -d \"$dotest\"\n+\tthen\n+\t\tdie \"previous dotest directory $dotest still exists.\" \\\n+\t\t\t'try git-rebase < --continue | --abort >'\n+\tfi\n fi\n \n # The tree must be really really clean.\n@@ -152,6 +278,38 @@ then\n \texit 0\n fi\n \n-git-format-patch -k --stdout --full-index \"$upstream\"..ORIG_HEAD |\n-git am --binary -3 -k --resolvemsg=\"$RESOLVEMSG\"\n+if test -z \"$do_merge\"\n+then\n+\tgit-format-patch -k --stdout --full-index \"$upstream\"..ORIG_HEAD |\n+\tgit am --binary -3 -k --resolvemsg=\"$RESOLVEMSG\"\n+\texit 0\n+fi\n+\n+# start doing a rebase with git-merge\n+# this is rename-aware if the recursive (default) strategy is used\n+\n+mkdir -p \"$dotest\"\n+echo \"$upstream\" > \"$dotest/upstream\"\n+prev_head=`git-rev-parse HEAD^0`\n+echo \"$prev_head\" > \"$dotest/prev_head\"\n+\n+msgnum=0\n+for cmt in `git-rev-list \"$upstream\"..ORIG_HEAD | tac`\n+do\n+\tmsgnum=$(($msgnum + 1))\n+\techo \"$cmt\" > \"$dotest/`printf \"%0${prec}d\" $msgnum`\"\n+done\n+\n+printf \"%0${prec}d\" 1 > \"$dotest/msgnum\"\n+printf \"%0${prec}d\" \"$msgnum\" > \"$dotest/end\"\n+\n+end=$msgnum\n+msgnum=1\n+\n+while test \"$msgnum\" -le \"$end\"\n+do\n+\tcall_merge \"$msgnum\"\n+\tcontinue_merge\n+done\n \n+finish_rb_merge\n-- \n1.4.0.ge24c\n"},{"id":"22002","messageId":"7vd5d63k7f.fsf@assigned-by-dhcp.cox.net","threadId":"4551","inReplyTo":"1150599735483-git-send-email-normalperson@yhbt.net","subject":"Re: [PATCH] rebase: Allow merge strategies to be used when rebasing","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-06-18T09:08:04Z","receivedAt":"2006-06-18T09:08:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Wong <normalperson@yhbt.net> writes:\n\n> This solves the problem of rebasing local commits against an\n> upstream that has renamed files.\n\nI think leveraging the merge strategy to perform rebase is\nsound, but the selection of merge base for this purpose is quite\ndifferent from the regular merge, and I think unfortunately this\npatch is probably wrong in letting git-merge choose the merge\nbase.\n\nBut let's mention other things as well.\n\n - You kept the original \"format-patch piped to am\" workflow\n   optionally working.\n\n - You check if merge or patch was used for failed rebase and\n   follow the appropriate codepath while resuming, which is\n   good.\n\n - The list of commits you generate with tac seem to include\n   merge commit -- you may want to give --no-merges to\n   rev-list.\n\n - I do not think we use \"tac\" elsewhere -- is it portable\n   enough?\n\n - Exiting with success unconditionally after \"git am\" feels\n   wrong.  I would do \"exit $?\" instead of \"exit 0\" there.\n\nSuppose you have this commit ancestry graph:\n\n----------------------------------------------------------------\nExample:       git-rebase --onto master A topic\n\n        A---B---C topic                       B'--C' topic\n       /                   -->               /\n  D---E---F---G master          D---E---F---G master\n----------------------------------------------------------------\n\nThis is slightly different from the one at the beginning of the\nscript.  The idea is A turned out to be not so cool, and we\nwould want to drop it.\n\n> +call_merge () {\n> +\tcmt=\"$(cat $dotest/`printf %0${prec}d $1`)\"\n> +\techo \"$cmt\" > \"$dotest/current\"\n> +\tgit-merge $strategy_args \"rebase-merge: $cmt\" HEAD \"$cmt\" \\\n> +\t\t\t|| die \"$MRESOLVEMSG\"\n> +}\n\ncall_merge is first called with B in cmt, and HEAD is pointing\nat G.  But the merge in this function makes a merge between B\nand G, taking the effect of E->A.\n\nI think the three-way merge you would want here is not between B\nand G using E as the pivot, but between B and G using A as the\npivot.  That's how cherry-pick and revert works.  I would\nleverage the interface that is one level lower for this -- the\nstrategy modules themselves.\n\n\tgit-merge-$strategy $cmt^ -- HEAD $cmt\n\nThe strategy modules take merge base(s), double-dash as the\nseparator, our head and the other head.  They do not make commit\nthemselves (instead they leave working tree and index in\ncommittable state) and signal the results with their exit\nstatus:\n\n\t0 -- success\n        1 -- conflicts\n        2 -- did not handle the merge at all\n"},{"id":"22012","messageId":"7vslm21y7p.fsf@assigned-by-dhcp.cox.net","threadId":"4551","inReplyTo":"1150599735483-git-send-email-normalperson@yhbt.net","subject":"[PATCH] Add renaming-rebase test.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-06-18T11:48:26Z","receivedAt":"2006-06-18T11:48:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This tests Eric's \"renaming rebase\" patch.  It tests only very\nbasic cases and most of the tests except the last one passes.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n t/t3402-rebase-merge.sh |   96 +++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 96 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh\nnew file mode 100755\nindex 0000000..8c7a519\n--- /dev/null\n+++ b/t/t3402-rebase-merge.sh\n@@ -0,0 +1,96 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2006 Junio C Hamano\n+#\n+\n+test_description='git rebase --merge test'\n+\n+. ./test-lib.sh\n+\n+T=\"A quick brown fox\n+jumps over the lazy dog.\"\n+for i in 1 2 3 4 5 6 7 8 9 10\n+do\n+\techo \"$i $T\"\n+done >original\n+\n+test_expect_success setup '\n+\tgit add original &&\n+\tgit commit -m\"initial\" &&\n+\tgit branch side &&\n+\techo \"11 $T\" >>original &&\n+\tgit commit -a -m\"master updates a bit.\" &&\n+\n+\techo \"12 $T\" >>original &&\n+\tgit commit -a -m\"master updates a bit more.\" &&\n+\n+\tgit checkout side &&\n+\t(echo \"0 $T\" ; cat original) >renamed &&\n+\tgit add renamed &&\n+\tgit update-index --force-remove original &&\n+\tgit commit -a -m\"side renames and edits.\" &&\n+\n+\ttr \"[a-z]\" \"[A-Z]\" <original >newfile &&\n+\tgit add newfile &&\n+\tgit commit -a -m\"side edits further.\" &&\n+\n+\ttr \"[a-m]\" \"[A-M]\" <original >newfile &&\n+\trm -f original &&\n+\tgit commit -a -m\"side edits once again.\" &&\n+\n+\tgit branch test-rebase side &&\n+\tgit branch test-rebase-pick side &&\n+\tgit branch test-reference-pick side &&\n+\tgit checkout -b test-merge side\n+'\n+\n+test_expect_success 'reference merge' '\n+\tgit merge -s recursive \"reference merge\" HEAD master\n+'\n+\n+test_expect_success rebase '\n+\tgit checkout test-rebase &&\n+\tgit rebase --merge master\n+'\n+\n+test_expect_success 'merge and rebase should match' '\n+\tgit diff-tree -r test-rebase test-merge >difference &&\n+\tif test -s difference\n+\tthen\n+\t\tcat difference\n+\t\t(exit 1)\n+\telse\n+\t\techo happy\n+\tfi\n+'\n+\n+test_expect_success 'rebase the other way' '\n+\tgit reset --hard master &&\n+\tgit rebase --merge side\n+'\n+\n+test_expect_success 'merge and rebase should match' '\n+\tgit diff-tree -r test-rebase test-merge >difference &&\n+\tif test -s difference\n+\tthen\n+\t\tcat difference\n+\t\t(exit 1)\n+\telse\n+\t\techo happy\n+\tfi\n+'\n+\n+test_expect_success 'picking rebase' '\n+\tgit reset --hard side &&\n+\tgit rebase --merge --onto master side^^ &&\n+\tmb=$(git merge-base master HEAD) &&\n+\tif test \"$mb\" = \"$(git rev-parse master)\"\n+\tthen\n+\t\techo happy\n+\telse\n+\t\tgit show-branch\n+\t\t(exit 1)\n+\tfi\n+'\n+\n+test_done\n-- \n1.4.0.g1910f\n"},{"id":"22077","messageId":"20060619213951.GA6987@hand.yhbt.net","threadId":"4551","inReplyTo":"7vd5d63k7f.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] rebase: Allow merge strategies to be used when rebasing","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-06-19T21:39:51Z","receivedAt":"2006-06-19T21:39:51Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Junio C Hamano <junkio@cox.net> wrote:\n> Eric Wong <normalperson@yhbt.net> writes:\n> \n> > This solves the problem of rebasing local commits against an\n> > upstream that has renamed files.\n> \n> I think leveraging the merge strategy to perform rebase is\n> sound, but the selection of merge base for this purpose is quite\n> different from the regular merge, and I think unfortunately this\n> patch is probably wrong in letting git-merge choose the merge\n> base.\n> \n> But let's mention other things as well.\n> \n>  - You kept the original \"format-patch piped to am\" workflow\n>    optionally working.\n\nI left it as the default, too.  I figured that it's best not\nto change the default (and most likely faster) behavior of\nsomething people rely on.\n\n>  - You check if merge or patch was used for failed rebase and\n>    follow the appropriate codepath while resuming, which is\n>    good.\n> \n>  - The list of commits you generate with tac seem to include\n>    merge commit -- you may want to give --no-merges to\n>    rev-list.\n\nGood point, I'll change it.\n\n>  - I do not think we use \"tac\" elsewhere -- is it portable\n>    enough?\n\nNope.  perl -e 'print reverse <>' is equivalent and we already use\nplenty of perl.\n\n>  - Exiting with success unconditionally after \"git am\" feels\n>    wrong.  I would do \"exit $?\" instead of \"exit 0\" there.\n\nOops, I'll change that, too.\n\n> Suppose you have this commit ancestry graph:\n> \n> ----------------------------------------------------------------\n> Example:       git-rebase --onto master A topic\n> \n>         A---B---C topic                       B'--C' topic\n>        /                   -->               /\n>   D---E---F---G master          D---E---F---G master\n> ----------------------------------------------------------------\n> \n> This is slightly different from the one at the beginning of the\n> script.  The idea is A turned out to be not so cool, and we\n> would want to drop it.\n> \n> > +call_merge () {\n> > +\tcmt=\"$(cat $dotest/`printf %0${prec}d $1`)\"\n> > +\techo \"$cmt\" > \"$dotest/current\"\n> > +\tgit-merge $strategy_args \"rebase-merge: $cmt\" HEAD \"$cmt\" \\\n> > +\t\t\t|| die \"$MRESOLVEMSG\"\n> > +}\n> \n> call_merge is first called with B in cmt, and HEAD is pointing\n> at G.  But the merge in this function makes a merge between B\n> and G, taking the effect of E->A.\n> \n> I think the three-way merge you would want here is not between B\n> and G using E as the pivot, but between B and G using A as the\n> pivot.  That's how cherry-pick and revert works.  I would\n> leverage the interface that is one level lower for this -- the\n> strategy modules themselves.\n> \n> \tgit-merge-$strategy $cmt^ -- HEAD $cmt\n\nChanging the 'git-merge $strategy_args \"rebase-merge: $cmt\" HEAD \"$cmt\"'\nline in call_merge() to this seems to have broken more tests.\n\nI'm not an expert at merging strategies by any measure, I've just\ntrusted merge-recursive to Do The Right Thing(TM) more often than not,\nand use rerere to avoid repeating work.\n\nI'm not entirely sure I want (or fully understand how) to support\ncherry-picking/revert with this, as I've already dropped --skip when\n--merge was in use.\n\nI'll think about this some more when I'm less distracted.\n\n> The strategy modules take merge base(s), double-dash as the\n> separator, our head and the other head.  They do not make commit\n> themselves (instead they leave working tree and index in\n> committable state) and signal the results with their exit\n> status:\n> \n> \t0 -- success\n>         1 -- conflicts\n>         2 -- did not handle the merge at all\n\nCool, that's good.\n\n-- \nEric Wong\n"},{"id":"22078","messageId":"7vbqso95f3.fsf@assigned-by-dhcp.cox.net","threadId":"4551","inReplyTo":"20060619213951.GA6987@hand.yhbt.net","subject":"Re: [PATCH] rebase: Allow merge strategies to be used when rebasing","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-06-19T21:55:28Z","receivedAt":"2006-06-19T21:55:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Wong <normalperson@yhbt.net> writes:\n\n> Junio C Hamano <junkio@cox.net> wrote:\n>\n>>  - You kept the original \"format-patch piped to am\" workflow\n>>    optionally working.\n>\n> I left it as the default, too.  I figured that it's best not\n> to change the default (and most likely faster) behavior of\n> something people rely on.\n\nI should have said: \"You kept ... working, which is good\".\n\n>> I think the three-way merge you would want here is not between B\n>> and G using E as the pivot, but between B and G using A as the\n>> pivot.  That's how cherry-pick and revert works.  I would\n>> leverage the interface that is one level lower for this -- the\n>> strategy modules themselves.\n>> \n>> \tgit-merge-$strategy $cmt^ -- HEAD $cmt\n>\n> Changing the 'git-merge $strategy_args \"rebase-merge: $cmt\" HEAD \"$cmt\"'\n> line in call_merge() to this seems to have broken more tests.\n\nOh, that is to be expected if you changed git-merge -s recursive\nwith git-merge-recursive without other changes.  The former\nmakes a commit (which your original patch later used to create a\nseparate commit chain and discarded); the latter does not make a\ncommit but expects the caller to create a commit out of the\nresulting index file.\n\n> I'm not an expert at merging strategies by any measure, I've just\n> trusted merge-recursive to Do The Right Thing(TM) more often than not,\n> and use rerere to avoid repeating work.\n\nI was originally hoping that rebasing would just be a matter of\nlisting sequence of commits to be ported onto a new base and\nrunning \"git-cherry-pick\" on each of them in sequence.  Now\ncherry-pick does not use merge machinery (hence does not use\ngit-merge-recursive), but if we change that then updating rebase\nwould be pretty much straightforward.  It just needs a UI layer\nto guide the user through recovery process when the merge does\nnot resolve cleanly in the middle, no?\n"},{"id":"22188","messageId":"20060621100138.GA15748@localdomain","threadId":"4551","inReplyTo":"7vbqso95f3.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] rebase: Allow merge strategies to be used when rebasing","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-06-21T10:01:38Z","receivedAt":"2006-06-21T10:01:38Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Junio C Hamano <junkio@cox.net> wrote:\n> Eric Wong <normalperson@yhbt.net> writes:\n> \n> > Junio C Hamano <junkio@cox.net> wrote:\n> >\n> >>  - You kept the original \"format-patch piped to am\" workflow\n> >>    optionally working.\n> >\n> > I left it as the default, too.  I figured that it's best not\n> > to change the default (and most likely faster) behavior of\n> > something people rely on.\n> \n> I should have said: \"You kept ... working, which is good\".\n> \n> >> I think the three-way merge you would want here is not between B\n> >> and G using E as the pivot, but between B and G using A as the\n> >> pivot.  That's how cherry-pick and revert works.  I would\n> >> leverage the interface that is one level lower for this -- the\n> >> strategy modules themselves.\n> >> \n> >> \tgit-merge-$strategy $cmt^ -- HEAD $cmt\n> >\n> > Changing the 'git-merge $strategy_args \"rebase-merge: $cmt\" HEAD \"$cmt\"'\n> > line in call_merge() to this seems to have broken more tests.\n> \n> Oh, that is to be expected if you changed git-merge -s recursive\n> with git-merge-recursive without other changes.  The former\n> makes a commit (which your original patch later used to create a\n> separate commit chain and discarded); the latter does not make a\n> commit but expects the caller to create a commit out of the\n> resulting index file.\n\nOops, *smacks head*\n\n> > I'm not an expert at merging strategies by any measure, I've just\n> > trusted merge-recursive to Do The Right Thing(TM) more often than not,\n> > and use rerere to avoid repeating work.\n> \n> I was originally hoping that rebasing would just be a matter of\n> listing sequence of commits to be ported onto a new base and\n> running \"git-cherry-pick\" on each of them in sequence.  Now\n> cherry-pick does not use merge machinery (hence does not use\n> git-merge-recursive), but if we change that then updating rebase\n> would be pretty much straightforward.  It just needs a UI layer\n> to guide the user through recovery process when the merge does\n> not resolve cleanly in the middle, no?\n\nSounds workable right to me. But then again, a cherry-pick is also a\ncase of rebase on a single commit, so we could be using rebase (and its\nrecovery code) in cherry-pick, too, right?\n\n-- \nEric Wong\n"},{"id":"22189","messageId":"11508842824018-git-send-email-normalperson@yhbt.net","threadId":"4551","inReplyTo":"20060621100138.GA15748@localdomain","subject":"[PATCH (fixed)] rebase: Allow merge strategies to be used when rebasing","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-06-21T10:04:40Z","receivedAt":"2006-06-21T10:04:40Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"1 - is fixed for --onto usage.\n2 - in the series is the test from Junio, so I'm not resending it:\n\tSubject: [PATCH] Add renaming-rebase test.\n3 - keeps the NO_PYTHON people happy\n\n-- \nEric Wong\n"},{"id":"22190","messageId":"11508842842125-git-send-email-normalperson@yhbt.net","threadId":"4551","inReplyTo":"11508842824018-git-send-email-normalperson@yhbt.net","subject":"[PATCH 1/3] rebase: Allow merge strategies to be used when rebasing","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-06-21T10:04:41Z","receivedAt":"2006-06-21T10:04:41Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"This solves the problem of rebasing local commits against an\nupstream that has renamed files.\n\nSigned-off-by: Eric Wong <normalperson@yhbt.net>\n---\n Documentation/git-rebase.txt |   20 ++++\n git-rebase.sh                |  192 ++++++++++++++++++++++++++++++++++++++++--\n 2 files changed, 202 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 08ee4aa..c339c45 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -7,7 +7,7 @@ git-rebase - Rebase local commits to a n\n \n SYNOPSIS\n --------\n-'git-rebase' [--onto <newbase>] <upstream> [<branch>]\n+'git-rebase' [--merge] [--onto <newbase>] <upstream> [<branch>]\n \n 'git-rebase' --continue | --skip | --abort\n \n@@ -106,6 +106,24 @@ OPTIONS\n --abort::\n \tRestore the original branch and abort the rebase operation.\n \n+--skip::\n+\tRestart the rebasing process by skipping the current patch.\n+\tThis does not work with the --merge option.\n+\n+--merge::\n+\tUse merging strategies to rebase.  When the recursive (default) merge\n+\tstrategy is used, this allows rebase to be aware of renames on the\n+\tupstream side.\n+\n+-s <strategy>, \\--strategy=<strategy>::\n+\tUse the given merge strategy; can be supplied more than\n+\tonce to specify them in the order they should be tried.\n+\tIf there is no `-s` option, a built-in list of strategies\n+\tis used instead (`git-merge-recursive` when merging a single\n+\thead, `git-merge-octopus` otherwise).  This implies --merge.\n+\n+include::merge-strategies.txt[]\n+\n NOTES\n -----\n When you rebase a branch, you are changing its history in a way that\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex e6b57b8..bce7bf8 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -34,7 +34,96 @@ When you have resolved this problem run \n If you would prefer to skip this patch, instead run \\\"git rebase --skip\\\".\n To restore the original branch and stop rebasing run \\\"git rebase --abort\\\".\n \"\n+\n+MRESOLVEMSG=\"\n+When you have resolved this problem run \\\"git rebase --continue\\\".\n+To restore the original branch and stop rebasing run \\\"git rebase --abort\\\".\n+\"\n unset newbase\n+strategy=recursive\n+do_merge=\n+dotest=$GIT_DIR/.dotest-merge\n+prec=4\n+\n+continue_merge () {\n+\ttest -n \"$prev_head\" || die \"prev_head must be defined\"\n+\ttest -d \"$dotest\" || die \"$dotest directory does not exist\"\n+\n+\tunmerged=$(git-ls-files -u)\n+\tif test -n \"$unmerged\"\n+\tthen\n+\t\techo \"You still have unmerged paths in your index\"\n+\t\techo \"did you forget update-index?\"\n+\t\tdie \"$MRESOLVEMSG\"\n+\tfi\n+\n+\tif test -n \"`git-diff-index HEAD`\"\n+\tthen\n+\t\tgit-commit -C \"`cat $dotest/current`\"\n+\telse\n+\t\techo \"Previous merge succeeded automatically\"\n+\tfi\n+\n+\tprev_head=`git-rev-parse HEAD^0`\n+\n+\t# save the resulting commit so we can read-tree on it later\n+\techo \"$prev_head\" > \"$dotest/`printf %0${prec}d $msgnum`.result\"\n+\techo \"$prev_head\" > \"$dotest/prev_head\"\n+\n+\t# onto the next patch:\n+\tmsgnum=$(($msgnum + 1))\n+\tprintf \"%0${prec}d\" \"$msgnum\" > \"$dotest/msgnum\"\n+}\n+\n+call_merge () {\n+\tcmt=\"$(cat $dotest/`printf %0${prec}d $1`)\"\n+\techo \"$cmt\" > \"$dotest/current\"\n+\tgit-merge-$strategy \"$cmt^\" -- HEAD \"$cmt\"\n+\trv=$?\n+\tcase \"$rv\" in\n+\t0)\n+\t\tgit-commit -C \"$cmt\" || die \"commit failed: $MRESOLVEMSG\"\n+\t\t;;\n+\t1)\n+\t\ttest -d \"$GIT_DIR/rr-cache\" && git-rerere\n+\t\tdie \"$MRESOLVEMSG\"\n+\t\t;;\n+\t2)\n+\t\techo \"Strategy: $rv $strategy failed, try another\" 1>&2\n+\t\tdie \"$MRESOLVEMSG\"\n+\t\t;;\n+\t*)\n+\t\tdie \"Unknown exit code ($rv) from command:\" \\\n+\t\t\t\"git-merge-$strategy $cmt^ -- HEAD $cmt\"\n+\t\t;;\n+\tesac\n+}\n+\n+finish_rb_merge () {\n+\tset -e\n+\n+\tmsgnum=1\n+\techo \"Finalizing rebased commits...\"\n+\tgit-reset --hard \"`cat $dotest/onto`\"\n+\tend=\"`cat $dotest/end`\"\n+\twhile test \"$msgnum\" -le \"$end\"\n+\tdo\n+\t\tmsgnum=`printf \"%0${prec}d\" \"$msgnum\"`\n+\t\tprintf \"%0${prec}d\" \"$msgnum\" > \"$dotest/msgnum\"\n+\n+\t\tgit-read-tree `cat \"$dotest/$msgnum.result\"`\n+\t\tgit-checkout-index -q -f -u -a\n+\t\tgit-commit -C \"`cat $dotest/$msgnum`\"\n+\n+\t\techo \"Committed $msgnum\"\n+\t\techo '    '`git-rev-list --pretty=oneline -1 HEAD | \\\n+\t\t\t\t\tsed 's/^[a-f0-9]\\+ //'`\n+\t\tmsgnum=$(($msgnum + 1))\n+\tdone\n+\trm -r \"$dotest\"\n+\techo \"All done.\"\n+}\n+\n while case \"$#\" in 0) break ;; esac\n do\n \tcase \"$1\" in\n@@ -46,17 +135,43 @@ do\n \t\t\texit 1\n \t\t\t;;\n \t\tesac\n+\t\tif test -d \"$dotest\"\n+\t\tthen\n+\t\t\tprev_head=\"`cat $dotest/prev_head`\"\n+\t\t\tend=\"`cat $dotest/end`\"\n+\t\t\tmsgnum=\"`cat $dotest/msgnum`\"\n+\t\t\tonto=\"`cat $dotest/onto`\"\n+\t\t\tcontinue_merge\n+\t\t\twhile test \"$msgnum\" -le \"$end\"\n+\t\t\tdo\n+\t\t\t\tcall_merge \"$msgnum\"\n+\t\t\t\tcontinue_merge\n+\t\t\tdone\n+\t\t\tfinish_rb_merge\n+\t\t\texit\n+\t\tfi\n \t\tgit am --resolved --3way --resolvemsg=\"$RESOLVEMSG\"\n \t\texit\n \t\t;;\n \t--skip)\n+\t\tif test -d \"$dotest\"\n+\t\tthen\n+\t\t\tdie \"--skip is not supported when using --merge\"\n+\t\tfi\n \t\tgit am -3 --skip --resolvemsg=\"$RESOLVEMSG\"\n \t\texit\n \t\t;;\n \t--abort)\n-\t\t[ -d .dotest ] || die \"No rebase in progress?\"\n+\t\tif test -d \"$dotest\"\n+\t\tthen\n+\t\t\trm -r \"$dotest\"\n+\t\telif test -d .dotest\n+\t\tthen\n+\t\t\trm -r .dotest\n+\t\telse\n+\t\t\tdie \"No rebase in progress?\"\n+\t\tfi\n \t\tgit reset --hard ORIG_HEAD\n-\t\trm -r .dotest\n \t\texit\n \t\t;;\n \t--onto)\n@@ -64,6 +179,23 @@ do\n \t\tnewbase=\"$2\"\n \t\tshift\n \t\t;;\n+\t-M|-m|--m|--me|--mer|--merg|--merge)\n+\t\tdo_merge=t\n+\t\t;;\n+\t-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\\\n+\t\t--strateg=*|--strategy=*|\\\n+\t-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)\n+\t\tcase \"$#,$1\" in\n+\t\t*,*=*)\n+\t\t\tstrategy=`expr \"$1\" : '-[^=]*=\\(.*\\)'` ;;\n+\t\t1,*)\n+\t\t\tusage ;;\n+\t\t*)\n+\t\t\tstrategy=\"$2\"\n+\t\t\tshift ;;\n+\t\tesac\n+\t\tdo_merge=t\n+\t\t;;\n \t-*)\n \t\tusage\n \t\t;;\n@@ -75,16 +207,25 @@ do\n done\n \n # Make sure we do not have .dotest\n-if mkdir .dotest\n+if test -z \"$do_merge\"\n then\n-\trmdir .dotest\n-else\n-\techo >&2 '\n+\tif mkdir .dotest\n+\tthen\n+\t\trmdir .dotest\n+\telse\n+\t\techo >&2 '\n It seems that I cannot create a .dotest directory, and I wonder if you\n are in the middle of patch application or another rebase.  If that is not\n the case, please rm -fr .dotest and run me again.  I am stopping in case\n you still have something valuable there.'\n-\texit 1\n+\t\texit 1\n+\tfi\n+else\n+\tif test -d \"$dotest\"\n+\tthen\n+\t\tdie \"previous dotest directory $dotest still exists.\" \\\n+\t\t\t'try git-rebase < --continue | --abort >'\n+\tfi\n fi\n \n # The tree must be really really clean.\n@@ -152,6 +293,39 @@ then\n \texit 0\n fi\n \n-git-format-patch -k --stdout --full-index \"$upstream\"..ORIG_HEAD |\n-git am --binary -3 -k --resolvemsg=\"$RESOLVEMSG\"\n+if test -z \"$do_merge\"\n+then\n+\tgit-format-patch -k --stdout --full-index \"$upstream\"..ORIG_HEAD |\n+\tgit am --binary -3 -k --resolvemsg=\"$RESOLVEMSG\"\n+\texit $?\n+fi\n+\n+# start doing a rebase with git-merge\n+# this is rename-aware if the recursive (default) strategy is used\n+\n+mkdir -p \"$dotest\"\n+echo \"$onto\" > \"$dotest/onto\"\n+prev_head=`git-rev-parse HEAD^0`\n+echo \"$prev_head\" > \"$dotest/prev_head\"\n+\n+msgnum=0\n+for cmt in `git-rev-list --no-merges \"$upstream\"..ORIG_HEAD \\\n+\t\t\t| perl -e 'print reverse <>'`\n+do\n+\tmsgnum=$(($msgnum + 1))\n+\techo \"$cmt\" > \"$dotest/`printf \"%0${prec}d\" $msgnum`\"\n+done\n+\n+printf \"%0${prec}d\" 1 > \"$dotest/msgnum\"\n+printf \"%0${prec}d\" \"$msgnum\" > \"$dotest/end\"\n+\n+end=$msgnum\n+msgnum=1\n+\n+while test \"$msgnum\" -le \"$end\"\n+do\n+\tcall_merge \"$msgnum\"\n+\tcontinue_merge\n+done\n \n+finish_rb_merge\n-- \n1.4.0.g65f3\n"},{"id":"22191","messageId":"11508842853098-git-send-email-normalperson@yhbt.net","threadId":"4551","inReplyTo":"11508842842125-git-send-email-normalperson@yhbt.net","subject":"[PATCH 3/3] rebase: error out for NO_PYTHON if they use recursive merge","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2006-06-21T10:04:42Z","receivedAt":"2006-06-21T10:04:42Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"recursive merge relies on Python, and we can't perform\nrename-aware merges without the recursive merge.  So bail out\nbefore trying it.\n\nThe test won't work w/o recursive merge, either, so skip that,\ntoo.\n\nSigned-off-by: Eric Wong <normalperson@yhbt.net>\n---\n git-rebase.sh           |    9 +++++++++\n t/t3402-rebase-merge.sh |    6 ++++++\n 2 files changed, 15 insertions(+), 0 deletions(-)\n\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex bce7bf8..b9ce112 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -300,6 +300,15 @@ then\n \texit $?\n fi\n \n+if test \"@@NO_PYTHON@@\" && test \"$strategy\" = \"recursive\"\n+then\n+\tdie 'The recursive merge strategy currently relies on Python,\n+which this installation of git was not configured with.  Please consider\n+a different merge strategy (e.g. octopus, resolve, stupid, ours)\n+or install Python and git with Python support.'\n+\n+fi\n+\n # start doing a rebase with git-merge\n # this is rename-aware if the recursive (default) strategy is used\n \ndiff --git a/t/t3402-rebase-merge.sh b/t/t3402-rebase-merge.sh\nindex 8c7a519..f1c1f35 100755\n--- a/t/t3402-rebase-merge.sh\n+++ b/t/t3402-rebase-merge.sh\n@@ -7,6 +7,12 @@ test_description='git rebase --merge tes\n \n . ./test-lib.sh\n \n+if test \"$no_python\"; then\n+\techo \"Skipping: no python => no recursive merge\"\n+\ttest_done\n+\texit 0\n+fi\n+\n T=\"A quick brown fox\n jumps over the lazy dog.\"\n for i in 1 2 3 4 5 6 7 8 9 10\n-- \n1.4.0.g65f3\n"},{"id":"22197","messageId":"7vac8622nm.fsf@assigned-by-dhcp.cox.net","threadId":"4551","inReplyTo":"20060621100138.GA15748@localdomain","subject":"Re: [PATCH] rebase: Allow merge strategies to be used when rebasing","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-06-21T11:01:33Z","receivedAt":"2006-06-21T11:01:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Wong <normalperson@yhbt.net> writes:\n\n> Junio C Hamano <junkio@cox.net> wrote:\n>>\n>> >> \tgit-merge-$strategy $cmt^ -- HEAD $cmt\n>> >\n>> > Changing the 'git-merge $strategy_args \"rebase-merge: $cmt\" HEAD \"$cmt\"'\n>> > line in call_merge() to this seems to have broken more tests.\n>> \n>> Oh, that is to be expected if you changed git-merge -s recursive\n>> with git-merge-recursive without other changes.  The former\n>> makes a commit (which your original patch later used to create a\n>> separate commit chain and discarded); the latter does not make a\n>> commit but expects the caller to create a commit out of the\n>> resulting index file.\n>\n> Oops, *smacks head*\n\nWell, but you used it to do the right thing after all ;-).\nThe patch looks quite good.\n\n>> I was originally hoping that rebasing would just be a matter of\n>> listing sequence of commits to be ported onto a new base and\n>> running \"git-cherry-pick\" on each of them in sequence.  Now\n>> cherry-pick does not use merge machinery (hence does not use\n>> git-merge-recursive), but if we change that then updating rebase\n>> would be pretty much straightforward.  It just needs a UI layer\n>> to guide the user through recovery process when the merge does\n>> not resolve cleanly in the middle, no?\n>\n> Sounds workable right to me. But then again, a cherry-pick is also a\n> case of rebase on a single commit, so we could be using rebase (and its\n> recovery code) in cherry-pick, too, right?\n\nRevert and cherry-pick are quite similar operation (the only\ndifference is that you swap his and pivot when doing revert), so\nwhen you implement cherry-pick as an atomic operation you can\nhave revert almost for free.  If you have a rebase like you did,\nit would be a bit more involved to make it do revert as well.\n"},{"id":"22194","messageId":"7v4pye22nk.fsf@assigned-by-dhcp.cox.net","threadId":"4551","inReplyTo":"11508842824018-git-send-email-normalperson@yhbt.net","subject":"Re: [PATCH (fixed)] rebase: Allow merge strategies to be used when rebasing","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-06-21T11:01:35Z","receivedAt":"2006-06-21T11:01:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Wong <normalperson@yhbt.net> writes:\n\n> 1 - is fixed for --onto usage.\n\nMore importantly, it used to rebase \"side edits further\" commit\nincorrectly in the test sequence -- the commit should only touch\n\"newfile\", but it touched \"renamed\" as well.  Your updated patch\nfixes this problem.\n"}]}