{"thread":{"id":"23038","subject":"[PATCH] Teach --no-ff option to 'rebase -i'.","startedAt":"2010-03-16T16:08:55Z","lastAt":"2010-03-24T21:45:33Z","messageCount":32,"participants":["Marc Branchaud","Jonathan Nieder","Johannes Sixt","Peter Baumann","Avery Pennarun","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"136962","messageId":"1268755735-20588-1-git-send-email-marcnarc@xiplink.com","threadId":"23038","inReplyTo":null,"subject":"[PATCH] Teach --no-ff option to 'rebase -i'.","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2010-03-16T16:08:55Z","receivedAt":"2010-03-16T16:08:55Z","isPatch":true,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"This option tells rebase--interactive to cherry-pick all the commits in the\nrebased branch, instead of fast-forwarding over any unchanged commits.\n\nSigned-off-by: Marc Branchaud <marcnarc@xiplink.com>\n---\n\nThis offers an alterntive way to deal with reverted merges (the\nrevert-a-faulty-merge.txt howto recommends reverting the initial reversion\nbefore re-merging a modified topic branch).\n\nWith this change, you can instead use the --no-ff option to recreate the\nbranch with entirely new commits (they're new because at the very least the\ncommitter time is different).  This obviates the need to revert the\nreversion, as you can re-merge the new topic branch directly.\n\n(Honestly, I wouldn't say that this approach is vastly superior to\nreverting the reversion.  I just find it a little less messy and a little\nmore intuitive.  It's also a bit easier to explain to people to \"use --no-ff\nafter reverting a merge\" instead of making sure they get the double-\nreversion right.)\n\n\t\tM.\n\n Documentation/git-rebase.txt |   13 ++++++++++++-\n git-rebase--interactive.sh   |   10 ++++++++--\n t/t3417-rebase-no-ff.sh      |   36 ++++++++++++++++++++++++++++++++++++\n 3 files changed, 56 insertions(+), 3 deletions(-)\n create mode 100755 t/t3417-rebase-no-ff.sh\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 823f2a4..01f1476 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -316,7 +316,18 @@ which makes little sense.\n \tcommit to be modified, and change the action of the moved\n \tcommit from `pick` to `squash` (or `fixup`).\n +\n-This option is only valid when '--interactive' option is used.\n+This option is only valid when the '--interactive' option is used.\n+\n+--no-ff::\n+\tCherry-pick all rebased commits instead of fast-forwarding over\n+\tthe unchanged ones.  This ensures that the entire history of the\n+\trebased branch is composed of new commits.  You may find this\n+\thelpful after reverting a topic branch merge, as this option\n+\trecreates the topic branch with fresh commits so it can be remerged\n+\tsuccessfully without needing to \"reverting the reversion\" (as described\n+\tin the link:howto/revert-a-faulty-merge.txt[revert-a-faulty-merge How-To]).\n++\n+This option is only valid when the '--interactive' option is used.\n \n include::merge-strategies.txt[]\n \ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex 3e4fd14..aecac3e 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -20,6 +20,7 @@ v,verbose          display a diffstat of what changed upstream\n onto=              rebase onto given branch instead of upstream\n p,preserve-merges  try to recreate merges instead of ignoring them\n s,strategy=        use the given merge strategy\n+no-ff              never fast-forward any commits, even if they're unchanged\n m,merge            always used (no-op)\n i,interactive      always used (no-op)\n  Actions:\n@@ -103,6 +104,7 @@ VERBOSE=\n OK_TO_SKIP_PRE_REBASE=\n REBASE_ROOT=\n AUTOSQUASH=\n+NO_SKIP=\n \n GIT_CHERRY_PICK_HELP=\"  After resolving the conflicts,\n mark the corrected paths with 'git add <paths>', and\n@@ -222,7 +224,7 @@ do_with_author () {\n }\n \n pick_one () {\n-\tno_ff=\n+\tno_ff=$NO_SKIP\n \tcase \"$1\" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac\n \toutput git rev-parse --verify $sha1 || die \"Invalid commit name: $sha1\"\n \ttest -d \"$REWRITTEN\" &&\n@@ -742,6 +744,10 @@ first and then run 'git rebase --continue' again.\"\n \t-i)\n \t\t# yeah, we know\n \t\t;;\n+\t--no-ff)\n+\t\t# Set -n parameter to pass to pick_one function\n+\t\tNO_SKIP=t\n+\t\t;;\n \t--root)\n \t\tREBASE_ROOT=t\n \t\t;;\n@@ -927,7 +933,7 @@ EOF\n \t\thas_action \"$TODO\" ||\n \t\t\tdie_abort \"Nothing to do\"\n \n-\t\ttest -d \"$REWRITTEN\" || skip_unnecessary_picks\n+\t\ttest -d \"$REWRITTEN\" || ( test -z \"$NO_SKIP\" && skip_unnecessary_picks )\n \n \t\tgit update-ref ORIG_HEAD $HEAD\n \t\toutput git checkout $ONTO && do_rest\ndiff --git a/t/t3417-rebase-no-ff.sh b/t/t3417-rebase-no-ff.sh\nnew file mode 100755\nindex 0000000..29455da\n--- /dev/null\n+++ b/t/t3417-rebase-no-ff.sh\n@@ -0,0 +1,36 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2010 Marc Branchaud\n+#\n+\n+test_description='git rebase -i --no-ff tests'\n+\n+. ./test-lib.sh\n+\n+. \"$TEST_DIRECTORY\"/lib-rebase.sh\n+\n+set_fake_editor\n+\n+test_expect_success setup '\n+\techo hello > hello &&\n+\tgit add hello &&\n+\tgit commit -m \"hello\" &&\n+\n+\techo world >> hello &&\n+\tgit commit -a -m \"hello world\" &&\n+\n+\techo goodbye >> hello &&\n+\tgit commit -a -m \"goodbye\" &&\n+\n+\tgit tag old_head\n+\t'\n+# Pause to ensure that the cherry-picked commits have a different\n+# timestamp.\n+sleep 1\n+\n+test_expect_success rebase '\n+\tgit rebase -i --no-ff HEAD~2 &&\n+\ttest ! $(git rev-parse HEAD) = $(git rev-parse old_head)\n+\t'\n+\n+test_done\n-- \n1.7.0.2.dirty\n"},{"id":"136970","messageId":"4B9FD9C1.9060200@xiplink.com","threadId":"23038","inReplyTo":"1268755735-20588-1-git-send-email-marcnarc@xiplink.com","subject":"Re: [PATCH] Teach --no-ff option to 'rebase -i'.","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2010-03-16T19:19:29Z","receivedAt":"2010-03-16T19:19:29Z","isPatch":true,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"Apologies -- this patch fails several rebase unit tests.\n\nBack to the drawing board...\n\n\t\tM.\n\n\nMarc Branchaud wrote:\n> This option tells rebase--interactive to cherry-pick all the commits in the\n> rebased branch, instead of fast-forwarding over any unchanged commits.\n> \n> Signed-off-by: Marc Branchaud <marcnarc@xiplink.com>\n> ---\n> \n> This offers an alterntive way to deal with reverted merges (the\n> revert-a-faulty-merge.txt howto recommends reverting the initial reversion\n> before re-merging a modified topic branch).\n> \n> With this change, you can instead use the --no-ff option to recreate the\n> branch with entirely new commits (they're new because at the very least the\n> committer time is different).  This obviates the need to revert the\n> reversion, as you can re-merge the new topic branch directly.\n> \n> (Honestly, I wouldn't say that this approach is vastly superior to\n> reverting the reversion.  I just find it a little less messy and a little\n> more intuitive.  It's also a bit easier to explain to people to \"use --no-ff\n> after reverting a merge\" instead of making sure they get the double-\n> reversion right.)\n> \n> \t\tM.\n> \n>  Documentation/git-rebase.txt |   13 ++++++++++++-\n>  git-rebase--interactive.sh   |   10 ++++++++--\n>  t/t3417-rebase-no-ff.sh      |   36 ++++++++++++++++++++++++++++++++++++\n>  3 files changed, 56 insertions(+), 3 deletions(-)\n>  create mode 100755 t/t3417-rebase-no-ff.sh\n> \n> diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\n> index 823f2a4..01f1476 100644\n> --- a/Documentation/git-rebase.txt\n> +++ b/Documentation/git-rebase.txt\n> @@ -316,7 +316,18 @@ which makes little sense.\n>  \tcommit to be modified, and change the action of the moved\n>  \tcommit from `pick` to `squash` (or `fixup`).\n>  +\n> -This option is only valid when '--interactive' option is used.\n> +This option is only valid when the '--interactive' option is used.\n> +\n> +--no-ff::\n> +\tCherry-pick all rebased commits instead of fast-forwarding over\n> +\tthe unchanged ones.  This ensures that the entire history of the\n> +\trebased branch is composed of new commits.  You may find this\n> +\thelpful after reverting a topic branch merge, as this option\n> +\trecreates the topic branch with fresh commits so it can be remerged\n> +\tsuccessfully without needing to \"reverting the reversion\" (as described\n> +\tin the link:howto/revert-a-faulty-merge.txt[revert-a-faulty-merge How-To]).\n> ++\n> +This option is only valid when the '--interactive' option is used.\n>  \n>  include::merge-strategies.txt[]\n>  \n> diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\n> index 3e4fd14..aecac3e 100755\n> --- a/git-rebase--interactive.sh\n> +++ b/git-rebase--interactive.sh\n> @@ -20,6 +20,7 @@ v,verbose          display a diffstat of what changed upstream\n>  onto=              rebase onto given branch instead of upstream\n>  p,preserve-merges  try to recreate merges instead of ignoring them\n>  s,strategy=        use the given merge strategy\n> +no-ff              never fast-forward any commits, even if they're unchanged\n>  m,merge            always used (no-op)\n>  i,interactive      always used (no-op)\n>   Actions:\n> @@ -103,6 +104,7 @@ VERBOSE=\n>  OK_TO_SKIP_PRE_REBASE=\n>  REBASE_ROOT=\n>  AUTOSQUASH=\n> +NO_SKIP=\n>  \n>  GIT_CHERRY_PICK_HELP=\"  After resolving the conflicts,\n>  mark the corrected paths with 'git add <paths>', and\n> @@ -222,7 +224,7 @@ do_with_author () {\n>  }\n>  \n>  pick_one () {\n> -\tno_ff=\n> +\tno_ff=$NO_SKIP\n>  \tcase \"$1\" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac\n>  \toutput git rev-parse --verify $sha1 || die \"Invalid commit name: $sha1\"\n>  \ttest -d \"$REWRITTEN\" &&\n> @@ -742,6 +744,10 @@ first and then run 'git rebase --continue' again.\"\n>  \t-i)\n>  \t\t# yeah, we know\n>  \t\t;;\n> +\t--no-ff)\n> +\t\t# Set -n parameter to pass to pick_one function\n> +\t\tNO_SKIP=t\n> +\t\t;;\n>  \t--root)\n>  \t\tREBASE_ROOT=t\n>  \t\t;;\n> @@ -927,7 +933,7 @@ EOF\n>  \t\thas_action \"$TODO\" ||\n>  \t\t\tdie_abort \"Nothing to do\"\n>  \n> -\t\ttest -d \"$REWRITTEN\" || skip_unnecessary_picks\n> +\t\ttest -d \"$REWRITTEN\" || ( test -z \"$NO_SKIP\" && skip_unnecessary_picks )\n>  \n>  \t\tgit update-ref ORIG_HEAD $HEAD\n>  \t\toutput git checkout $ONTO && do_rest\n> diff --git a/t/t3417-rebase-no-ff.sh b/t/t3417-rebase-no-ff.sh\n> new file mode 100755\n> index 0000000..29455da\n> --- /dev/null\n> +++ b/t/t3417-rebase-no-ff.sh\n> @@ -0,0 +1,36 @@\n> +#!/bin/sh\n> +#\n> +# Copyright (c) 2010 Marc Branchaud\n> +#\n> +\n> +test_description='git rebase -i --no-ff tests'\n> +\n> +. ./test-lib.sh\n> +\n> +. \"$TEST_DIRECTORY\"/lib-rebase.sh\n> +\n> +set_fake_editor\n> +\n> +test_expect_success setup '\n> +\techo hello > hello &&\n> +\tgit add hello &&\n> +\tgit commit -m \"hello\" &&\n> +\n> +\techo world >> hello &&\n> +\tgit commit -a -m \"hello world\" &&\n> +\n> +\techo goodbye >> hello &&\n> +\tgit commit -a -m \"goodbye\" &&\n> +\n> +\tgit tag old_head\n> +\t'\n> +# Pause to ensure that the cherry-picked commits have a different\n> +# timestamp.\n> +sleep 1\n> +\n> +test_expect_success rebase '\n> +\tgit rebase -i --no-ff HEAD~2 &&\n> +\ttest ! $(git rev-parse HEAD) = $(git rev-parse old_head)\n> +\t'\n> +\n> +test_done\n"},{"id":"136971","messageId":"1268768556-32176-1-git-send-email-marcnarc@xiplink.com","threadId":"23038","inReplyTo":"1268755735-20588-1-git-send-email-marcnarc@xiplink.com","subject":"[PATCHv2] Teach --no-ff option to 'rebase -i'.","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2010-03-16T19:42:36Z","receivedAt":"2010-03-16T19:42:36Z","isPatch":false,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"This option tells rebase--interactive to cherry-pick all the commits in the\nrebased branch, instead of fast-forwarding over any unchanged commits.\n\nSigned-off-by: Marc Branchaud <marcnarc@xiplink.com>\n---\n\nNow passes all rebase unit tests.  I'll repeat my rationale from the v1\nposting:\n\nThis offers an alterntive way to deal with reverted merges (the\nrevert-a-faulty-merge.txt howto recommends reverting the initial reversion\nbefore re-merging a modified topic branch).\n\nWith this change, you can instead use the --no-ff option to recreate the\nbranch with entirely new commits (they're new because at the very least the\ncommitter time is different).  This obviates the need to revert the\nreversion, as you can re-merge the new topic branch directly.\n\n(Honestly, I wouldn't say that this approach is vastly superior to\nreverting the reversion.  I just find it a little less messy and a little\nmore intuitive.  It's also a bit easier to explain to people to \"use --no-ff\nafter reverting a merge\" instead of making sure they get the double-\nreversion right.)\n\n\t\tM.\n\n Documentation/git-rebase.txt |   13 ++++++++++++-\n git-rebase--interactive.sh   |   10 ++++++++--\n t/t3417-rebase-no-ff.sh      |   36 ++++++++++++++++++++++++++++++++++++\n 3 files changed, 56 insertions(+), 3 deletions(-)\n create mode 100755 t/t3417-rebase-no-ff.sh\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 823f2a4..01f1476 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -316,7 +316,18 @@ which makes little sense.\n \tcommit to be modified, and change the action of the moved\n \tcommit from `pick` to `squash` (or `fixup`).\n +\n-This option is only valid when '--interactive' option is used.\n+This option is only valid when the '--interactive' option is used.\n+\n+--no-ff::\n+\tCherry-pick all rebased commits instead of fast-forwarding over\n+\tthe unchanged ones.  This ensures that the entire history of the\n+\trebased branch is composed of new commits.  You may find this\n+\thelpful after reverting a topic branch merge, as this option\n+\trecreates the topic branch with fresh commits so it can be remerged\n+\tsuccessfully without needing to \"reverting the reversion\" (as described\n+\tin the link:howto/revert-a-faulty-merge.txt[revert-a-faulty-merge How-To]).\n++\n+This option is only valid when the '--interactive' option is used.\n \n include::merge-strategies.txt[]\n \ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex 3e4fd14..35943c0 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -20,6 +20,7 @@ v,verbose          display a diffstat of what changed upstream\n onto=              rebase onto given branch instead of upstream\n p,preserve-merges  try to recreate merges instead of ignoring them\n s,strategy=        use the given merge strategy\n+no-ff              never fast-forward any commits, even if they're unchanged\n m,merge            always used (no-op)\n i,interactive      always used (no-op)\n  Actions:\n@@ -103,6 +104,7 @@ VERBOSE=\n OK_TO_SKIP_PRE_REBASE=\n REBASE_ROOT=\n AUTOSQUASH=\n+NO_SKIP=\n \n GIT_CHERRY_PICK_HELP=\"  After resolving the conflicts,\n mark the corrected paths with 'git add <paths>', and\n@@ -222,7 +224,7 @@ do_with_author () {\n }\n \n pick_one () {\n-\tno_ff=\n+\tno_ff=$NO_SKIP\n \tcase \"$1\" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac\n \toutput git rev-parse --verify $sha1 || die \"Invalid commit name: $sha1\"\n \ttest -d \"$REWRITTEN\" &&\n@@ -742,6 +744,10 @@ first and then run 'git rebase --continue' again.\"\n \t-i)\n \t\t# yeah, we know\n \t\t;;\n+\t--no-ff)\n+\t\t# Set -n parameter to pass to pick_one function\n+\t\tNO_SKIP=t\n+\t\t;;\n \t--root)\n \t\tREBASE_ROOT=t\n \t\t;;\n@@ -927,7 +933,7 @@ EOF\n \t\thas_action \"$TODO\" ||\n \t\t\tdie_abort \"Nothing to do\"\n \n-\t\ttest -d \"$REWRITTEN\" || skip_unnecessary_picks\n+\t\ttest -d \"$REWRITTEN\" || test -n \"$NO_SKIP\" || skip_unnecessary_picks\n \n \t\tgit update-ref ORIG_HEAD $HEAD\n \t\toutput git checkout $ONTO && do_rest\ndiff --git a/t/t3417-rebase-no-ff.sh b/t/t3417-rebase-no-ff.sh\nnew file mode 100755\nindex 0000000..2a2be81\n--- /dev/null\n+++ b/t/t3417-rebase-no-ff.sh\n@@ -0,0 +1,36 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2010 Marc Branchaud\n+#\n+\n+test_description='git rebase -i --no-ff tests'\n+\n+. ./test-lib.sh\n+\n+. \"$TEST_DIRECTORY\"/lib-rebase.sh\n+\n+set_fake_editor\n+\n+test_expect_success setup '\n+\techo hello > hello &&\n+\tgit add hello &&\n+\tgit commit -m \"hello\" &&\n+\n+\techo world >> hello &&\n+\tgit commit -a -m \"hello world\" &&\n+\n+\techo goodbye >> hello &&\n+\tgit commit -a -m \"goodbye\" &&\n+\n+\tgit tag old_head\n+\t'\n+# Pause to ensure that the cherry-picked commits have a different\n+# timestamp.\n+sleep 1\n+\n+test_expect_success 'rebase -i --no-ff' '\n+\tgit rebase -i --no-ff HEAD~2 &&\n+\ttest ! $(git rev-parse HEAD) = $(git rev-parse old_head)\n+\t'\n+\n+test_done\n-- \n1.7.0.2.1.g6d104.dirty\n"},{"id":"136974","messageId":"20100316214717.GA24880@progeny.tock","threadId":"23038","inReplyTo":"1268768556-32176-1-git-send-email-marcnarc@xiplink.com","subject":"Re: [PATCHv2] Teach --no-ff option to 'rebase -i'.","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-03-16T21:47:17Z","receivedAt":"2010-03-16T21:47:17Z","isPatch":false,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi Marc,\n\nMarc Branchaud wrote:\n\n> This option tells rebase--interactive to cherry-pick all the commits in the\n> rebased branch, instead of fast-forwarding over any unchanged commits.\n> \n> Signed-off-by: Marc Branchaud <marcnarc@xiplink.com>\n[...]\n> This offers an alterntive way to deal with reverted merges (the\n> revert-a-faulty-merge.txt howto recommends reverting the initial reversion\n> before re-merging a modified topic branch).\n> \n> With this change, you can instead use the --no-ff option to recreate the\n> branch with entirely new commits (they're new because at the very least the\n> committer time is different).  This obviates the need to revert the\n> reversion, as you can re-merge the new topic branch directly.\n\nI think this rationale should go in the commit message, for the sake of\nfuture readers studying the code you’ve changed.\n\nBut let me make sure I understand it.\n\nIf I am understanding properly, your idea is that this would be used on\na branch after “unmerging” it from master:\n\n    B --- C --- D [topic]\n  /              \\\n A ---  ...   --- M ... --- U [master]\n\nHere M is a merge commit and U a commit reverting the change from M^\nto M.\n\nAs howto/revert-a-faulty-merge explains, this is an accident waiting to\nhappen: if you merge master into topic, then it will pull in the revert\nalong with the rest of the changes.  Similarly, if you merge topic into\nmaster, it will be a no-op (“already up to date”).\n\nYour solution, to rewrite the branch, should address both of these\nproblems.  The new history looks like this:\n\n    B' --- C' --- D' [topic]\n  /\n |  B --- C --- D\n |/              \\\n A ---   ...  --- M ... --- U [master]\n\nWhat happens now if you merge topic into master (or master into topic)?\n\nGit will try to reconcile the changes from the two branches since when\nthey diverged.  master gained the changes from the old topic branch and\nthen reverted them since it diverged from topic; since merge algorithm\nis a simple three-way merge, all that history is ignored and Git will\njust combine any new changes from master with the changes from topic.\nSuccess.\n\nSide note:\n\nAfter this maneuver, I would want to merge ‘master’ into ‘topic’ as\nsoon as possible.  Why?  Because there is still a failure mode possible\nafter this: if you merge any commits from the old topic into the new\none before then, you’re back in trouble.  This could happen if some\ntopic2 that topic ends up needing is based on a point in master after M\nand before U.\n\nAfter merging master into topic, the _meaning_ of the history is much\nclearer: by making U an ancestor, we are saying that as far as this\nbranch is concerned, the state at the tip of the topic branch is to be\npreferred over the reverted state.\n\nThis suggests an alternative method to achieve your goal:\n\n git checkout master\n git revert -m 1 M;\t# (1)\n git checkout topic\n git merge master^;\t# (2)\n git merge -s ours master;\t# (3)\n\n1. Add a commit U removing the changes introduced by topic from master.\n\n2. Merge all changes from master before the revert into topic.\n\n3. Declare the changes from topic to supersede the master branch.\n   This way, if topic is ever merged back to master, the changes will be\n   reintroduced again.\n\nResulting history:\n\n    B --- C --- D --- ... --- T^ --- T [topic]\n  /              \\            /     /\n A ---   ...  --- M ... --- U^ --- U [master]\n\nYou can avoid the perhaps pointless commit T^ by using --no-commit in\nthe command (2).  If not all changes from M..master are suitable for\nmerging into topic, you can use the same trick on a temporary side\nbranch:\n\n git checkout -b revert-topic M\n git revert -m 1 M\n git checkout topic\n git merge --no-commit revert-topic^\n git merge -s ours revert-topic\n git checkout master\n git merge revert-topic\n\nIf not all changes from topic..M are suitable for merging into topic,\nthen things are harder.  I’d suggest making a special unrevert-topic\nbranch as above to keep in the wings until its time.\n\n> (Honestly, I wouldn't say that this approach is vastly superior to\n> reverting the reversion.  I just find it a little less messy and a little\n> more intuitive.  It's also a bit easier to explain to people to \"use --no-ff\n> after reverting a merge\" instead of making sure they get the double-\n> reversion right.)\n\nHope this helps clarify things.\n\nYour patch itself might still be a good idea.  I think plain\n‘git rebase’ already has something like it, in the form of the\n--force-rebase option.  I have used it before, though I don’t remember\nwhy.\n\nThanks for the food for thought,\nJonathan\n"},{"id":"136991","messageId":"4BA07DC7.9070502@viscovery.net","threadId":"23038","inReplyTo":"20100316214717.GA24880@progeny.tock","subject":"Re: [PATCHv2] Teach --no-ff option to 'rebase -i'.","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2010-03-17T06:59:19Z","receivedAt":"2010-03-17T06:59:19Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Jonathan Nieder schrieb:\n> If I am understanding properly, your idea is that this would be used on\n> a branch after “unmerging” it from master:\n> \n>     B --- C --- D [topic]\n>   /              \\\n>  A ---  ...   --- M ... --- U [master]\n> \n> Here M is a merge commit and U a commit reverting the change from M^\n> to M.\n\nIf I were to re-merge topic into master a second time after this\nsituation, I would install a temporary graft that removes the second\nparent of M and repeat the merge. After the graft is removed, the history\nwould look like this:\n\n     B --- C --- D --------------.   [topic]\n   /              \\               \\\n  A ---  ...   --- M ... --- U ... N [master]\n\nAre there any downsides? I don't know - I haven't thought it through.\n\n-- Hannes\n"},{"id":"137025","messageId":"4BA0FBC7.3080305@xiplink.com","threadId":"23038","inReplyTo":"20100316214717.GA24880@progeny.tock","subject":"Re: [PATCHv2] Teach --no-ff option to 'rebase -i'.","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2010-03-17T15:56:55Z","receivedAt":"2010-03-17T15:56:55Z","isPatch":false,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"Jonathan Nieder wrote:\n> Hi Marc,\n> \n> Marc Branchaud wrote:\n> \n>> This option tells rebase--interactive to cherry-pick all the commits in the\n>> rebased branch, instead of fast-forwarding over any unchanged commits.\n>>\n>> Signed-off-by: Marc Branchaud <marcnarc@xiplink.com>\n> [...]\n>> This offers an alterntive way to deal with reverted merges (the\n>> revert-a-faulty-merge.txt howto recommends reverting the initial reversion\n>> before re-merging a modified topic branch).\n>>\n>> With this change, you can instead use the --no-ff option to recreate the\n>> branch with entirely new commits (they're new because at the very least the\n>> committer time is different).  This obviates the need to revert the\n>> reversion, as you can re-merge the new topic branch directly.\n> \n> I think this rationale should go in the commit message, for the sake of\n> future readers studying the code you’ve changed.\n\nEasy to do, but how necessary?  The rationale is already in the description\nof --no-ff in rebase's man page.\n\nThere's an extra aspect to my motivation that I failed to mention, which is\nthat I would like the topic branch to stay rooted at its original commit.\nThis is because in our workflow that original commit is a synchronization\npoint for several maintenance and \"next\"-style branches, and keeping the\ntopic rooted there makes it easy to merge into different branches.\n\n> But let me make sure I understand it.\n> \n> If I am understanding properly, your idea is that this would be used on\n> a branch after “unmerging” it from master:\n> \n>     B --- C --- D [topic]\n>   /              \\\n>  A ---  ...   --- M ... --- U [master]\n> \n> Here M is a merge commit and U a commit reverting the change from M^\n> to M.\n> \n> As howto/revert-a-faulty-merge explains, this is an accident waiting to\n> happen: if you merge master into topic, then it will pull in the revert\n> along with the rest of the changes.  Similarly, if you merge topic into\n> master, it will be a no-op (“already up to date”).\n> \n> Your solution, to rewrite the branch, should address both of these\n> problems.  The new history looks like this:\n> \n>     B' --- C' --- D' [topic]\n>   /\n>  |  B --- C --- D\n>  |/              \\\n>  A ---   ...  --- M ... --- U [master]\n> \n> What happens now if you merge topic into master (or master into topic)?\n> \n> Git will try to reconcile the changes from the two branches since when\n> they diverged.  master gained the changes from the old topic branch and\n> then reverted them since it diverged from topic; since merge algorithm\n> is a simple three-way merge, all that history is ignored and Git will\n> just combine any new changes from master with the changes from topic.\n> Success.\n\nYup, you got it!\n\n> Side note:\n> \n> After this maneuver, I would want to merge ‘master’ into ‘topic’ as\n> soon as possible.\n\nWell, I wouldn't want to do that because it would make it impossible to merge\nthe topic into some other branch without also bringing in all of master.\n\n> Why?  Because there is still a failure mode possible\n> after this: if you merge any commits from the old topic into the new\n> one before then, you’re back in trouble.  This could happen if some\n> topic2 that topic ends up needing is based on a point in master after M\n> and before U.\n\nIf the re-cast topic1 doesn't rewrite those commits, then the merge will\nsimply succeed because the code is already identical in both branches.\n\nBut if topic1 does rewrite those commits then there'll be a conflict.  IMO\nthat's correct, because with the merging of topic2 the code in master really\ndid diverge in a relevant way from what's in topic1, so that conflict should\nget resolved in the normal way.\n\n> After merging master into topic, the _meaning_ of the history is much\n> clearer: by making U an ancestor, we are saying that as far as this\n> branch is concerned, the state at the tip of the topic branch is to be\n> preferred over the reverted state.\n\nYes, but it also means that all the history of master since commit A is now\npart of the topic, which isn't acceptable to me.\n\n> This suggests an alternative method to achieve your goal:\n> \n>  git checkout master\n>  git revert -m 1 M;\t# (1)\n>  git checkout topic\n>  git merge master^;\t# (2)\n>  git merge -s ours master;\t# (3)\n> \n> 1. Add a commit U removing the changes introduced by topic from master.\n> \n> 2. Merge all changes from master before the revert into topic.\n> \n> 3. Declare the changes from topic to supersede the master branch.\n>    This way, if topic is ever merged back to master, the changes will be\n>    reintroduced again.\n> \n> Resulting history:\n> \n>     B --- C --- D --- ... --- T^ --- T [topic]\n>   /              \\            /     /\n>  A ---   ...  --- M ... --- U^ --- U [master]\n\nI understand how this achieves your goal but, aside from dragging all of\nmaster into the topic, this is way more complicated than recreating the topic\nor even doing the double-revert.  By creating T with \"-s ours\" U becomes like\na surrogate ancestor of the topic -- it's there, but it doesn't share any\ngenetic material with the topic.  I think that's a recipe for confusion to\nfuture explorers of the repo's history.\n\nBesides, I do expect at least one of the topic's original commits to actually\nbe different in the recast topic -- the original merge was reverted because\nthe topic was faulty in some way, so there needs to be a real change made\nsomewhere.  If the topic could simply be fixed with an extra commit or two\nafter D then re-merged, there wouldn't be a need to revert the original merge\nin the first place.\n\n> You can avoid the perhaps pointless commit T^ by using --no-commit in\n> the command (2).  If not all changes from M..master are suitable for\n> merging into topic, you can use the same trick on a temporary side\n> branch:\n> \n>  git checkout -b revert-topic M\n>  git revert -m 1 M\n>  git checkout topic\n>  git merge --no-commit revert-topic^\n>  git merge -s ours revert-topic\n>  git checkout master\n>  git merge revert-topic\n> \n> If not all changes from topic..M are suitable for merging into topic,\n> then things are harder.  I’d suggest making a special unrevert-topic\n> branch as above to keep in the wings until its time.\n\nThis still brings all of the master's A..M^ commits into the topic.\n\n>> (Honestly, I wouldn't say that this approach is vastly superior to\n>> reverting the reversion.  I just find it a little less messy and a little\n>> more intuitive.  It's also a bit easier to explain to people to \"use --no-ff\n>> after reverting a merge\" instead of making sure they get the double-\n>> reversion right.)\n> \n> Hope this helps clarify things.\n> \n> Your patch itself might still be a good idea.  I think plain\n> ‘git rebase’ already has something like it, in the form of the\n> --force-rebase option.  I have used it before, though I don’t remember\n> why.\n\nNo, the man page says --force-rebase does something else.\n\nThanks for the feedback!\n\n\t\tM.\n"},{"id":"137027","messageId":"20100317155842.GA2557@m62s10.vlinux.de","threadId":"23038","inReplyTo":"4BA07DC7.9070502@viscovery.net","subject":"Re: [PATCHv2] Teach --no-ff option to 'rebase -i'.","fromName":"Peter Baumann","fromEmail":"waste.manager@gmx.de","sentAt":"2010-03-17T15:58:42Z","receivedAt":"2010-03-17T15:58:42Z","isPatch":false,"sender":{"key":"waste.manager@gmx.de","avatar":null},"body":"On Wed, Mar 17, 2010 at 07:59:19AM +0100, Johannes Sixt wrote:\n> Jonathan Nieder schrieb:\n> > If I am understanding properly, your idea is that this would be used on\n> > a branch after “unmerging” it from master:\n> > \n> >     B --- C --- D [topic]\n> >   /              \\\n> >  A ---  ...   --- M ... --- U [master]\n> > \n> > Here M is a merge commit and U a commit reverting the change from M^\n> > to M.\n> \n> If I were to re-merge topic into master a second time after this\n> situation, I would install a temporary graft that removes the second\n> parent of M and repeat the merge. After the graft is removed, the history\n> would look like this:\n> \n>      B --- C --- D --------------.   [topic]\n>    /              \\               \\\n>   A ---  ...   --- M ... --- U ... N [master]\n> \n> Are there any downsides? I don't know - I haven't thought it through.\n> \n\nMight be. If there is any branch starting anywhere in between M and U\nwhich also needs to merge [topic] will also cause you headaches :-)\n\n       B --- C --- D --------------.   [topic]\n     /              \\               \\\n    A ---  ...   --- M ... --- U ... N [master]\n                         \\\n                          x --- y [side_branch wich needs to merge topic] \n\n--\nPeter\n"},{"id":"137028","messageId":"4BA0FD5B.5090408@xiplink.com","threadId":"23038","inReplyTo":"4BA07DC7.9070502@viscovery.net","subject":"Re: [PATCHv2] Teach --no-ff option to 'rebase -i'.","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2010-03-17T16:03:39Z","receivedAt":"2010-03-17T16:03:39Z","isPatch":false,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"Johannes Sixt wrote:\n> Jonathan Nieder schrieb:\n>> If I am understanding properly, your idea is that this would be used on\n>> a branch after “unmerging” it from master:\n>>\n>>     B --- C --- D [topic]\n>>   /              \\\n>>  A ---  ...   --- M ... --- U [master]\n>>\n>> Here M is a merge commit and U a commit reverting the change from M^\n>> to M.\n> \n> If I were to re-merge topic into master a second time after this\n> situation, I would install a temporary graft that removes the second\n> parent of M and repeat the merge. After the graft is removed, the history\n> would look like this:\n> \n>      B --- C --- D --------------.   [topic]\n>    /              \\               \\\n>   A ---  ...   --- M ... --- U ... N [master]\n> \n> Are there any downsides? I don't know - I haven't thought it through.\n\nI'm not sure I follow how to create that graft.\n\nBut the original point (which I hadn't made clear) is that at least one of\nthe topic's commits needs to change in some substantial way.  So it's not\njust a straight re-merge but a new take on the topic.\n\nConsider that if the topic's first commit (B) needed to be rewritten then the\nrepaired topic would contain only new commits and it could be merged into\nmaster without reverting the first merge's reversion.\n\nWhat \"rebase -i --no-ff\" does is allow you to ensure that this will always be\nthe case, even if you don't actually need to change the topic's first commit.\n\n\t\tM.\n"},{"id":"137029","messageId":"4BA0FE59.7020303@viscovery.net","threadId":"23038","inReplyTo":"20100317155842.GA2557@m62s10.vlinux.de","subject":"Re: [PATCHv2] Teach --no-ff option to 'rebase -i'.","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2010-03-17T16:07:53Z","receivedAt":"2010-03-17T16:07:53Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Peter Baumann schrieb:\n> On Wed, Mar 17, 2010 at 07:59:19AM +0100, Johannes Sixt wrote:\n>> If I were to re-merge topic into master a second time after this\n>> situation, I would install a temporary graft that removes the second\n>> parent of M and repeat the merge. After the graft is removed, the history\n>> would look like this:\n>>\n>>      B --- C --- D --------------.   [topic]\n>>    /              \\               \\\n>>   A ---  ...   --- M ... --- U ... N [master]\n>>\n>> Are there any downsides? I don't know - I haven't thought it through.\n>>\n> \n> Might be. If there is any branch starting anywhere in between M and U\n> which also needs to merge [topic] will also cause you headaches :-)\n> \n>        B --- C --- D --------------.   [topic]\n>      /              \\               \\\n>     A ---  ...   --- M ... --- U ... N [master]\n>                          \\\n>                           x --- y [side_branch wich needs to merge topic] \n\n?? I don't follow you. The side branch already contains the topic. What do\nyou want to merge?\n\n-- Hannes\n"},{"id":"137031","messageId":"4BA1010E.8030908@viscovery.net","threadId":"23038","inReplyTo":"4BA0FD5B.5090408@xiplink.com","subject":"Re: [PATCHv2] Teach --no-ff option to 'rebase -i'.","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2010-03-17T16:19:26Z","receivedAt":"2010-03-17T16:19:26Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Marc Branchaud schrieb:\n> Johannes Sixt wrote:\n>> If I were to re-merge topic into master a second time after this\n>> situation, I would install a temporary graft that removes the second\n>> parent of M and repeat the merge. After the graft is removed, the history\n>> would look like this:\n>>\n>>      B --- C --- D --------------.   [topic]\n>>    /              \\               \\\n>>   A ---  ...   --- M ... --- U ... N [master]\n>>\n>> Are there any downsides? I don't know - I haven't thought it through.\n> \n> I'm not sure I follow how to create that graft.\n\n  $ echo $(git rev-parse M M^) >> .git/info/grafts\n\n> But the original point (which I hadn't made clear) is that at least one of\n> the topic's commits needs to change in some substantial way.  So it's not\n> just a straight re-merge but a new take on the topic.\n> \n> Consider that if the topic's first commit (B) needed to be rewritten then the\n> repaired topic would contain only new commits and it could be merged into\n> master without reverting the first merge's reversion.\n\nYou don't need --ff nor --no-ff in this case.\n\n> What \"rebase -i --no-ff\" does is allow you to ensure that this will always be\n> the case, even if you don't actually need to change the topic's first commit.\n\nBut why do you base the reworked topic on A instead of U or later?\n\nOr why don't you just mark the first commit as r(eword) and just exit the\neditor; it would rewrite the commit and all subsequent ones will be\nrewritten as well. Never in my life would I have searched for a *option*\nthat achieves the goal. It is such a rare situation that we don't need an\noption, do we?\n\n-- Hannes\n"},{"id":"137042","messageId":"20100317175320.GA26124@progeny.tock","threadId":"23038","inReplyTo":"4BA0FBC7.3080305@xiplink.com","subject":"Re: [PATCHv2] Teach --no-ff option to 'rebase -i'.","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-03-17T17:53:20Z","receivedAt":"2010-03-17T17:53:20Z","isPatch":false,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Marc Branchaud wrote:\n\n> If the re-cast topic1 doesn't rewrite those commits, then the merge will\n> simply succeed because the code is already identical in both branches.\n> \n> But if topic1 does rewrite those commits then there'll be a conflict.  IMO\n> that's correct, because with the merging of topic2 the code in master really\n> did diverge in a relevant way from what's in topic1, so that conflict should\n> get resolved in the normal way.\n\nSorry, I did not think it through all the way.  Here is an example of\nwhat I meant by trouble.  Suppose you have re-cast topic using\nrebase --no-ff:\n\n   B' --- C' --- D' [topic]\n  /\n | B --- C --- D         F [topic2]\n |/             \\       /\n A ---  ...  --- M --- E ... --- U [master]\n\ntopic represents a new feature that was merged prematurely and then\nreverted, and topic2 represents some helpful new plumbing.\n(introduction of a few functions, maybe).\n\nTo take advantage of the changes from F, someone merges topic2 into\ntopic and builds on it:\n\n                    .. X --- Y [topic]\n                   /  /\n   B' --- C' --- D'  F [topic2]\n  /                 /\n A -- ... -- M --- E ... --- U [master]\n\nNow someone decides it is time to merge topic into master.  The\nmerge-base for Y and U is E, and the result is a that the changes from\ntopic are reverted.\n\nWhat I had missed: it would be just as dangerous to simply merge topic2\ndirectly.  Merging ‘master’ into ‘topic’ does nothing to prevent that.\n\nThe new advice: when using rebase --no-ff this way, be sure to rewrite\n_every_ branch that includes those commits but doesn’t include U.\n\nJonathan\n"},{"id":"137044","messageId":"4BA11B23.4090801@xiplink.com","threadId":"23038","inReplyTo":"4BA1010E.8030908@viscovery.net","subject":"Re: [PATCHv2] Teach --no-ff option to 'rebase -i'.","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2010-03-17T18:10:43Z","receivedAt":"2010-03-17T18:10:43Z","isPatch":false,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"Johannes Sixt wrote:\n> Marc Branchaud schrieb:\n>> Johannes Sixt wrote:\n>>> If I were to re-merge topic into master a second time after this\n>>> situation, I would install a temporary graft that removes the second\n>>> parent of M and repeat the merge. After the graft is removed, the history\n>>> would look like this:\n>>>\n>>>      B --- C --- D --------------.   [topic]\n>>>    /              \\               \\\n>>>   A ---  ...   --- M ... --- U ... N [master]\n>>>\n>>> Are there any downsides? I don't know - I haven't thought it through.\n>> I'm not sure I follow how to create that graft.\n> \n>   $ echo $(git rev-parse M M^) >> .git/info/grafts\n> \n>> But the original point (which I hadn't made clear) is that at least one of\n>> the topic's commits needs to change in some substantial way.  So it's not\n>> just a straight re-merge but a new take on the topic.\n>>\n>> Consider that if the topic's first commit (B) needed to be rewritten then the\n>> repaired topic would contain only new commits and it could be merged into\n>> master without reverting the first merge's reversion.\n> \n> You don't need --ff nor --no-ff in this case.\n> \n>> What \"rebase -i --no-ff\" does is allow you to ensure that this will always be\n>> the case, even if you don't actually need to change the topic's first commit.\n> \n> But why do you base the reworked topic on A instead of U or later?\n\nI want the topic based on A so that I can merge it into other branches that\nare also based on A.\n\n> Or why don't you just mark the first commit as r(eword) and just exit the\n> editor; it would rewrite the commit and all subsequent ones will be\n> rewritten as well.\n\nYes, that works just as well (at least until someone optimizes the reword\ncommand).\n\n> Never in my life would I have searched for a *option*\n> that achieves the goal. It is such a rare situation that we don't need an\n> option, do we?\n\nThat's a more fundamental question.  I don't feel strongly either way.  The\nmain advantage I see with having the option is that it codifies the process,\nwith documentation and a unit test to help make sure it doesn't break.  So if\nnobody wants the new option, I would then like to add a unit test to ensure\nthat rebase's reword command doesn't get optimized (if such a test doesn't\nalready exist), and maybe also add a note to the revert-a-faulty-merge howto.\n\n\t\tM.\n"},{"id":"137045","messageId":"20100317181347.GA26376@progeny.tock","threadId":"23038","inReplyTo":"20100317175320.GA26124@progeny.tock","subject":"Re: [PATCHv2] Teach --no-ff option to 'rebase -i'.","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-03-17T18:13:47Z","receivedAt":"2010-03-17T18:13:47Z","isPatch":false,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi again,\n\nSorry to keep talking to myself here...\n\nJonathan Nieder wrote:\n\n> To take advantage of the changes from F, someone merges topic2 into\n> topic and builds on it:\n> \n>                     .. X --- Y [topic]\n>                    /  /\n>    B' --- C' --- D'  F [topic2]\n>   /                 /\n>  A -- ... -- M --- E ... --- U [master]\n> \n> Now someone decides it is time to merge topic into master.  The\n> merge-base for Y and U is E, and the result is a that the changes from\n> topic are reverted.\n\nAlmost certainly bad.\n\n> What I had missed: it would be just as dangerous to simply merge topic2\n> directly.  Merging ‘master’ into ‘topic’ does nothing to prevent that.\n\nMight be bad, might not, depending on what topic2 is about.\n\n> The new advice: when using rebase --no-ff this way, be sure to rewrite\n> _every_ branch that includes those commits but doesn’t include U.\n\nOr rather: rewrite every branch that you want to make supersede U.\n\nSurprisingly tricky.\n\nI prefer the “revert the faulty revert” technique because it is more\ntransparent.  If it is only possible to re-merge now because of\ndevelopments that occured in topic, a simple revert would create a\nnon-bisectable history, but Johannes’s trick (or the uglier method I\ndescribed before) should work.\n\n git checkout master\n : >> .git/info/grafts\n cp .git/info/grafts .git/info/saved-grafts\n echo $(git rev-parse M M^) >> .git/info/grafts\n git merge topic;\t# being sure to mention M in the commit message!\n mv .git/info/saved-grafts .git/info/grafts\n\nJonathan\n"},{"id":"137048","messageId":"20100317184210.GB2557@m62s10.vlinux.de","threadId":"23038","inReplyTo":"4BA0FE59.7020303@viscovery.net","subject":"Re: [PATCHv2] Teach --no-ff option to 'rebase -i'.","fromName":"Peter Baumann","fromEmail":"waste.manager@gmx.de","sentAt":"2010-03-17T18:42:10Z","receivedAt":"2010-03-17T18:42:10Z","isPatch":false,"sender":{"key":"waste.manager@gmx.de","avatar":null},"body":"On Wed, Mar 17, 2010 at 05:07:53PM +0100, Johannes Sixt wrote:\n> Peter Baumann schrieb:\n> > On Wed, Mar 17, 2010 at 07:59:19AM +0100, Johannes Sixt wrote:\n> >> If I were to re-merge topic into master a second time after this\n> >> situation, I would install a temporary graft that removes the second\n> >> parent of M and repeat the merge. After the graft is removed, the history\n> >> would look like this:\n> >>\n> >>      B --- C --- D --------------.   [topic]\n> >>    /              \\               \\\n> >>   A ---  ...   --- M ... --- U ... N [master]\n> >>\n> >> Are there any downsides? I don't know - I haven't thought it through.\n> >>\n> > \n> > Might be. If there is any branch starting anywhere in between M and U\n> > which also needs to merge [topic] will also cause you headaches :-)\n> > \n> >        B --- C --- D --------------.   [topic]\n> >      /              \\               \\\n> >     A ---  ...   --- M ... --- U ... N [master]\n> >                          \\\n> >                           x --- y [side_branch wich needs to merge topic] \n> \n> ?? I don't follow you. The side branch already contains the topic. What do\n> you want to merge?\n> \n\nWon't it loose the revert 'U' after merging side_branch back to master?\n\nAh. Looking at the picture more closely, I could answer myself and say it would\nonly cause a huge mergeconflict, won't it?.\n\n--\nPeter\n"},{"id":"137073","messageId":"4BA1D183.3050907@viscovery.net","threadId":"23038","inReplyTo":"20100317184210.GB2557@m62s10.vlinux.de","subject":"Re: [PATCHv2] Teach --no-ff option to 'rebase -i'.","fromName":"Johannes Sixt","fromEmail":"j.sixt@viscovery.net","sentAt":"2010-03-18T07:08:51Z","receivedAt":"2010-03-18T07:08:51Z","isPatch":false,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Peter Baumann schrieb:\n> On Wed, Mar 17, 2010 at 05:07:53PM +0100, Johannes Sixt wrote:\n>> Peter Baumann schrieb:\n>>> On Wed, Mar 17, 2010 at 07:59:19AM +0100, Johannes Sixt wrote:\n>>>> If I were to re-merge topic into master a second time after this\n>>>> situation, I would install a temporary graft that removes the second\n>>>> parent of M and repeat the merge. After the graft is removed, the history\n>>>> would look like this:\n>>>>\n>>>>      B --- C --- D --------------.   [topic]\n>>>>    /              \\               \\\n>>>>   A ---  ...   --- M ... --- U ... N [master]\n>>>>\n>>>> Are there any downsides? I don't know - I haven't thought it through.\n>>>>\n>>> Might be. If there is any branch starting anywhere in between M and U\n>>> which also needs to merge [topic] will also cause you headaches :-)\n>>>\n>>>        B --- C --- D --------------.   [topic]\n>>>      /              \\               \\\n>>>     A ---  ...   --- M ... --- U ... N [master]\n>>>                          \\\n>>>                           x --- y [side_branch wich needs to merge topic] \n>> ?? I don't follow you. The side branch already contains the topic. What do\n>> you want to merge?\n>>\n> \n> Won't it loose the revert 'U' after merging side_branch back to master?\n> \n> Ah. Looking at the picture more closely, I could answer myself and say it would\n> only cause a huge mergeconflict, won't it?.\n\nNo. N and the merge-base of N and y are identical (wrt changes introduced\nby B,C,D). At least this part will not cause any conflicts.\n\n-- Hannes\n"},{"id":"137075","messageId":"20100318080331.GC2557@m62s10.vlinux.de","threadId":"23038","inReplyTo":"4BA1D183.3050907@viscovery.net","subject":"Re: [PATCHv2] Teach --no-ff option to 'rebase -i'.","fromName":"Peter Baumann","fromEmail":"waste.manager@gmx.de","sentAt":"2010-03-18T08:03:31Z","receivedAt":"2010-03-18T08:03:31Z","isPatch":false,"sender":{"key":"waste.manager@gmx.de","avatar":null},"body":"On Thu, Mar 18, 2010 at 08:08:51AM +0100, Johannes Sixt wrote:\n> Peter Baumann schrieb:\n> > On Wed, Mar 17, 2010 at 05:07:53PM +0100, Johannes Sixt wrote:\n> >> Peter Baumann schrieb:\n> >>> On Wed, Mar 17, 2010 at 07:59:19AM +0100, Johannes Sixt wrote:\n> >>>> If I were to re-merge topic into master a second time after this\n> >>>> situation, I would install a temporary graft that removes the second\n> >>>> parent of M and repeat the merge. After the graft is removed, the history\n> >>>> would look like this:\n> >>>>\n> >>>>      B --- C --- D --------------.   [topic]\n> >>>>    /              \\               \\\n> >>>>   A ---  ...   --- M ... --- U ... N [master]\n> >>>>\n> >>>> Are there any downsides? I don't know - I haven't thought it through.\n> >>>>\n> >>> Might be. If there is any branch starting anywhere in between M and U\n> >>> which also needs to merge [topic] will also cause you headaches :-)\n> >>>\n> >>>        B --- C --- D --------------.   [topic]\n> >>>      /              \\               \\\n> >>>     A ---  ...   --- M ... --- U ... N [master]\n> >>>                          \\\n> >>>                           x --- y [side_branch wich needs to merge topic] \n> >> ?? I don't follow you. The side branch already contains the topic. What do\n> >> you want to merge?\n> >>\n> > \n> > Won't it loose the revert 'U' after merging side_branch back to master?\n> > \n> > Ah. Looking at the picture more closely, I could answer myself and say it would\n> > only cause a huge mergeconflict, won't it?.\n> \n> No. N and the merge-base of N and y are identical (wrt changes introduced\n> by B,C,D). At least this part will not cause any conflicts.\n> \n\nYou are right. How could I missed that.\n\nThanks for the clarification.\n\n--\nPeter\n"},{"id":"137576","messageId":"1269285942-17496-1-git-send-email-marcnarc@xiplink.com","threadId":"23038","inReplyTo":"4BA11B23.4090801@xiplink.com","subject":"[PATCH] Test that the 'rebase -i' \"reword\" command always cherry-picks a commit.","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2010-03-22T19:25:42Z","receivedAt":"2010-03-22T19:25:42Z","isPatch":true,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"In particular, \"reword\" should cherry-pick a reworded commit even if the\ncommit's message is unchanged.\n\nThis behaviour provides a way to deal with a situation that can arise when\na merge had to be reverted.  Added an addendum to revert-a-faulty-merge.txt\ndescribing the situation and how to use \"reword\" to handle it.\n---\n\nIs this more acceptable than adding --no-ff to rebase--interactive?\n\nI wasn't sure how to integrate the new text into revert-a-faulty-merge.txt.\nI went with an addendum, but I'm open to other approaches.\n\n\t\tM.\n\n Documentation/howto/revert-a-faulty-merge.txt |   64 +++++++++++++++++++++++++\n t/t3417-rebase-reword-forces-cherry-pick.sh   |   59 +++++++++++++++++++++++\n 2 files changed, 123 insertions(+), 0 deletions(-)\n create mode 100755 t/t3417-rebase-reword-forces-cherry-pick.sh\n\ndiff --git a/Documentation/howto/revert-a-faulty-merge.txt b/Documentation/howto/revert-a-faulty-merge.txt\nindex 3b4a390..892433d 100644\n--- a/Documentation/howto/revert-a-faulty-merge.txt\n+++ b/Documentation/howto/revert-a-faulty-merge.txt\n@@ -142,6 +142,8 @@ different resolution strategies:\n    revert of a merge was rebuilt from scratch (i.e. rebasing and fixing,\n    as you seem to have interpreted), then re-merging the result without\n    doing anything else fancy would be the right thing to do.\n+   (See the ADDENDUM below for how to rebuild a branch from scratch\n+   without changing its original branching-off point.)\n \n However, there are things to keep in mind when reverting a merge (and\n reverting such a revert).\n@@ -177,3 +179,65 @@ the answer is: \"oops, I really shouldn't have merged it, because it wasn't\n ready yet, and I really need to undo _all_ of the merge\"). So then you\n really should revert the merge, but when you want to re-do the merge, you\n now need to do it by reverting the revert.\n+\n+ADDENDUM\n+\n+Sometimes you're in a situation like this\n+\n+ P---o---o---M---x---x---W---x\n+  \\         /\n+   A---B---C\n+\n+where you:\n+\n+ - Need to rewrite one of the commits on the A-B-C branch; and\n+\n+ - Want the rewritten A-B-C branch to still start at commit P (perhaps P\n+   is a branching-off point for yet another branch, and you want be able to\n+   merge A-B-C into both branches).\n+\n+The natural thing to do in this case is to checkout the A-B-C branch and use\n+\"rebase -i A\" to change commit B.  However, this does not rewrite commit A,\n+and you end up with this:\n+\n+ P---o---o---M---x---x---W---x\n+  \\         /\n+   A---B---C   <-- old branch\n+   \\\n+    B'---C'    <-- rewritten branch\n+\n+To merge A-B'-C' into the mainline branch you would still have to first revert\n+commit W in order to pick up the changes in A, but then it's likely that the\n+changes in B' will conflict with the original B changes re-introduced by the\n+reversion of W.\n+\n+However, you could avoid these problems if you recreated the entire branch,\n+including commit A:\n+\n+ P---o---o---M---x---x---W---x\n+ |\\         /\n+ | A---B---C   <-- old branch\n+ \\\n+  A'---B'---C' <-- entirely recreated branch\n+\n+Now you can merge A'-B'-C' into the mainline branch without worrying about\n+first reverting W.\n+\n+But if you don't actually need to change commit A, then you need some way to\n+recreate it as a new commit with the same changes in it.  One way to do this\n+is to use the \"reword\" command in \"rebase -i\".  If you:\n+\n+ - Checkout the A-B-C branch\n+\n+ - Run \"rebase -i P\" to rebase the entire branch\n+\n+ - \"reword\" commit A (the first commit in the rebase script)\n+\n+ - Make NO changes to commit A's message\n+\n+then you'll end up with a new branch A'-B'-C' with all-new commits (all the\n+SHA IDs will be different) but that has the same changes that were in the\n+original A-B-C branch.\n+\n+You can then modify commit B' as you like and merge the new A'-B''-C' branch\n+into the mainline branch without first reverting commit W.\ndiff --git a/t/t3417-rebase-reword-forces-cherry-pick.sh b/t/t3417-rebase-reword-forces-cherry-pick.sh\nnew file mode 100755\nindex 0000000..3e29f0f\n--- /dev/null\n+++ b/t/t3417-rebase-reword-forces-cherry-pick.sh\n@@ -0,0 +1,59 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2010 Marc Branchaud\n+#\n+\n+test_description='git rebase interactive \"reword\" always forces cherry-pick\n+\n+This test ensures that the \"reword\" command of \"git rebase -i\" cherry-picks\n+a reworded commit even if no changes are made to the commit message.\n+\n+This behaviour provides a way to deal with a situation that can arise when a\n+merge had to be reverted.  For details, see the ADDENDUM in\n+Documentation/howto/revert-a-faulty-merge.txt.\n+'\n+\n+. ./test-lib.sh\n+\n+. \"$TEST_DIRECTORY\"/lib-rebase.sh\n+\n+set_fake_editor\n+\n+# A simple linear history.\n+test_expect_success 'setup' '\n+\ttest_commit A file1 &&\n+\ttest_commit B file1 &&\n+\ttest_commit C file1 &&\n+\ttest_commit D file1 &&\n+\tgit branch old-master HEAD\n+'\n+\n+# Ensure that the rebased commits get a different timestamp.\n+test_tick\n+\n+# Reword the first commit, but don't actually change the message\n+test_expect_success 'reword' '\n+\tFAKE_LINES=\"reword 1 2 3\" git rebase -i A\n+'\n+\n+# There should be no textual differences between the old and new branches.\n+# However:\n+#  - Commit A should be completely unchanged.\n+#  - Commits B, C and D should have different SHA IDs.\n+test_expect_success 'verify' '\n+\ttouch empty &&\n+\ttest ! $(git rev-parse ) = $(git rev-parse master) &&\n+\tgit diff old-master master > out &&\n+\ttest_cmp empty out &&\n+\ttest ! $(git rev-parse C) = $(git rev-parse master^) &&\n+\tgit diff old-master^ master^ > out &&\n+\ttest_cmp empty out &&\n+\ttest ! $(git rev-parse B) = $(git rev-parse master^^) &&\n+\tgit diff old-master^^ master^^ > out &&\n+\ttest_cmp empty out &&\n+\ttest $(git rev-parse A) = $(git rev-parse master^^^) &&\n+\tgit diff old-master^^^ master^^^ > out &&\n+\ttest_cmp empty out\n+'\n+\n+test_done\n-- \n1.7.0.3\n"},{"id":"137584","messageId":"32541b131003221323u1ed540bbi87d8d427cfcc421a@mail.gmail.com","threadId":"23038","inReplyTo":"1269285942-17496-1-git-send-email-marcnarc@xiplink.com","subject":"Re: [PATCH] Test that the 'rebase -i' \"reword\" command always cherry-picks a commit.","fromName":"Avery Pennarun","fromEmail":"apenwarr@gmail.com","sentAt":"2010-03-22T20:23:19Z","receivedAt":"2010-03-22T20:23:19Z","isPatch":true,"sender":{"key":"apenwarr@gmail.com","avatar":"https://avatars.githubusercontent.com/u/20592?v=4"},"body":"On Mon, Mar 22, 2010 at 3:25 PM, Marc Branchaud <marcnarc@xiplink.com> wrote:\n> +Sometimes you're in a situation like this\n> +\n> + P---o---o---M---x---x---W---x\n> +  \\         /\n> +   A---B---C\n> +\n> +where you:\n> +\n> + - Need to rewrite one of the commits on the A-B-C branch; and\n> +\n> + - Want the rewritten A-B-C branch to still start at commit P (perhaps P\n> +   is a branching-off point for yet another branch, and you want be able to\n> +   merge A-B-C into both branches).\n> +\n> +The natural thing to do in this case is to checkout the A-B-C branch and use\n> +\"rebase -i A\" to change commit B.  However, this does not rewrite commit A,\n> +and you end up with this:\n> +\n> + P---o---o---M---x---x---W---x\n> +  \\         /\n> +   A---B---C   <-- old branch\n> +   \\\n> +    B'---C'    <-- rewritten branch\n> +\n> +To merge A-B'-C' into the mainline branch you would still have to first revert\n> +commit W in order to pick up the changes in A, but then it's likely that the\n> +changes in B' will conflict with the original B changes re-introduced by the\n> +reversion of W.\n\nI think you need to clarify in the above text that W is a revert of M.\n I was very confused by this at first.\n\nOther than that, I'll leave it to others more opinionated than me to\ncomment on whether regenerating a commit just for the sake of\nregenerating it is actually desirable or not :)\n\nHave fun,\n\nAvery\n"},{"id":"137587","messageId":"7vd3yw84pd.fsf@alter.siamese.dyndns.org","threadId":"23038","inReplyTo":"1269285942-17496-1-git-send-email-marcnarc@xiplink.com","subject":"Re: [PATCH] Test that the 'rebase -i' \"reword\" command always cherry-picks a commit.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-03-22T20:46:38Z","receivedAt":"2010-03-22T20:46:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Marc Branchaud <marcnarc@xiplink.com> writes:\n\n> In particular, \"reword\" should cherry-pick a reworded commit even if the\n> commit's message is unchanged.\n>\n> This behaviour provides a way to deal with a situation that can arise when\n> a merge had to be reverted.  Added an addendum to revert-a-faulty-merge.txt\n> describing the situation and how to use \"reword\" to handle it.\n> ---\n>\n> Is this more acceptable than adding --no-ff to rebase--interactive?\n>\n> I wasn't sure how to integrate the new text into revert-a-faulty-merge.txt.\n> I went with an addendum, but I'm open to other approaches.\n\nThe addendum looked readable, but I am a bit puzzled.  \"rebase -i --no-ff\"\nalready exists, and is probably a more natural way to do this than saying\n\"reword\" but not rewording anything, no?\n\nI would actually say \"rebase -f P\" would be even easier and clearer,\nespecially as...\n\n> ...\n> +However, you could avoid these problems if you recreated the entire branch,\n> +including commit A:\n> +\n> + P---o---o---M---x---x---W---x\n> + |\\         /\n> + | A---B---C   <-- old branch\n> + \\\n> +  A'---B'---C' <-- entirely recreated branch\n> +\n> +Now you can merge A'-B'-C' into the mainline branch without worrying about\n> +first reverting W.\n> +\n> +But if you don't actually need to change commit A, then you need some way to\n> +recreate it as a new commit with the same changes in it.\n\n... this part seems to talk about working around the tendency of 'rebase -i'\nto fast-forward.\n"},{"id":"137588","messageId":"4BA7E9F5.2000404@xiplink.com","threadId":"23038","inReplyTo":"32541b131003221323u1ed540bbi87d8d427cfcc421a@mail.gmail.com","subject":"Re: [PATCH] Test that the 'rebase -i' \"reword\" command always cherry-picks a commit.","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2010-03-22T22:06:45Z","receivedAt":"2010-03-22T22:06:45Z","isPatch":true,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"Avery Pennarun wrote:\n> On Mon, Mar 22, 2010 at 3:25 PM, Marc Branchaud <marcnarc@xiplink.com> wrote:\n>> +Sometimes you're in a situation like this\n>> +\n>> + P---o---o---M---x---x---W---x\n>> +  \\         /\n>> +   A---B---C\n>> +\n>> +where you:\n>> +\n>> + - Need to rewrite one of the commits on the A-B-C branch; and\n>> +\n>> + - Want the rewritten A-B-C branch to still start at commit P (perhaps P\n>> +   is a branching-off point for yet another branch, and you want be able to\n>> +   merge A-B-C into both branches).\n>> +\n>> +The natural thing to do in this case is to checkout the A-B-C branch and use\n>> +\"rebase -i A\" to change commit B.  However, this does not rewrite commit A,\n>> +and you end up with this:\n>> +\n>> + P---o---o---M---x---x---W---x\n>> +  \\         /\n>> +   A---B---C   <-- old branch\n>> +   \\\n>> +    B'---C'    <-- rewritten branch\n>> +\n>> +To merge A-B'-C' into the mainline branch you would still have to first revert\n>> +commit W in order to pick up the changes in A, but then it's likely that the\n>> +changes in B' will conflict with the original B changes re-introduced by the\n>> +reversion of W.\n> \n> I think you need to clarify in the above text that W is a revert of M.\n>  I was very confused by this at first.\n\nSomeone who reads through the whole file will see that W is the reversion of\nM.  It's probably good to repeat that in the addendum for readers who jump to\nthe addendum right away.\n\n> Other than that, I'll leave it to others more opinionated than me to\n> comment on whether regenerating a commit just for the sake of\n> regenerating it is actually desirable or not :)\n\nI'm all ears!\n\n\t\tM.\n"},{"id":"137589","messageId":"20100322220929.GA1384@progeny.tock","threadId":"23038","inReplyTo":"1269285942-17496-1-git-send-email-marcnarc@xiplink.com","subject":"Re: [PATCH] Test that the 'rebase -i' \"reword\" command always cherry-picks a commit.","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-03-22T22:09:29Z","receivedAt":"2010-03-22T22:09:29Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Marc Branchaud wrote:\n\n> Is this more acceptable than adding --no-ff to rebase--interactive?\n\nNo, in my opinion the latter is better, and I suspect it will go in. :)\n\nNo one has objected to the --no-ff option.  I like it: if someone is\ntrying to reset the committer date on a collection of patches, I’d\nrather they use rebase -f or rebase -i --no-ff than format-patch and am.\nIt’s cooking in pu for the moment, and from here the natural progression\nis to next, master, then maybe maint.\n\nHope that helps,\nJonathan\n"},{"id":"137641","messageId":"4BA8D263.8040602@xiplink.com","threadId":"23038","inReplyTo":"7vd3yw84pd.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH] Test that the 'rebase -i' \"reword\" command always cherry-picks a commit.","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2010-03-23T14:38:27Z","receivedAt":"2010-03-23T14:38:27Z","isPatch":true,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"Junio C Hamano wrote:\n> Marc Branchaud <marcnarc@xiplink.com> writes:\n> \n>> In particular, \"reword\" should cherry-pick a reworded commit even if the\n>> commit's message is unchanged.\n>>\n>> This behaviour provides a way to deal with a situation that can arise when\n>> a merge had to be reverted.  Added an addendum to revert-a-faulty-merge.txt\n>> describing the situation and how to use \"reword\" to handle it.\n>> ---\n>>\n>> Is this more acceptable than adding --no-ff to rebase--interactive?\n>>\n>> I wasn't sure how to integrate the new text into revert-a-faulty-merge.txt.\n>> I went with an addendum, but I'm open to other approaches.\n> \n> The addendum looked readable, but I am a bit puzzled.\n\nYa think _you're_ puzzled?  :)\n\n> \"rebase -i --no-ff\"\n> already exists, and is probably a more natural way to do this than saying\n> \"reword\" but not rewording anything, no?\n> \n> I would actually say \"rebase -f P\" would be even easier and clearer,\n> especially as...\n> \n>> ...\n>> +However, you could avoid these problems if you recreated the entire branch,\n>> +including commit A:\n>> +\n>> + P---o---o---M---x---x---W---x\n>> + |\\         /\n>> + | A---B---C   <-- old branch\n>> + \\\n>> +  A'---B'---C' <-- entirely recreated branch\n>> +\n>> +Now you can merge A'-B'-C' into the mainline branch without worrying about\n>> +first reverting W.\n>> +\n>> +But if you don't actually need to change commit A, then you need some way to\n>> +recreate it as a new commit with the same changes in it.\n> \n> ... this part seems to talk about working around the tendency of 'rebase -i'\n> to fast-forward.\n\nYes.  Thanks for pointing this out, it's cleared up a lot for me.\n\nI was confused about the purpose of \"rebase -f\".  Jonathan Nieder even\npointed me to it when I posted my original patch for \"rebase -i --no-ff\", but\nthe description in the man page threw me:\n\n\tForce the rebase even if the current branch is a descendant of\n\tthe commit you are rebasing onto. Normally the command will\n\texit with the message \"Current branch is up to date\" in such a\n\tsituation.\n\nI didn't realize that this is exactly the situation that \"rebase -i\" normally\ndeals with (-i basically implies -f), and that \"rebase -f\" would do exactly\nwhat I wanted \"rebase -i --no-ff\" to do.\n\nBut I think I see an approach that might make sense:\n\n - Teach \"rebase -i\" to recognize the -f parameter (instead of --no-ff).\n\n - Update rebase's man page to better explain -f.\n\n - Update revert-a-faulty-merge.txt to explain how to use \"rebase [-i] -f\".\n\nI'll submit a new patch shortly.\n\n\t\tM.\n"},{"id":"137647","messageId":"1269361187-31291-1-git-send-email-marcnarc@xiplink.com","threadId":"23038","inReplyTo":"4BA8D263.8040602@xiplink.com","subject":"[PATCHv3] Teach -f/--force-rebase option to 'rebase -i'.","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2010-03-23T16:19:47Z","receivedAt":"2010-03-23T16:19:47Z","isPatch":false,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"This option tells rebase--interactive to cherry-pick all the commits in the\nrebased branch, instead of fast-forwarding over any unchanged commits.\n\nAlso expanded -f's description in rebase's man page.\n\n-f offers an alterntive way to deal with reverted merges.  Instead of\n\"reverting the revert\" you can use \"rebase -f [-i]\" to recreate the branch\nwith entirely new commits (they're new because at the very least the\ncommitter time is different).  This obviates the need to revert the\nreversion, as you can re-merge the new topic branch directly.  Added an\naddendum to revert-a-faulty-merge.txt describing the situation and how to\nuse -f to handle it.\n\nSigned-off-by: Marc Branchaud <marcnarc@xiplink.com>\n---\n\nIt seems more natural to me to just teach -f to \"rebase -i\" instead of adding\na new --no-ff option.\n\nThis patch combines my initial \"--no-ff\" patch with the documentation changes\nin my \"reword\" patch.  I moved the unit test for \"rebase -f -i\" into\nt3404-rebase-interactive.sh.\n\n\t\tM.\n\n\n Documentation/git-rebase.txt                  |   10 ++++-\n Documentation/howto/revert-a-faulty-merge.txt |   57 +++++++++++++++++++++++++\n git-rebase--interactive.sh                    |    9 +++-\n t/t3404-rebase-interactive.sh                 |   36 ++++++++++++++--\n 4 files changed, 104 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 823f2a4..1fd8d1c 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -274,9 +274,15 @@ which makes little sense.\n -f::\n --force-rebase::\n \tForce the rebase even if the current branch is a descendant\n-\tof the commit you are rebasing onto.  Normally the command will\n+\tof the commit you are rebasing onto.  Normally non-interactive rebase will\n \texit with the message \"Current branch is up to date\" in such a\n-\tsituation.\n+\tsituation.  With --interactive, this option forces rebase to\n+\tcherry-pick all commits, even if they're not actually changed.\n++\n+--force-rebase can sometimes be helpful after reverting a topic branch merge,\n+as it recreates the topic branch with fresh commits so it can be remerged\n+successfully without needing to \"revert the reversion\" (see the\n+link:howto/revert-a-faulty-merge.txt[revert-a-faulty-merge How-To]).\n \n --ignore-whitespace::\n --whitespace=<option>::\ndiff --git a/Documentation/howto/revert-a-faulty-merge.txt b/Documentation/howto/revert-a-faulty-merge.txt\nindex 3b4a390..994bd7f 100644\n--- a/Documentation/howto/revert-a-faulty-merge.txt\n+++ b/Documentation/howto/revert-a-faulty-merge.txt\n@@ -142,6 +142,8 @@ different resolution strategies:\n    revert of a merge was rebuilt from scratch (i.e. rebasing and fixing,\n    as you seem to have interpreted), then re-merging the result without\n    doing anything else fancy would be the right thing to do.\n+   (See the ADDENDUM below for how to rebuild a branch from scratch\n+   without changing its original branching-off point.)\n \n However, there are things to keep in mind when reverting a merge (and\n reverting such a revert).\n@@ -177,3 +179,58 @@ the answer is: \"oops, I really shouldn't have merged it, because it wasn't\n ready yet, and I really need to undo _all_ of the merge\"). So then you\n really should revert the merge, but when you want to re-do the merge, you\n now need to do it by reverting the revert.\n+\n+ADDENDUM\n+\n+Sometimes you're in a situation like this\n+\n+ P---o---o---M---x---x---W---x\n+  \\         /\n+   A---B---C\n+\n+where W is the reversion of merge M and you:\n+\n+ - Need to rewrite one of the commits on the A-B-C branch; and\n+\n+ - Want the rewritten A-B-C branch to still start at commit P (perhaps P\n+   is a branching-off point for yet another branch, and you want be able to\n+   merge A-B-C into both branches).\n+\n+The natural thing to do in this case is to checkout the A-B-C branch and use\n+\"rebase -i A\" to change commit B.  However, this does not rewrite commit A,\n+and you end up with this:\n+\n+ P---o---o---M---x---x---W---x\n+  \\         /\n+   A---B---C   <-- old branch\n+   \\\n+    B'---C'    <-- rewritten branch\n+\n+To merge A-B'-C' into the mainline branch you would still have to first revert\n+commit W in order to pick up the changes in A, but then it's likely that the\n+changes in B' will conflict with the original B changes re-introduced by the\n+reversion of W.\n+\n+However, you can avoid these problems if you recreate the entire branch,\n+including commit A:\n+\n+ P---o---o---M---x---x---W---x\n+ |\\         /\n+ | A---B---C   <-- old branch\n+ \\\n+  A'---B'---C' <-- entirely recreated branch\n+\n+Now you can merge A'-B'-C' into the mainline branch without worrying about\n+first reverting W.\n+\n+But if you don't actually need to change commit A, then you need some way to\n+recreate it as a new commit with the same changes in it.  One way to do this\n+is to rebase the entire branch from the branching-off point (commit P) using\n+the -f parameter to ensure that all the commits get recreated:\n+\n+    $ git rebase -f -i P\n+\n+This creates a new branch A'-B'-C' with all-new commits (all the SHA IDs will\n+be different) even if you only actually modify commit B.  You can then merge\n+this new branch directly into the mainline branch and be sure you'll get all\n+of the branch's changes.\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex 3e4fd14..61ccf4c 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -20,6 +20,7 @@ v,verbose          display a diffstat of what changed upstream\n onto=              rebase onto given branch instead of upstream\n p,preserve-merges  try to recreate merges instead of ignoring them\n s,strategy=        use the given merge strategy\n+f,force-rebase     cherry-pick all commits, even if unchanged\n m,merge            always used (no-op)\n i,interactive      always used (no-op)\n  Actions:\n@@ -103,6 +104,7 @@ VERBOSE=\n OK_TO_SKIP_PRE_REBASE=\n REBASE_ROOT=\n AUTOSQUASH=\n+FORCE=\n \n GIT_CHERRY_PICK_HELP=\"  After resolving the conflicts,\n mark the corrected paths with 'git add <paths>', and\n@@ -222,7 +224,7 @@ do_with_author () {\n }\n \n pick_one () {\n-\tno_ff=\n+\tno_ff=$FORCE\n \tcase \"$1\" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac\n \toutput git rev-parse --verify $sha1 || die \"Invalid commit name: $sha1\"\n \ttest -d \"$REWRITTEN\" &&\n@@ -742,6 +744,9 @@ first and then run 'git rebase --continue' again.\"\n \t-i)\n \t\t# yeah, we know\n \t\t;;\n+\t-f)\n+\t\tFORCE=t\n+\t\t;;\n \t--root)\n \t\tREBASE_ROOT=t\n \t\t;;\n@@ -927,7 +932,7 @@ EOF\n \t\thas_action \"$TODO\" ||\n \t\t\tdie_abort \"Nothing to do\"\n \n-\t\ttest -d \"$REWRITTEN\" || skip_unnecessary_picks\n+\t\ttest -d \"$REWRITTEN\" || test -n \"$FORCE\" || skip_unnecessary_picks\n \n \t\tgit update-ref ORIG_HEAD $HEAD\n \t\toutput git checkout $ONTO && do_rest\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 4e35137..a169470 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -22,12 +22,18 @@ set_fake_editor\n # | \\\n # |   F - G - H                (branch1)\n # |     \\\n-#  \\      I                    (branch2)\n-#   \\\n-#     J - K - L - M            (no-conflict-branch)\n+# |\\      I                    (branch2)\n+# | \\\n+# |   J - K - L - M            (no-conflict-branch)\n+#  \\\n+#    N - O - P                 (force-branch)\n #\n # where A, B, D and G all touch file1, and one, two, three, four all\n # touch file \"conflict\".\n+#\n+# WARNING: Modifications to the initial repository can change the SHA ID used\n+# in the expect2 file for the 'stop on conflicting pick' test.\n+\n \n test_expect_success 'setup' '\n \ttest_commit A file1 &&\n@@ -50,6 +56,11 @@ test_expect_success 'setup' '\n \tfor n in J K L M\n \tdo\n \t\ttest_commit $n file$n\n+\tdone &&\n+\tgit checkout -b force-branch A &&\n+\tfor n in N O P\n+\tdo\n+\t\ttest_commit $n file$n\n \tdone\n '\n \n@@ -113,7 +124,7 @@ cat > expect2 << EOF\n D\n =======\n G\n->>>>>>> 51047de... G\n+>>>>>>> 5d18e54... G\n EOF\n \n test_expect_success 'stop on conflicting pick' '\n@@ -553,4 +564,21 @@ test_expect_success 'reword' '\n \tgit show HEAD~2 | grep \"C changed\"\n '\n \n+test_tick # Ensure that the rebased commits get a different timestamp.\n+test_expect_success 'always cherry-pick with -f' '\n+\tgit checkout force-branch &&\n+\tgit tag original-force-branch &&\n+\tgit rebase -i -f A &&\n+\ttouch empty &&\n+\tfor p in 0 1 2\n+\tdo\n+\t\ttest ! $(git rev-parse HEAD~$p) = $(git rev-parse original-force-branch~$p) &&\n+\t\tgit diff HEAD~$p original-force-branch~$p > out &&\n+\t\ttest_cmp empty out\n+\tdone &&\n+\ttest $(git rev-parse HEAD~3) = $(git rev-parse original-force-branch~3) &&\n+\tgit diff HEAD~3 original-force-branch~3 > out &&\n+\ttest_cmp empty out\n+'\n+\n test_done\n-- \n1.7.0.3.dirty\n"},{"id":"137662","messageId":"20100323191603.GB1382@progeny.tock","threadId":"23038","inReplyTo":"4BA8D263.8040602@xiplink.com","subject":"Re: [PATCH] Test that the 'rebase -i' \"reword\" command always cherry-picks a commit.","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2010-03-23T19:16:03Z","receivedAt":"2010-03-23T19:16:03Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Marc Branchaud wrote:\n> Junio C Hamano wrote:\n\n>> \"rebase -i --no-ff\"\n>> already exists, and is probably a more natural way to do this than saying\n>> \"reword\" but not rewording anything, no?\n>> \n>> I would actually say \"rebase -f P\" would be even easier and clearer,\n>> especially as...\n[...]\n> I was confused about the purpose of \"rebase -f\".  Jonathan Nieder even\n> pointed me to it when I posted my original patch for \"rebase -i --no-ff\", but\n> the description in the man page threw me:\n>\n> \tForce the rebase even if the current branch is a descendant of\n> \tthe commit you are rebasing onto. Normally the command will\n> \texit with the message \"Current branch is up to date\" in such a\n> \tsituation.\n>\n> I didn't realize that this is exactly the situation that \"rebase -i\" normally\n> deals with (-i basically implies -f), and that \"rebase -f\" would do exactly\n> what I wanted \"rebase -i --no-ff\" to do.\n\nYes, sorry for not following up on that comment.  The point is that\nrebase without --interactive never fast-forwards.  So if you do not need\nto edit your branch interactively, it should work for your purpose.\n\n> But I think I see an approach that might make sense:\n> \n>  - Teach \"rebase -i\" to recognize the -f parameter (instead of --no-ff).\n\nI like --no-ff better. :)\n\n>  - Update rebase's man page to better explain -f.\n\nWhile at it, I should mention another documentation problem: rebase’s\nman page does not explain that rebase --interactive will fast-forward at\nthe beginning of the series.  It should say that rebase -i will do that\nand rebase without -i will not.  I can prepare a patch to fix it this\nevening.\n\n>  - Update revert-a-faulty-merge.txt to explain how to use \"rebase [-i] -f\".\n\nSounds interesting.  Simpler to use \"rebase (-f | -i --no-ff)\", I think.\nOr maybe rebase could learn a no-op --no-ff option to make it easier to\nswitch between it and rebase -i, and this would become\n\"rebase (-f | -i) --no-ff\".\n\nJonathan\n"},{"id":"137675","messageId":"7vzl1yd5j4.fsf@alter.siamese.dyndns.org","threadId":"23038","inReplyTo":"1269361187-31291-1-git-send-email-marcnarc@xiplink.com","subject":"Re: [PATCHv3] Teach -f/--force-rebase option to 'rebase -i'.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-03-23T22:42:07Z","receivedAt":"2010-03-23T22:42:07Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Marc Branchaud <marcnarc@xiplink.com> writes:\n\n> This option tells rebase--interactive to cherry-pick all the commits in the\n> rebased branch, instead of fast-forwarding over any unchanged commits.\n>\n> Also expanded -f's description in rebase's man page.\n>\n> -f offers an alterntive way to deal with reverted merges.  Instead of\n> \"reverting the revert\" you can use \"rebase -f [-i]\" to recreate the branch\n> with entirely new commits (they're new because at the very least the\n> committer time is different).  This obviates the need to revert the\n> reversion, as you can re-merge the new topic branch directly.  Added an\n> addendum to revert-a-faulty-merge.txt describing the situation and how to\n> use -f to handle it.\n>\n> Signed-off-by: Marc Branchaud <marcnarc@xiplink.com>\n> ---\n>\n> It seems more natural to me to just teach -f to \"rebase -i\" instead of adding\n> a new --no-ff option.\n>\n> This patch combines my initial \"--no-ff\" patch with the documentation changes\n> in my \"reword\" patch.  I moved the unit test for \"rebase -f -i\" into\n> t3404-rebase-interactive.sh.\n\nHmm, to be honest, this is not what I expected.\n\nA non-interactive \"rebase\" will _refuse_ to do anything, because it knows\nthat the end result will be exactly the same, in a fast-forward situation.\nIn general, the option \"--force\" is \"I know the precondition I am in will\ntrigger your logic to refuse this request---but please do so anyway\", and\nthe use of that option in non-interactive one is in line with that\ndefinition.  So \"--force\" makes perfect sense to non-interactive rebase.\n\nBut \"rebase -i\" cannot _refuse_ when the range to be rebased is already\nfast-forward, as the whole point of the command is to be able to rewrite\nchosen commits, and that \"rewriting chosen commits\" is the reason why it\nmakes sense for the command to fast-forward the earlier picks that were\nnot chosen to be rewritten.  But \"forcing\" doesn't make sense, if the\ncommand does not even refuse to begin with.  So \"--no-ff\" is sensible\nthere.\n\nWhat I meant was that as a measure to prepare for a future merge of\n(corrected) side branch whose earlier merge was once reverted, it would\nmake sense to use \"rebase -f\" without any \"interactive\", compared to the\n\"start rebase -i, and to ensure it wouldn't fast-forward, lie to it that\nyou will reword the first one and do nothing in reality\", which your\nearlier documentation suggested.\n\nIf anything, teaching --no-ff to non-interactive rebase would also make\nsense.  Just like saying \"I know you will refuse if it is a fast forward,\nbut please do so anyway\" with --force makes sense, it is sensible to say\n\"I know you will make this a no-op if it is a fast forward, but please\nrewrite all of them\" with --no-ff.\n"},{"id":"137707","messageId":"1269445261-2941-1-git-send-email-marcnarc@xiplink.com","threadId":"23038","inReplyTo":"7vzl1yd5j4.fsf@alter.siamese.dyndns.org","subject":"[PATCHv4 0/2] Teach the --no-ff option to 'rebase -i'.","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2010-03-24T15:40:59Z","receivedAt":"2010-03-24T15:40:59Z","isPatch":false,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"Thanks to both Junio and Jonathan for your patience in working through this.\n\nJunio, I think I see what you mean.  I like teaching --no-ff to 'rebase -i'\nbecause it allows me to combine two commands into one for this situation.\n\nI've split this work into two patches:\n\nThe first one simply teaches \"rebase -i\" to accept and ignore -f.  I feel\nthis is better than adding text to the man page explaining why interactive\nrebase has --no-ff but not -f, while non-interactive has the opposite.\n\nThe second is a re-roll of the --no-ff patch.  The only significant\ndifferences are in the documentation:\n\n - The rebase man page mentions reverting a merge under both the -f and\n   --no-ff options.\n\n - Rewrote the last 3 paragraphs of the revert-a-faulty-merge.txt howto\n   (starting at \"But if you don't ...\").\n\nI'm wondering now if it would make sense to also teach non-interactive\nrebase to accept --no-ff as a synonym for -f.  Thoughts?\n\n\t\tM.\n\n Documentation/git-rebase.txt                  |   23 +++++++++-\n Documentation/howto/revert-a-faulty-merge.txt |   61 +++++++++++++++++++++++++\n git-rebase--interactive.sh                    |   13 +++++-\n t/t3404-rebase-interactive.sh                 |   36 +++++++++++++--\n 4 files changed, 125 insertions(+), 8 deletions(-)\n"},{"id":"137705","messageId":"1269445261-2941-2-git-send-email-marcnarc@xiplink.com","threadId":"23038","inReplyTo":"7vzl1yd5j4.fsf@alter.siamese.dyndns.org","subject":"[PATCH 1/2] Teach 'rebase -i' to accept and ignore the -f/--force-rebase option.","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2010-03-24T15:41:00Z","receivedAt":"2010-03-24T15:41:00Z","isPatch":true,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"An interactive rebase implies -f:  The user wants to rewrite some of the\nbranch's own commits.\n---\n git-rebase--interactive.sh |    4 ++++\n 1 files changed, 4 insertions(+), 0 deletions(-)\n\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex 3e4fd14..aa0ab1a 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -22,6 +22,7 @@ p,preserve-merges  try to recreate merges instead of ignoring them\n s,strategy=        use the given merge strategy\n m,merge            always used (no-op)\n i,interactive      always used (no-op)\n+f,force-rebase     always used (no-op)\n  Actions:\n continue           continue rebasing process\n abort              abort rebasing process and restore original branch\n@@ -733,6 +734,9 @@ first and then run 'git rebase --continue' again.\"\n \t-m)\n \t\t# we use merge anyway\n \t\t;;\n+\t-f)\n+\t\t# we don't need to be forced\n+\t\t;;\n \t-v)\n \t\tVERBOSE=t\n \t\t;;\n-- \n1.7.0.3.1.g59254.dirty\n"},{"id":"137706","messageId":"1269445261-2941-3-git-send-email-marcnarc@xiplink.com","threadId":"23038","inReplyTo":"7vzl1yd5j4.fsf@alter.siamese.dyndns.org","subject":"[PATCH 2/2] Teach the --no-ff option to 'rebase -i'.","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2010-03-24T15:41:01Z","receivedAt":"2010-03-24T15:41:01Z","isPatch":true,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"This option tells git-rebase--interactive to cherry-pick all the commits in\nthe rebased branch, instead of fast-forwarding over any unchanged commits.\n\n--no-ff offers an alterntive way to deal with reverted merges.  Instead of\n\"reverting the revert\" you can use \"rebase -i --no-ff\" to recreate the\nbranch with entirely new commits (they're new because at the very least the\ncommitter time is different).  This obviates the need to revert the\nreversion, as you can re-merge the new topic branch directly.  Added an\naddendum to revert-a-faulty-merge.txt describing the situation and how to\nuse --no-ff to handle it.\n\n--force-rebase also provides this alternative with non-interactive rebase,\nso updated rebase's man page to mention it.\n\nSigned-off-by: Marc Branchaud <marcnarc@xiplink.com>\n---\n Documentation/git-rebase.txt                  |   23 +++++++++-\n Documentation/howto/revert-a-faulty-merge.txt |   61 +++++++++++++++++++++++++\n git-rebase--interactive.sh                    |    9 +++-\n t/t3404-rebase-interactive.sh                 |   36 +++++++++++++--\n 4 files changed, 121 insertions(+), 8 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 823f2a4..ca06c55 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -274,9 +274,15 @@ which makes little sense.\n -f::\n --force-rebase::\n \tForce the rebase even if the current branch is a descendant\n-\tof the commit you are rebasing onto.  Normally the command will\n+\tof the commit you are rebasing onto.  Normally non-interactive rebase will\n \texit with the message \"Current branch is up to date\" in such a\n \tsituation.\n++\n+You may find this (or --no-ff with an interactive rebase) helpful after\n+reverting a topic branch merge, as this option recreates the topic branch with\n+fresh commits so it can be remerged successfully without needing to \"reverting\n+the reversion\" (as described in the\n+link:howto/revert-a-faulty-merge.txt[revert-a-faulty-merge How-To]).\n \n --ignore-whitespace::\n --whitespace=<option>::\n@@ -316,7 +322,20 @@ which makes little sense.\n \tcommit to be modified, and change the action of the moved\n \tcommit from `pick` to `squash` (or `fixup`).\n +\n-This option is only valid when '--interactive' option is used.\n+This option is only valid when the '--interactive' option is used.\n+\n+--no-ff::\n+\tCherry-pick all rebased commits instead of fast-forwarding over\n+\tthe unchanged ones.  This ensures that the entire history of the\n+\trebased branch is composed of new commits.\n++\n+This option is only valid when the '--interactive' option is used.\n++\n+You may find this (or --force-rebase with a non-interactive rebase) helpful\n+after reverting a topic branch merge, as this option recreates the topic\n+branch with fresh commits so it can be remerged successfully without needing\n+to \"reverting the reversion\" (as described in the\n+link:howto/revert-a-faulty-merge.txt[revert-a-faulty-merge How-To]).\n \n include::merge-strategies.txt[]\n \ndiff --git a/Documentation/howto/revert-a-faulty-merge.txt b/Documentation/howto/revert-a-faulty-merge.txt\nindex 3b4a390..eaa8b32 100644\n--- a/Documentation/howto/revert-a-faulty-merge.txt\n+++ b/Documentation/howto/revert-a-faulty-merge.txt\n@@ -142,6 +142,8 @@ different resolution strategies:\n    revert of a merge was rebuilt from scratch (i.e. rebasing and fixing,\n    as you seem to have interpreted), then re-merging the result without\n    doing anything else fancy would be the right thing to do.\n+   (See the ADDENDUM below for how to rebuild a branch from scratch\n+   without changing its original branching-off point.)\n \n However, there are things to keep in mind when reverting a merge (and\n reverting such a revert).\n@@ -177,3 +179,62 @@ the answer is: \"oops, I really shouldn't have merged it, because it wasn't\n ready yet, and I really need to undo _all_ of the merge\"). So then you\n really should revert the merge, but when you want to re-do the merge, you\n now need to do it by reverting the revert.\n+\n+ADDENDUM\n+\n+Sometimes you're in a situation like this\n+\n+ P---o---o---M---x---x---W---x\n+  \\         /\n+   A---B---C\n+\n+where W is the reversion of merge M and you:\n+\n+ - Need to rewrite one of the commits on the A-B-C branch; and\n+\n+ - Want the rewritten A-B-C branch to still start at commit P (perhaps P\n+   is a branching-off point for yet another branch, and you want be able to\n+   merge A-B-C into both branches).\n+\n+The natural thing to do in this case is to checkout the A-B-C branch and use\n+\"rebase -i A\" to change commit B.  However, this does not rewrite commit A,\n+and you end up with this:\n+\n+ P---o---o---M---x---x---W---x\n+  \\         /\n+   A---B---C   <-- old branch\n+   \\\n+    B'---C'    <-- rewritten branch\n+\n+To merge A-B'-C' into the mainline branch you would still have to first revert\n+commit W in order to pick up the changes in A, but then it's likely that the\n+changes in B' will conflict with the original B changes re-introduced by the\n+reversion of W.\n+\n+However, you can avoid these problems if you recreate the entire branch,\n+including commit A:\n+\n+ P---o---o---M---x---x---W---x\n+ |\\         /\n+ | A---B---C   <-- old branch\n+ \\\n+  A'---B'---C' <-- entirely recreated branch\n+\n+Now you can merge A'-B'-C' into the mainline branch without worrying about\n+first reverting W.\n+\n+But if you don't actually need to change commit A, then you need some way to\n+recreate it as a new commit with the same changes in it.  The rebase commmand's\n+two modes (interactive and non-interactive) provide options to do this.  With\n+\"rebase -i\" use the --no-ff option:\n+\n+    $ git rebase -i --no-ff P\n+\n+With plain \"rebase\" use the -f option:\n+\n+    $ git rebase -f P\n+\n+Both these commands create a new branch A'-B'-C' with all-new commits (all the\n+SHA IDs will be different) even if in the interactive case you only actually\n+modify commit B.  You can then merge this new branch directly into the mainline\n+branch and be sure you'll get all of the branch's changes.\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex aa0ab1a..42aa705 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -20,6 +20,7 @@ v,verbose          display a diffstat of what changed upstream\n onto=              rebase onto given branch instead of upstream\n p,preserve-merges  try to recreate merges instead of ignoring them\n s,strategy=        use the given merge strategy\n+no-ff              cherry-pick all commits, even if unchanged\n m,merge            always used (no-op)\n i,interactive      always used (no-op)\n f,force-rebase     always used (no-op)\n@@ -104,6 +105,7 @@ VERBOSE=\n OK_TO_SKIP_PRE_REBASE=\n REBASE_ROOT=\n AUTOSQUASH=\n+NEVER_FF=\n \n GIT_CHERRY_PICK_HELP=\"  After resolving the conflicts,\n mark the corrected paths with 'git add <paths>', and\n@@ -223,7 +225,7 @@ do_with_author () {\n }\n \n pick_one () {\n-\tno_ff=\n+\tno_ff=$NEVER_FF\n \tcase \"$1\" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac\n \toutput git rev-parse --verify $sha1 || die \"Invalid commit name: $sha1\"\n \ttest -d \"$REWRITTEN\" &&\n@@ -746,6 +748,9 @@ first and then run 'git rebase --continue' again.\"\n \t-i)\n \t\t# yeah, we know\n \t\t;;\n+\t--no-ff)\n+\t\tNEVER_FF=t\n+\t\t;;\n \t--root)\n \t\tREBASE_ROOT=t\n \t\t;;\n@@ -931,7 +936,7 @@ EOF\n \t\thas_action \"$TODO\" ||\n \t\t\tdie_abort \"Nothing to do\"\n \n-\t\ttest -d \"$REWRITTEN\" || skip_unnecessary_picks\n+\t\ttest -d \"$REWRITTEN\" || test -n \"$NEVER_FF\" || skip_unnecessary_picks\n \n \t\tgit update-ref ORIG_HEAD $HEAD\n \t\toutput git checkout $ONTO && do_rest\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 4e35137..624e78e 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -22,12 +22,18 @@ set_fake_editor\n # | \\\n # |   F - G - H                (branch1)\n # |     \\\n-#  \\      I                    (branch2)\n-#   \\\n-#     J - K - L - M            (no-conflict-branch)\n+# |\\      I                    (branch2)\n+# | \\\n+# |   J - K - L - M            (no-conflict-branch)\n+#  \\\n+#    N - O - P                 (no-ff-branch)\n #\n # where A, B, D and G all touch file1, and one, two, three, four all\n # touch file \"conflict\".\n+#\n+# WARNING: Modifications to the initial repository can change the SHA ID used\n+# in the expect2 file for the 'stop on conflicting pick' test.\n+\n \n test_expect_success 'setup' '\n \ttest_commit A file1 &&\n@@ -50,6 +56,11 @@ test_expect_success 'setup' '\n \tfor n in J K L M\n \tdo\n \t\ttest_commit $n file$n\n+\tdone &&\n+\tgit checkout -b no-ff-branch A &&\n+\tfor n in N O P\n+\tdo\n+\t\ttest_commit $n file$n\n \tdone\n '\n \n@@ -113,7 +124,7 @@ cat > expect2 << EOF\n D\n =======\n G\n->>>>>>> 51047de... G\n+>>>>>>> 5d18e54... G\n EOF\n \n test_expect_success 'stop on conflicting pick' '\n@@ -553,4 +564,21 @@ test_expect_success 'reword' '\n \tgit show HEAD~2 | grep \"C changed\"\n '\n \n+test_tick # Ensure that the rebased commits get a different timestamp.\n+test_expect_success 'always cherry-pick with --no-ff' '\n+\tgit checkout no-ff-branch &&\n+\tgit tag original-no-ff-branch &&\n+\tgit rebase -i --no-ff A &&\n+\ttouch empty &&\n+\tfor p in 0 1 2\n+\tdo\n+\t\ttest ! $(git rev-parse HEAD~$p) = $(git rev-parse original-no-ff-branch~$p) &&\n+\t\tgit diff HEAD~$p original-no-ff-branch~$p > out &&\n+\t\ttest_cmp empty out\n+\tdone &&\n+\ttest $(git rev-parse HEAD~3) = $(git rev-parse original-no-ff-branch~3) &&\n+\tgit diff HEAD~3 original-no-ff-branch~3 > out &&\n+\ttest_cmp empty out\n+'\n+\n test_done\n-- \n1.7.0.3.1.g59254.dirty\n"},{"id":"137713","messageId":"7vaatxskwc.fsf@alter.siamese.dyndns.org","threadId":"23038","inReplyTo":"1269445261-2941-1-git-send-email-marcnarc@xiplink.com","subject":"Re: [PATCHv4 0/2] Teach the --no-ff option to 'rebase -i'.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-03-24T17:13:23Z","receivedAt":"2010-03-24T17:13:23Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Marc Branchaud <marcnarc@xiplink.com> writes:\n\n> Thanks to both Junio and Jonathan for your patience in working through this.\n>\n> Junio, I think I see what you mean.  I like teaching --no-ff to 'rebase -i'\n> because it allows me to combine two commands into one for this situation.\n>\n> I've split this work into two patches:\n>\n> The first one simply teaches \"rebase -i\" to accept and ignore -f.  I feel\n> this is better than adding text to the man page explaining why interactive\n> rebase has --no-ff but not -f, while non-interactive has the opposite.\n\nI am mildly against that change; I cannot explain why \"rebase -i\" accepts\nand then ignores \"-f\" myself to the users.  \"Non interactive one knows -f\nbut interactive doesn't\" is not necessarily an inconsistency.  \"--force\"\nis about forcing what would normally rejected, and if the latter doesn't\nhave anything that normally rejects, it doesn't need to have a \"--force\".\n\nOn the other hand, I wouldn't mind at all if you were to add \"--no-ff\" to\nnon-interactive rebase; that has a much easier and logical explanation.\n"},{"id":"137724","messageId":"7vfx3pilo1.fsf@alter.siamese.dyndns.org","threadId":"23038","inReplyTo":"1269445261-2941-3-git-send-email-marcnarc@xiplink.com","subject":"Re: [PATCH 2/2] Teach the --no-ff option to 'rebase -i'.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-03-24T19:06:54Z","receivedAt":"2010-03-24T19:06:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Marc Branchaud <marcnarc@xiplink.com> writes:\n\n> This option tells git-rebase--interactive to cherry-pick all the commits in\n> the rebased branch, instead of fast-forwarding over any unchanged commits.\n\nThanks.\n\n> +Sometimes you're in a situation like this\n> +\n> + P---o---o---M---x---x---W---x\n> +  \\         /\n> +   A---B---C\n> +\n> +where W is the reversion of merge M and you:\n> +\n> + - Need to rewrite one of the commits on the A-B-C branch; and\n> +\n> + - Want the rewritten A-B-C branch to still start at commit P (perhaps P\n> +   is a branching-off point for yet another branch, and you want be able to\n> +   merge A-B-C into both branches).\n> +\n> +The natural thing to do in this case is to checkout the A-B-C branch and use\n> +\"rebase -i A\" to change commit B.  However, this does not rewrite commit A,\n> +and you end up with this:\n> +\n> + P---o---o---M---x---x---W---x\n> +  \\         /\n> +   A---B---C   <-- old branch\n> +   \\\n> +    B'---C'    <-- rewritten branch\n> +\n> +To merge A-B'-C' into the mainline branch you would still have to first revert\n> +commit W in order to pick up the changes in A, but then it's likely that the\n> +changes in B' will conflict with the original B changes re-introduced by the\n> +reversion of W.\n\nI find it somewhat a contrived and unconvincing explanation.  A moderately\nintelligent reader would wonder \"Why did you choose to 'rebase -i A' in\nthe first place?  if you did 'rebase -i P' instead, you didn't have to\nsuffer from this issue, no?\", and you are not answering that question.\n\nTwo things that you are assuming but not telling the user to make this\nexplanation appear unsatisfactory are:\n\n - The true reason you reverted M turns out to be that B was bad, and A\n   was fine, so you want to fix B in A-B-C and merge it to master; and\n\n - Because \"rebase -i\" by default fast-forwards earlier \"pick\", even\n   \"rebase -i P\" wouldn't have given you a rewritten A.\n\nIf you spell these out, then the explanation starts to make sense.\n\n> +However, you can avoid these problems if you recreate the entire branch,\n> +including commit A:\n> +\n> + P---o---o---M---x---x---W---x\n> + |\\         /\n> + | A---B---C   <-- old branch\n> + \\\n> +  A'---B'---C' <-- entirely recreated branch\n\nThis is just an advice on drawing, but you can avoid this ugly picture by\nstarting your discussion with:\n\n      A---B---C\n     /         \\\n    P---o---o---M---x---x---W---x\n\nand adding the reproduction like this:\n\n      A---B---C\n     /         \\\n    P---o---o---M---x---x---W---x\n     \\\n      A'--B'--C'\n\n> +But if you don't actually need to change commit A, then you need some way to\n> +recreate it as a new commit with the same changes in it.  The rebase commmand's\n> +two modes (interactive and non-interactive) provide options to do this.  With\n> +\"rebase -i\" use the --no-ff option:\n> +\n> +    $ git rebase -i --no-ff P\n> +\n> +With plain \"rebase\" use the -f option:\n> +\n> +    $ git rebase -f P\n\nThis is exactly why I don't mind giving a --no-ff synonym for --force to\nnon-interactive rebase.  That would make the choice between -i and no -i\nmade by the reader solely depend on the need to change anything in the\ncommits.  If you are fixing B in A-B-C, you would use \"rebase -i\".  If\ninstead you queued a fix-up as D, like so:\n\n      A---B---C-------------------D\n     /         \\\n    P---o---o---M---x---x---W---x\n\nthen you can rebase non-interactively the whole thing in preparation for\nanother merge:\n\n      A---B---C---D\n     /         \\\n    P---o---o---M---x---x---W---x...*\n     \\                             /\n      A'--B'--C'------------------D'\n"},{"id":"137738","messageId":"1269462844-13468-1-git-send-email-marcnarc@xiplink.com","threadId":"23038","inReplyTo":"7vaatxskwc.fsf@alter.siamese.dyndns.org","subject":"[PATCHv5] Teach rebase the --no-ff option.","fromName":"Marc Branchaud","fromEmail":"marcnarc@xiplink.com","sentAt":"2010-03-24T20:34:04Z","receivedAt":"2010-03-24T20:34:04Z","isPatch":false,"sender":{"key":"marcnarc@xiplink.com","avatar":"https://avatars.githubusercontent.com/u/14980203?v=4"},"body":"For git-rebase.sh, --no-ff is a synonym for --force-rebase.\n\nFor git-rebase--interactive.sh, --no-ff cherry-picks all the commits in\nthe rebased branch, instead of fast-forwarding over any unchanged commits.\n\n--no-ff offers an alternative way to deal with reverted merges.  Instead of\n\"reverting the revert\" you can use \"rebase --no-ff\" to recreate the branch\nwith entirely new commits (they're new because at the very least the\ncommitter time is different).  This obviates the need to revert the\nreversion, as you can re-merge the new topic branch directly.  Added an\naddendum to revert-a-faulty-merge.txt describing the situation and how to\nuse --no-ff to handle it.\n\nSigned-off-by: Marc Branchaud <marcnarc@xiplink.com>\n---\n\nNow also makes git-rebase.sh treat --no-ff as a synonym for -f.  I think I\ngot the man page right...\n\nAlso rewrote the addendum to revert-a-faulty-merge.txt to (hopefully) reflect\nJunio's comments.\n\nI agree about not teaching -f to rebase--interative.  I was only mildly for\nit to begin with.  It also occurred to me that we don't want users to think\nthat \"rebase -i\" treats -f the same way as it does --no-ff, but making\nrebase--interactive silently accept -f might encourage that mistake.\n\n\t\tM.\n\n Documentation/git-rebase.txt                  |   23 ++++++-\n Documentation/howto/revert-a-faulty-merge.txt |   90 +++++++++++++++++++++++++\n git-rebase--interactive.sh                    |    9 ++-\n git-rebase.sh                                 |    4 +-\n t/t3404-rebase-interactive.sh                 |   36 +++++++++-\n 5 files changed, 152 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt\nindex 823f2a4..0d07b1b 100644\n--- a/Documentation/git-rebase.txt\n+++ b/Documentation/git-rebase.txt\n@@ -274,9 +274,16 @@ which makes little sense.\n -f::\n --force-rebase::\n \tForce the rebase even if the current branch is a descendant\n-\tof the commit you are rebasing onto.  Normally the command will\n+\tof the commit you are rebasing onto.  Normally non-interactive rebase will\n \texit with the message \"Current branch is up to date\" in such a\n \tsituation.\n+\tIncompatible with the --interactive option.\n++\n+You may find this (or --no-ff with an interactive rebase) helpful after\n+reverting a topic branch merge, as this option recreates the topic branch with\n+fresh commits so it can be remerged successfully without needing to \"revert\n+the reversion\" (see the\n+link:howto/revert-a-faulty-merge.txt[revert-a-faulty-merge How-To] for details).\n \n --ignore-whitespace::\n --whitespace=<option>::\n@@ -316,7 +323,19 @@ which makes little sense.\n \tcommit to be modified, and change the action of the moved\n \tcommit from `pick` to `squash` (or `fixup`).\n +\n-This option is only valid when '--interactive' option is used.\n+This option is only valid when the '--interactive' option is used.\n+\n+--no-ff::\n+\tWith --interactive, cherry-pick all rebased commits instead of\n+\tfast-forwarding over the unchanged ones.  This ensures that the\n+\tentire history of the rebased branch is composed of new commits.\n++\n+Without --interactive, this is a synonym for --force-rebase.\n++\n+You may find this helpful after reverting a topic branch merge, as this option\n+recreates the topic branch with fresh commits so it can be remerged\n+successfully without needing to \"revert the reversion\" (see the\n+link:howto/revert-a-faulty-merge.txt[revert-a-faulty-merge How-To] for details).\n \n include::merge-strategies.txt[]\n \ndiff --git a/Documentation/howto/revert-a-faulty-merge.txt b/Documentation/howto/revert-a-faulty-merge.txt\nindex 3b4a390..ff5c0bc 100644\n--- a/Documentation/howto/revert-a-faulty-merge.txt\n+++ b/Documentation/howto/revert-a-faulty-merge.txt\n@@ -142,6 +142,8 @@ different resolution strategies:\n    revert of a merge was rebuilt from scratch (i.e. rebasing and fixing,\n    as you seem to have interpreted), then re-merging the result without\n    doing anything else fancy would be the right thing to do.\n+   (See the ADDENDUM below for how to rebuild a branch from scratch\n+   without changing its original branching-off point.)\n \n However, there are things to keep in mind when reverting a merge (and\n reverting such a revert).\n@@ -177,3 +179,91 @@ the answer is: \"oops, I really shouldn't have merged it, because it wasn't\n ready yet, and I really need to undo _all_ of the merge\"). So then you\n really should revert the merge, but when you want to re-do the merge, you\n now need to do it by reverting the revert.\n+\n+ADDENDUM\n+\n+Sometimes you have to rewrite one of a topic branch's commits *and* you can't\n+change the topic's branching-off point.  Consider the following situation:\n+\n+ P---o---o---M---x---x---W---x\n+  \\         /\n+   A---B---C\n+\n+where commit W reverted commit M because it turned out that commit B was wrong\n+and needs to be rewritten, but you need the rewritten topic to still branch\n+from commit P (perhaps P is a branching-off point for yet another branch, and\n+you want be able to merge the topic into both branches).\n+\n+The natural thing to do in this case is to checkout the A-B-C branch and use\n+\"rebase -i P\" to change commit B.  However this does not rewrite commit A,\n+because \"rebase -i\" by default fast-forwards over any initial commits selected\n+with the \"pick\" command.  So you end up with this:\n+\n+ P---o---o---M---x---x---W---x\n+  \\         /\n+   A---B---C   <-- old branch\n+    \\\n+     B'---C'   <-- naively rewritten branch\n+\n+To merge A-B'-C' into the mainline branch you would still have to first revert\n+commit W in order to pick up the changes in A, but then it's likely that the\n+changes in B' will conflict with the original B changes re-introduced by the\n+reversion of W.\n+\n+However, you can avoid these problems if you recreate the entire branch,\n+including commit A:\n+\n+   A'---B'---C'  <-- completely rewritten branch\n+  /\n+ P---o---o---M---x---x---W---x\n+  \\         /\n+   A---B---C\n+\n+You can merge A'-B'-C' into the mainline branch without worrying about first\n+reverting W.  Mainline's history would look like this:\n+\n+   A'---B'---C'------------------\n+  /                              \\\n+ P---o---o---M---x---x---W---x---M2\n+  \\         /\n+   A---B---C\n+\n+But if you don't actually need to change commit A, then you need some way to\n+recreate it as a new commit with the same changes in it.  The rebase commmand's\n+--no-ff option provides a way to do this:\n+\n+    $ git rebase [-i] --no-ff P\n+\n+The --no-ff option creates a new branch A'-B'-C' with all-new commits (all the\n+SHA IDs will be different) even if in the interactive case you only actually\n+modify commit B.  You can then merge this new branch directly into the mainline\n+branch and be sure you'll get all of the branch's changes.\n+\n+You can also use --no-ff in cases where you just add extra commits to the topic\n+to fix it up.  Let's revisit the situation discussed at the start of this howto:\n+\n+ P---o---o---M---x---x---W---x\n+  \\         /\n+   A---B---C----------------D---E   <-- fixed-up topic branch\n+\n+At this point, you can use --no-ff to recreate the topic branch:\n+\n+    $ git checkout E\n+    $ git rebase --no-ff P\n+\n+yielding\n+\n+   A'---B'---C'------------D'---E'  <-- recreated topic branch\n+  /\n+ P---o---o---M---x---x---W---x\n+  \\         /\n+   A---B---C----------------D---E\n+\n+You can merge the recreated branch into the mainline without reverting commit W,\n+and mainline's history will look like this:\n+\n+   A'---B'---C'------------D'---E'\n+  /                              \\\n+ P---o---o---M---x---x---W---x---M2\n+  \\         /\n+   A---B---C\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex 3e4fd14..d5468b0 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -20,6 +20,7 @@ v,verbose          display a diffstat of what changed upstream\n onto=              rebase onto given branch instead of upstream\n p,preserve-merges  try to recreate merges instead of ignoring them\n s,strategy=        use the given merge strategy\n+no-ff              cherry-pick all commits, even if unchanged\n m,merge            always used (no-op)\n i,interactive      always used (no-op)\n  Actions:\n@@ -103,6 +104,7 @@ VERBOSE=\n OK_TO_SKIP_PRE_REBASE=\n REBASE_ROOT=\n AUTOSQUASH=\n+NEVER_FF=\n \n GIT_CHERRY_PICK_HELP=\"  After resolving the conflicts,\n mark the corrected paths with 'git add <paths>', and\n@@ -222,7 +224,7 @@ do_with_author () {\n }\n \n pick_one () {\n-\tno_ff=\n+\tno_ff=$NEVER_FF\n \tcase \"$1\" in -n) sha1=$2; no_ff=t ;; *) sha1=$1 ;; esac\n \toutput git rev-parse --verify $sha1 || die \"Invalid commit name: $sha1\"\n \ttest -d \"$REWRITTEN\" &&\n@@ -742,6 +744,9 @@ first and then run 'git rebase --continue' again.\"\n \t-i)\n \t\t# yeah, we know\n \t\t;;\n+\t--no-ff)\n+\t\tNEVER_FF=t\n+\t\t;;\n \t--root)\n \t\tREBASE_ROOT=t\n \t\t;;\n@@ -927,7 +932,7 @@ EOF\n \t\thas_action \"$TODO\" ||\n \t\t\tdie_abort \"Nothing to do\"\n \n-\t\ttest -d \"$REWRITTEN\" || skip_unnecessary_picks\n+\t\ttest -d \"$REWRITTEN\" || test -n \"$NEVER_FF\" || skip_unnecessary_picks\n \n \t\tgit update-ref ORIG_HEAD $HEAD\n \t\toutput git checkout $ONTO && do_rest\ndiff --git a/git-rebase.sh b/git-rebase.sh\nindex fb4fef7..8b23f8b 100755\n--- a/git-rebase.sh\n+++ b/git-rebase.sh\n@@ -3,7 +3,7 @@\n # Copyright (c) 2005 Junio C Hamano.\n #\n \n-USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--onto <newbase>] [<upstream>|--root] [<branch>] [--quiet | -q]'\n+USAGE='[--interactive | -i] [-v] [--force-rebase | -f] [--no-ff] [--onto <newbase>] [<upstream>|--root] [<branch>] [--quiet | -q]'\n LONG_USAGE='git-rebase replaces <branch> with a new branch of the\n same name.  When the --onto option is provided the new branch starts\n out with a HEAD equal to <newbase>, otherwise it is equal to <upstream>\n@@ -347,7 +347,7 @@ do\n \t--root)\n \t\trebase_root=t\n \t\t;;\n-\t-f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase)\n+\t-f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase|--no-ff)\n \t\tforce_rebase=t\n \t\t;;\n \t--rerere-autoupdate|--no-rerere-autoupdate)\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 4e35137..624e78e 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -22,12 +22,18 @@ set_fake_editor\n # | \\\n # |   F - G - H                (branch1)\n # |     \\\n-#  \\      I                    (branch2)\n-#   \\\n-#     J - K - L - M            (no-conflict-branch)\n+# |\\      I                    (branch2)\n+# | \\\n+# |   J - K - L - M            (no-conflict-branch)\n+#  \\\n+#    N - O - P                 (no-ff-branch)\n #\n # where A, B, D and G all touch file1, and one, two, three, four all\n # touch file \"conflict\".\n+#\n+# WARNING: Modifications to the initial repository can change the SHA ID used\n+# in the expect2 file for the 'stop on conflicting pick' test.\n+\n \n test_expect_success 'setup' '\n \ttest_commit A file1 &&\n@@ -50,6 +56,11 @@ test_expect_success 'setup' '\n \tfor n in J K L M\n \tdo\n \t\ttest_commit $n file$n\n+\tdone &&\n+\tgit checkout -b no-ff-branch A &&\n+\tfor n in N O P\n+\tdo\n+\t\ttest_commit $n file$n\n \tdone\n '\n \n@@ -113,7 +124,7 @@ cat > expect2 << EOF\n D\n =======\n G\n->>>>>>> 51047de... G\n+>>>>>>> 5d18e54... G\n EOF\n \n test_expect_success 'stop on conflicting pick' '\n@@ -553,4 +564,21 @@ test_expect_success 'reword' '\n \tgit show HEAD~2 | grep \"C changed\"\n '\n \n+test_tick # Ensure that the rebased commits get a different timestamp.\n+test_expect_success 'always cherry-pick with --no-ff' '\n+\tgit checkout no-ff-branch &&\n+\tgit tag original-no-ff-branch &&\n+\tgit rebase -i --no-ff A &&\n+\ttouch empty &&\n+\tfor p in 0 1 2\n+\tdo\n+\t\ttest ! $(git rev-parse HEAD~$p) = $(git rev-parse original-no-ff-branch~$p) &&\n+\t\tgit diff HEAD~$p original-no-ff-branch~$p > out &&\n+\t\ttest_cmp empty out\n+\tdone &&\n+\ttest $(git rev-parse HEAD~3) = $(git rev-parse original-no-ff-branch~3) &&\n+\tgit diff HEAD~3 original-no-ff-branch~3 > out &&\n+\ttest_cmp empty out\n+'\n+\n test_done\n-- \n1.7.0.3.1.g7f7ff\n"},{"id":"137750","messageId":"7vr5n9xuki.fsf@alter.siamese.dyndns.org","threadId":"23038","inReplyTo":"1269462844-13468-1-git-send-email-marcnarc@xiplink.com","subject":"Re: [PATCHv5] Teach rebase the --no-ff option.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2010-03-24T21:45:33Z","receivedAt":"2010-03-24T21:45:33Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Marc Branchaud <marcnarc@xiplink.com> writes:\n\n> For git-rebase.sh, --no-ff is a synonym for --force-rebase.\n\nThanks, will queue.\n"}]}