{"thread":{"id":"15737","subject":"[PATCH] Fix interactive rebase on dropped commits.","startedAt":"2008-10-01T06:11:07Z","lastAt":"2008-10-06T04:26:52Z","messageCount":3,"participants":["Stephen Haberman"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"92032","messageId":"20081001011107.0971ce32.stephen@exigencecorp.com","threadId":"15737","inReplyTo":null,"subject":"[PATCH] Fix interactive rebase on dropped commits.","fromName":"Stephen Haberman","fromEmail":"stephen@exigencecorp.com","sentAt":"2008-10-01T06:11:07Z","receivedAt":"2008-10-01T06:11:07Z","isPatch":true,"sender":{"key":"stephen@exigencecorp.com","avatar":"https://gravatar.com/avatar/23b93ad70a06ce53505f17ddba65176edbcfb6588e7a4c1a2dca04aaf0a6aff1?d=mp&s=160"},"body":"Interactive rebase got its rev-list of commits to keep by --left-right and\n--cherry-pick. Adding --cherry-pick would throw out commits that were just\nduplicating changes already in the rebase target.\n\nWhich is desirable, except the dropped commit has forgotten about when it came\nto rewriting the parents of its descendents, so the descendents would get\ncherry-picked as-in and essentially make the rebase a no-op.\n\nThis change adds a $DOTEST/dropped directory to remember dropped commits and\nrewrite its children's parents as the children's (possibly rewritten)\ngrandparents.\n\nSigned-off-by: Stephen Haberman <stephen@exigencecorp.com>\n---\n git-rebase--interactive.sh                  |   52 ++++++++++++---\n t/t3409-rebase-interactive-cherry-picked.sh |   94 +++++++++++++++++++++++++++\n 2 files changed, 136 insertions(+), 10 deletions(-)\n create mode 100644 t/t3409-rebase-interactive-cherry-picked.sh\n\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex 929d681..2d62590 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -37,6 +37,7 @@ DONE=\"$DOTEST\"/done\n MSG=\"$DOTEST\"/message\n SQUASH_MSG=\"$DOTEST\"/message-squash\n REWRITTEN=\"$DOTEST\"/rewritten\n+DROPPED=\"$DOTEST\"/dropped\n PRESERVE_MERGES=\n STRATEGY=\n ONTO=\n@@ -146,15 +147,7 @@ pick_one () {\n \n pick_one_preserving_merges () {\n \tfast_forward=t\n-\tcase \"$1\" in\n-\t-n)\n-\t\tfast_forward=f\n-\t\tsha1=$2\n-\t\t;;\n-\t*)\n-\t\tsha1=$1\n-\t\t;;\n-\tesac\n+\tcase \"$1\" in -n) fast_forward=f sha1=$2 ;; *) sha1=$1 ;; esac\n \tsha1=$(git rev-parse $sha1)\n \n \tif test -f \"$DOTEST\"/current-commit\n@@ -183,7 +176,28 @@ pick_one_preserving_merges () {\n \t\t\t\t;;\n \t\t\tesac\n \t\telse\n-\t\t\tnew_parents=\"$new_parents $p\"\n+\t\t\tif test -f \"$DROPPED\"/$p\n+\t\t\tthen\n+\t\t\t\tfast_forward=f\n+\t\t\t\tcat \"$DROPPED\"/$p | while read grandparent\n+\t\t\t\tdo\n+\t\t\t\t\tif test -f \"$REWRITTEN\"/$grandparent\n+\t\t\t\t\tthen\n+\t\t\t\t\t\tnew_p=$(cat \"$REWRITTEN\"/$grandparent)\n+\t\t\t\t\telse\n+\t\t\t\t\t\tnew_p=$grandparent\n+\t\t\t\t\tfi\n+\t\t\t\t\tcase \"$new_parents\" in\n+\t\t\t\t\t*$new_p*)\n+\t\t\t\t\t\t;; # do nothing; that parent is already there\n+\t\t\t\t\t*)\n+\t\t\t\t\t\tnew_parents=\"$new_parents $new_p\"\n+\t\t\t\t\t\t;;\n+\t\t\t\t\tesac\n+\t\t\t\tdone\n+\t\t\telse\n+\t\t\t\tnew_parents=\"$new_parents $p\"\n+\t\t\tfi\n \t\tfi\n \tdone\n \tcase $fast_forward in\n@@ -574,6 +588,24 @@ do\n #\n EOF\n \n+\t\t# Watch for commits that been dropped by --cherry-pick\n+\t\tif test t = \"$PRESERVE_MERGES\"\n+\t\tthen\n+\t\t\tmkdir \"$DROPPED\"\n+\t\t\t# drop the --cherry-pick parameter this time\n+\t\t\tgit rev-list $MERGES_OPTION --abbrev-commit \\\n+\t\t\t\t--abbrev=7 $UPSTREAM...$HEAD --left-right | \\\n+\t\t\t\tsed -n \"s/^>//p\" | while read rev\n+\t\t\tdo\n+\t\t\t\tgrep --quiet \"$rev\" \"$TODO\"\n+\t\t\t\tif [ $? -ne 0 ]\n+\t\t\t\tthen\n+\t\t\t\t\tfull=$(git rev-parse $rev)\n+\t\t\t\t\tgit rev-list --parents -1 $rev | cut -d' ' -f2- | sed 's/ /\\n/g' > \"$DROPPED\"/$full\n+\t\t\t\tfi\n+\t\t\tdone\n+\t\tfi\n+\n \t\thas_action \"$TODO\" ||\n \t\t\tdie_abort \"Nothing to do\"\n \ndiff --git a/t/t3409-rebase-interactive-cherry-picked.sh b/t/t3409-rebase-interactive-cherry-picked.sh\nnew file mode 100644\nindex 0000000..3d20180\n--- /dev/null\n+++ b/t/t3409-rebase-interactive-cherry-picked.sh\n@@ -0,0 +1,94 @@\n+#!/bin/sh\n+\n+test_description='rebase interactive does not rebase'\n+\n+. ./test-lib.sh\n+\n+test_expect_success 'setup' '\n+\techo \"setup\" >a &&\n+\tgit add a &&\n+\tgit commit -m \"setup\" &&\n+\tgit clone ./. server &&\n+\trm -fr server/.git/hooks &&\n+\tgit remote add origin ./server &&\n+\tgit config --add branch.master.remote origin &&\n+\tgit config --add branch.master.merge refs/heads/master &&\n+\tgit fetch &&\n+\n+\tgit checkout -b stable master &&\n+\techo \"setup.stable\" >a &&\n+\tgit commit -a -m \"stable\" &&\n+\tgit push origin stable\n+'\n+#\n+# A --C------            <-- origin/stable\n+#  \\  |      \\\n+#   B -- D -- E -- F     <-- origin/topic2\n+#    \\|             \\\n+#     g -- h ------- i   <-- topic2\n+#\n+# Trying to push F..i\n+#\n+# merge-base(F, h) has two options: B and C\n+#\n+test_expect_success 'merging in stable with tricky double baserev does not fool the script' '\n+\t# B: start our topic2 branch, and share it\n+\tgit checkout -b topic2 origin/stable &&\n+\tgit config --add branch.topic2.merge refs/heads/topic2 &&\n+\techo \"commit B\" >a.topic2 &&\n+\tgit add a.topic2 &&\n+\tgit commit -m \"commit B created topic2\" &&\n+\tgit push origin topic2 &&\n+\n+\t# C: now, separately, move ahead stable, and share it\n+\tgit checkout stable\n+\techo \"commit C\" >a &&\n+\tgit commit -a -m \"commit C moved stable\" &&\n+\tgit push origin stable &&\n+\n+\t# D: have another client commit (in this case, it is the server, but close enough) moves topic2\n+\tcd server &&\n+\tgit checkout topic2 &&\n+\techo \"commit D continuing topic2\" >a.client2 &&\n+\tgit add a.client2 &&\n+\tgit commit -m \"commit D by client2\" &&\n+\n+\t# E: the same other client merges the moved stable\n+\tgit merge stable &&\n+\n+\t# F: the same other client moves topic2 again\n+\techo \"commit F\" >a.client2 &&\n+\tgit commit -a -m \"commit F by client2\" &&\n+\tF_hash=$(git rev-parse HEAD) &&\n+\tcd .. &&\n+\n+\t# g: now locally merge in the moved stable (even though our topic2 is out of date)\n+\tgit checkout topic2 &&\n+\tgit merge stable &&\n+\tg_hash=$(git rev-parse HEAD) &&\n+\n+\t# h: advance local topic2\n+\techo \"commit H\" >a.topic2 &&\n+\tgit commit -a -m \"commit H continues local fork\" &&\n+\th_hash=$(git rev-parse HEAD) &&\n+\n+\t# i: make a new merge commit\n+\tgit pull --no-rebase &&\n+\ti_hash=$(git rev-parse HEAD) &&\n+\n+\t# Watch merge rejected as something that should get rebased\n+\t# ! git push origin topic2\n+\ttest \"$i_hash $h_hash $F_hash\" = \"$(git rev-list --parents --no-walk HEAD)\"\n+\n+\t# Now fix it the merge by rebasing it\n+\tgit reset --hard ORIG_HEAD &&\n+\tGIT_EDITOR=: git rebase -i -p origin/topic2 &&\n+\th2_hash=$(git rev-parse HEAD) &&\n+\n+\t# Previously $F_hash was dropped and it was the same $h_hash $g_hash\n+\ttest \"$h2_hash $F_hash\" = \"$(git rev-list --parents --no-walk HEAD)\"\n+'\n+\n+test_done\n+\n+\n-- \n1.6.0.2\n"},{"id":"92266","messageId":"20081003133246.bae6b657.stephen@exigencecorp.com","threadId":"15737","inReplyTo":"20081001011107.0971ce32.stephen@exigencecorp.com","subject":"[PATCH v2 RFC] Fix interactive rebase on dropped commits.","fromName":"Stephen Haberman","fromEmail":"stephen@exigencecorp.com","sentAt":"2008-10-03T18:32:46Z","receivedAt":"2008-10-03T18:32:46Z","isPatch":true,"sender":{"key":"stephen@exigencecorp.com","avatar":"https://gravatar.com/avatar/23b93ad70a06ce53505f17ddba65176edbcfb6588e7a4c1a2dca04aaf0a6aff1?d=mp&s=160"},"body":"Interactive rebase got it's rev-list of commits to keep by --left-right and\n--cherry-pick. Adding --cherry-pick would throw out commits that were just\nduplicating changes already in the rebase target.\n\nWhich is cool, except the dropped commit was forgotten about when it came to\nrewriting the parents of its descendents, so the descendents would get\ncherry-picked as-in and essentially make the rebase a noop.\n\nThis change adds a $DOTEST/dropped directory to remember dropped commits and\nrewrite its children's parents as the children's grandparents (possibly\nrewritten).\n\nSigned-off-by: Stephen Haberman <stephen@exigencecorp.com>\n---\n\nThis includes 3410, which borrows the 3404 DAG in a separate\ntest to do more a complex test of the dropped commits than my\nprevious test.\n\nThe implementation is also simpler--instead of following all\nof a dropped commit's parents, we follow just the first. We can\ntrust rev-list is telling us to drop it for a reason, so only\nthe first-parent is needed so we can correctly rewrite commits\nbased on top of ours.\n\nt3404 is still passing. (Finally.)\n\n git-rebase--interactive.sh                |   37 +++++++-\n t/t3410-rebase-preserve-dropped-merges.sh |  140 +++++++++++++++++++++++++++++\n 2 files changed, 175 insertions(+), 2 deletions(-)\n create mode 100644 t/t3410-rebase-preserve-dropped-merges.sh\n\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex edb6ec6..e8cb8a2 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -37,6 +37,7 @@ DONE=\"$DOTEST\"/done\n MSG=\"$DOTEST\"/message\n SQUASH_MSG=\"$DOTEST\"/message-squash\n REWRITTEN=\"$DOTEST\"/rewritten\n+DROPPED=\"$DOTEST\"/dropped\n PRESERVE_MERGES=\n STRATEGY=\n ONTO=\n@@ -169,8 +170,12 @@ pick_one_preserving_merges () {\n \n \t# rewrite parents; if none were rewritten, we can fast-forward.\n \tnew_parents=\n-\tfor p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)\n+\tpend=\" $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)\"\n+\twhile [ \"$pend\" != \"\" ]\n \tdo\n+\t\tp=$(expr \"$pend\" : ' \\([^ ]*\\)')\n+\t\tpend=\"${pend# $p}\"\n+\n \t\tif test -f \"$REWRITTEN\"/$p\n \t\tthen\n \t\t\tnew_p=$(cat \"$REWRITTEN\"/$p)\n@@ -183,7 +188,13 @@ pick_one_preserving_merges () {\n \t\t\t\t;;\n \t\t\tesac\n \t\telse\n-\t\t\tnew_parents=\"$new_parents $p\"\n+\t\t\tif test -f \"$DROPPED\"/$p\n+\t\t\tthen\n+\t\t\t\tfast_forward=f\n+\t\t\t\tpend=\"$pend $(cat \"$DROPPED\"/$p)\"\n+\t\t\telse\n+\t\t\t\tnew_parents=\"$new_parents $new_p\"\n+\t\t\tfi\n \t\tfi\n \tdone\n \tcase $fast_forward in\n@@ -582,6 +593,28 @@ first and then run 'git rebase --continue' again.\"\n #\n EOF\n \n+\t\t# Watch for commits that been dropped by --cherry-pick\n+\t\tif test t = \"$PRESERVE_MERGES\"\n+\t\tthen\n+\t\t\tmkdir \"$DROPPED\"\n+\t\t\t# drop the --cherry-pick parameter this time\n+\t\t\tgit rev-list $MERGES_OPTION --abbrev-commit \\\n+\t\t\t\t--abbrev=7 $UPSTREAM...$HEAD --left-right | \\\n+\t\t\t\tsed -n \"s/^>//p\" | while read rev\n+\t\t\tdo\n+\t\t\t\tgrep --quiet \"$rev\" \"$TODO\"\n+\t\t\t\tif [ $? -ne 0 ]\n+\t\t\t\tthen\n+\t\t\t\t\t# Use -f2 because if rev-list is telling this commit is not\n+\t\t\t\t\t# worthwhile, we don't want to track its multiple heads,\n+\t\t\t\t\t# just the history of its first-parent for others that will\n+\t\t\t\t\t# be rebasing on top of us\n+\t\t\t\t\tfull=$(git rev-parse $rev)\n+\t\t\t\t\tgit rev-list --parents -1 $rev | cut -d' ' -f2 > \"$DROPPED\"/$full\n+\t\t\t\tfi\n+\t\t\tdone\n+\t\tfi\n+\n \t\thas_action \"$TODO\" ||\n \t\t\tdie_abort \"Nothing to do\"\n \ndiff --git a/t/t3410-rebase-preserve-dropped-merges.sh b/t/t3410-rebase-preserve-dropped-merges.sh\nnew file mode 100644\nindex 0000000..7c8862b\n--- /dev/null\n+++ b/t/t3410-rebase-preserve-dropped-merges.sh\n@@ -0,0 +1,140 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2008 Stephen Haberman\n+#\n+\n+test_description='git rebase preserve merges\n+\n+This test runs git rebase with preserve merges and ensures commits\n+dropped by the --cherry-pick flag have their childrens parents\n+rewritten.\n+'\n+. ./test-lib.sh\n+\n+# set up two branches like this:\n+#\n+# A - B - C - D - E\n+#   \\\n+#     F - G - H\n+#       \\\n+#         I\n+#\n+# where B, D and G touch the same file.\n+\n+test_expect_success 'setup' '\n+\t: > file1 &&\n+\tgit add file1 &&\n+\ttest_tick &&\n+\tgit commit -m A &&\n+\tgit tag A &&\n+\techo 1 > file1 &&\n+\ttest_tick &&\n+\tgit commit -m B file1 &&\n+\t: > file2 &&\n+\tgit add file2 &&\n+\ttest_tick &&\n+\tgit commit -m C &&\n+\techo 2 > file1 &&\n+\ttest_tick &&\n+\tgit commit -m D file1 &&\n+\t: > file3 &&\n+\tgit add file3 &&\n+\ttest_tick &&\n+\tgit commit -m E &&\n+\tgit tag E &&\n+\tgit checkout -b branch1 A &&\n+\t: > file4 &&\n+\tgit add file4 &&\n+\ttest_tick &&\n+\tgit commit -m F &&\n+\tgit tag F &&\n+\techo 3 > file1 &&\n+\ttest_tick &&\n+\tgit commit -m G file1 &&\n+\tgit tag G &&\n+\t: > file5 &&\n+\tgit add file5 &&\n+\ttest_tick &&\n+\tgit commit -m H &&\n+\tgit tag H &&\n+\tgit checkout -b branch2 F &&\n+\t: > file6 &&\n+\tgit add file6 &&\n+\ttest_tick &&\n+\tgit commit -m I &&\n+\tgit tag I\n+'\n+\n+# A - B - C - D - E\n+#   \\             \\ \\\n+#     F - G - H -- L \\        -->   L\n+#       \\            |               \\\n+#         I -- G2 -- J -- K           I -- K\n+# G2 = same changes as G\n+test_expect_success 'skip same-resolution merges with -p' '\n+\tgit checkout branch1 &&\n+\t! git merge E &&\n+\techo 23 > file1 &&\n+\tgit add file1 &&\n+\tgit commit -m L &&\n+\tgit checkout branch2 &&\n+\techo 3 > file1 &&\n+\tgit commit -a -m G2 &&\n+\t! git merge E &&\n+\techo 23 > file1 &&\n+\tgit add file1 &&\n+\tgit commit -m J &&\n+\techo file7 > file7 &&\n+\tgit add file7 &&\n+\tgit commit -m K &&\n+\tGIT_EDITOR=: git rebase -i -p branch1 &&\n+\ttest $(git rev-parse branch2^^) = $(git rev-parse branch1) &&\n+\ttest \"23\" = \"$(cat file1)\" &&\n+\ttest \"\" = \"$(cat file6)\" &&\n+\ttest \"file7\" = \"$(cat file7)\" &&\n+\n+\tgit checkout branch1 &&\n+\tgit reset --hard H &&\n+\tgit checkout branch2 &&\n+\tgit reset --hard I\n+'\n+\n+# A - B - C - D - E\n+#   \\             \\ \\\n+#     F - G - H -- L \\        -->   L\n+#       \\            |               \\\n+#         I -- G2 -- J -- K           I -- G2 -- K\n+# G2 = different changes as G\n+test_expect_success 'keep different-resolution merges with -p' '\n+\tgit checkout branch1 &&\n+\t! git merge E &&\n+\techo 23 > file1 &&\n+\tgit add file1 &&\n+\tgit commit -m L &&\n+\tgit checkout branch2 &&\n+\techo 4 > file1 &&\n+\tgit commit -a -m G2 &&\n+\t! git merge E &&\n+\techo 24 > file1 &&\n+\tgit add file1 &&\n+\tgit commit -m J &&\n+\techo file7 > file7 &&\n+\tgit add file7 &&\n+\tgit commit -m K &&\n+\t! GIT_EDITOR=: git rebase -i -p branch1 &&\n+\techo 234 > file1 &&\n+\tgit add file1 &&\n+\tGIT_EDITOR=: git rebase --continue &&\n+\ttest $(git rev-parse branch2^^^) = $(git rev-parse branch1) &&\n+\ttest \"234\" = \"$(cat file1)\" &&\n+\ttest \"\" = \"$(cat file6)\" &&\n+\ttest \"file7\" = \"$(cat file7)\" &&\n+\n+\tgit checkout branch1 &&\n+\tgit reset --hard H &&\n+\tgit checkout branch2 &&\n+\tgit reset --hard I\n+'\n+\n+test_done\n+\n-- \n1.6.0.2\n"},{"id":"92383","messageId":"20081005232652.ab151c3e.stephen@exigencecorp.com","threadId":"15737","inReplyTo":"20081003133246.bae6b657.stephen@exigencecorp.com","subject":"[PATCH v3] rebase--interactive: fix parent rewriting for dropped commits","fromName":"Stephen Haberman","fromEmail":"stephen@exigencecorp.com","sentAt":"2008-10-06T04:26:52Z","receivedAt":"2008-10-06T04:26:52Z","isPatch":true,"sender":{"key":"stephen@exigencecorp.com","avatar":"https://gravatar.com/avatar/23b93ad70a06ce53505f17ddba65176edbcfb6588e7a4c1a2dca04aaf0a6aff1?d=mp&s=160"},"body":"`rebase -i -p` got its rev-list of commits to keep by --left-right and\n--cherry-pick. Adding --cherry-pick would drop commits that duplicated changes\nalready in the rebase target.\n\nThe dropped commits were then forgotten about when it came to rewriting the\nparents of their descendents, so the descendents would get cherry-picked with\ntheir old, unwritten parents and essentially make the rebase a no-op.\n\nThis commit adds a $DOTEST/dropped directory to remember dropped commits and\nrewrite their children's parent as the dropped commit's possibly-rewritten\nfirst-parent.\n\nSigned-off-by: Stephen Haberman <stephen@exigencecorp.com>\n---\n\nThese two lines changed from the v2 patch:\n\n    pend=\" $(cat \"$DROPPED\"/$p)$pend\"\n\nIt now puts $p at the start of $pend instead of the end. I have no tests\nthat assert this behavior (vs. the old of putting it at the end), but it\nseems rational to put the dropped-replacement parent first so that the\ncurrent commit's parents come out in the same order (otherwise a commit\nwith parents \"p1 p2dropped p3\" would end up \"p1 p3 p2firstparent\").\n\nAlso, this line changed:\n\n    new_parents=\"$new_parents $p\"\n\nPreviously it said \"$new_parents $new_p\" which was a copy/paste bug--$new_p\nwas from up in the code a bit and here we just want to use $p.\n\nI also cleaned up the commit message.\n\n git-rebase--interactive.sh                |   37 +++++++-\n t/t3410-rebase-preserve-dropped-merges.sh |  140 +++++++++++++++++++++++++++++\n 2 files changed, 175 insertions(+), 2 deletions(-)\n create mode 100644 t/t3410-rebase-preserve-dropped-merges.sh\n\ndiff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh\nindex edb6ec6..4d53347 100755\n--- a/git-rebase--interactive.sh\n+++ b/git-rebase--interactive.sh\n@@ -37,6 +37,7 @@ DONE=\"$DOTEST\"/done\n MSG=\"$DOTEST\"/message\n SQUASH_MSG=\"$DOTEST\"/message-squash\n REWRITTEN=\"$DOTEST\"/rewritten\n+DROPPED=\"$DOTEST\"/dropped\n PRESERVE_MERGES=\n STRATEGY=\n ONTO=\n@@ -169,8 +170,12 @@ pick_one_preserving_merges () {\n \n \t# rewrite parents; if none were rewritten, we can fast-forward.\n \tnew_parents=\n-\tfor p in $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)\n+\tpend=\" $(git rev-list --parents -1 $sha1 | cut -d' ' -f2-)\"\n+\twhile [ \"$pend\" != \"\" ]\n \tdo\n+\t\tp=$(expr \"$pend\" : ' \\([^ ]*\\)')\n+\t\tpend=\"${pend# $p}\"\n+\n \t\tif test -f \"$REWRITTEN\"/$p\n \t\tthen\n \t\t\tnew_p=$(cat \"$REWRITTEN\"/$p)\n@@ -183,7 +188,13 @@ pick_one_preserving_merges () {\n \t\t\t\t;;\n \t\t\tesac\n \t\telse\n-\t\t\tnew_parents=\"$new_parents $p\"\n+\t\t\tif test -f \"$DROPPED\"/$p\n+\t\t\tthen\n+\t\t\t\tfast_forward=f\n+\t\t\t\tpend=\" $(cat \"$DROPPED\"/$p)$pend\"\n+\t\t\telse\n+\t\t\t\tnew_parents=\"$new_parents $p\"\n+\t\t\tfi\n \t\tfi\n \tdone\n \tcase $fast_forward in\n@@ -582,6 +593,28 @@ first and then run 'git rebase --continue' again.\"\n #\n EOF\n \n+\t\t# Watch for commits that been dropped by --cherry-pick\n+\t\tif test t = \"$PRESERVE_MERGES\"\n+\t\tthen\n+\t\t\tmkdir \"$DROPPED\"\n+\t\t\t# drop the --cherry-pick parameter this time\n+\t\t\tgit rev-list $MERGES_OPTION --abbrev-commit \\\n+\t\t\t\t--abbrev=7 $UPSTREAM...$HEAD --left-right | \\\n+\t\t\t\tsed -n \"s/^>//p\" | while read rev\n+\t\t\tdo\n+\t\t\t\tgrep --quiet \"$rev\" \"$TODO\"\n+\t\t\t\tif [ $? -ne 0 ]\n+\t\t\t\tthen\n+\t\t\t\t\t# Use -f2 because if rev-list is telling this commit is not\n+\t\t\t\t\t# worthwhile, we don't want to track its multiple heads,\n+\t\t\t\t\t# just the history of its first-parent for others that will\n+\t\t\t\t\t# be rebasing on top of us\n+\t\t\t\t\tfull=$(git rev-parse $rev)\n+\t\t\t\t\tgit rev-list --parents -1 $rev | cut -d' ' -f2 > \"$DROPPED\"/$full\n+\t\t\t\tfi\n+\t\t\tdone\n+\t\tfi\n+\n \t\thas_action \"$TODO\" ||\n \t\t\tdie_abort \"Nothing to do\"\n \ndiff --git a/t/t3410-rebase-preserve-dropped-merges.sh b/t/t3410-rebase-preserve-dropped-merges.sh\nnew file mode 100644\nindex 0000000..7c8862b\n--- /dev/null\n+++ b/t/t3410-rebase-preserve-dropped-merges.sh\n@@ -0,0 +1,140 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2008 Stephen Haberman\n+#\n+\n+test_description='git rebase preserve merges\n+\n+This test runs git rebase with preserve merges and ensures commits\n+dropped by the --cherry-pick flag have their childrens parents\n+rewritten.\n+'\n+. ./test-lib.sh\n+\n+# set up two branches like this:\n+#\n+# A - B - C - D - E\n+#   \\\n+#     F - G - H\n+#       \\\n+#         I\n+#\n+# where B, D and G touch the same file.\n+\n+test_expect_success 'setup' '\n+\t: > file1 &&\n+\tgit add file1 &&\n+\ttest_tick &&\n+\tgit commit -m A &&\n+\tgit tag A &&\n+\techo 1 > file1 &&\n+\ttest_tick &&\n+\tgit commit -m B file1 &&\n+\t: > file2 &&\n+\tgit add file2 &&\n+\ttest_tick &&\n+\tgit commit -m C &&\n+\techo 2 > file1 &&\n+\ttest_tick &&\n+\tgit commit -m D file1 &&\n+\t: > file3 &&\n+\tgit add file3 &&\n+\ttest_tick &&\n+\tgit commit -m E &&\n+\tgit tag E &&\n+\tgit checkout -b branch1 A &&\n+\t: > file4 &&\n+\tgit add file4 &&\n+\ttest_tick &&\n+\tgit commit -m F &&\n+\tgit tag F &&\n+\techo 3 > file1 &&\n+\ttest_tick &&\n+\tgit commit -m G file1 &&\n+\tgit tag G &&\n+\t: > file5 &&\n+\tgit add file5 &&\n+\ttest_tick &&\n+\tgit commit -m H &&\n+\tgit tag H &&\n+\tgit checkout -b branch2 F &&\n+\t: > file6 &&\n+\tgit add file6 &&\n+\ttest_tick &&\n+\tgit commit -m I &&\n+\tgit tag I\n+'\n+\n+# A - B - C - D - E\n+#   \\             \\ \\\n+#     F - G - H -- L \\        -->   L\n+#       \\            |               \\\n+#         I -- G2 -- J -- K           I -- K\n+# G2 = same changes as G\n+test_expect_success 'skip same-resolution merges with -p' '\n+\tgit checkout branch1 &&\n+\t! git merge E &&\n+\techo 23 > file1 &&\n+\tgit add file1 &&\n+\tgit commit -m L &&\n+\tgit checkout branch2 &&\n+\techo 3 > file1 &&\n+\tgit commit -a -m G2 &&\n+\t! git merge E &&\n+\techo 23 > file1 &&\n+\tgit add file1 &&\n+\tgit commit -m J &&\n+\techo file7 > file7 &&\n+\tgit add file7 &&\n+\tgit commit -m K &&\n+\tGIT_EDITOR=: git rebase -i -p branch1 &&\n+\ttest $(git rev-parse branch2^^) = $(git rev-parse branch1) &&\n+\ttest \"23\" = \"$(cat file1)\" &&\n+\ttest \"\" = \"$(cat file6)\" &&\n+\ttest \"file7\" = \"$(cat file7)\" &&\n+\n+\tgit checkout branch1 &&\n+\tgit reset --hard H &&\n+\tgit checkout branch2 &&\n+\tgit reset --hard I\n+'\n+\n+# A - B - C - D - E\n+#   \\             \\ \\\n+#     F - G - H -- L \\        -->   L\n+#       \\            |               \\\n+#         I -- G2 -- J -- K           I -- G2 -- K\n+# G2 = different changes as G\n+test_expect_success 'keep different-resolution merges with -p' '\n+\tgit checkout branch1 &&\n+\t! git merge E &&\n+\techo 23 > file1 &&\n+\tgit add file1 &&\n+\tgit commit -m L &&\n+\tgit checkout branch2 &&\n+\techo 4 > file1 &&\n+\tgit commit -a -m G2 &&\n+\t! git merge E &&\n+\techo 24 > file1 &&\n+\tgit add file1 &&\n+\tgit commit -m J &&\n+\techo file7 > file7 &&\n+\tgit add file7 &&\n+\tgit commit -m K &&\n+\t! GIT_EDITOR=: git rebase -i -p branch1 &&\n+\techo 234 > file1 &&\n+\tgit add file1 &&\n+\tGIT_EDITOR=: git rebase --continue &&\n+\ttest $(git rev-parse branch2^^^) = $(git rev-parse branch1) &&\n+\ttest \"234\" = \"$(cat file1)\" &&\n+\ttest \"\" = \"$(cat file6)\" &&\n+\ttest \"file7\" = \"$(cat file7)\" &&\n+\n+\tgit checkout branch1 &&\n+\tgit reset --hard H &&\n+\tgit checkout branch2 &&\n+\tgit reset --hard I\n+'\n+\n+test_done\n+\n-- \n1.6.0.2\n"}]}