Merge branch 'mz/rebase-tests'
authorJunio C Hamano <gitster@pobox.com>
Sun, 23 Jun 2013 21:53:26 +0000 (14:53 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 23 Jun 2013 21:53:27 +0000 (14:53 -0700)
* mz/rebase-tests:
rebase topology tests: fix commit names on case-insensitive file systems
tests: move test for rebase messages from t3400 to t3406
t3406: modernize style
add tests for rebasing merged history
add tests for rebasing root
add tests for rebasing of empty commits
add tests for rebasing with patch-equivalence present
add simple tests of consistency across rebase types

t/lib-rebase.sh
t/t3400-rebase.sh
t/t3401-rebase-partial.sh [deleted file]
t/t3404-rebase-interactive.sh
t/t3406-rebase-message.sh
t/t3409-rebase-preserve-merges.sh
t/t3421-rebase-topology-linear.sh [new file with mode: 0755]
t/t3425-rebase-topology-merges.sh [new file with mode: 0755]
index 6ccf7970916b58748aedcce7e583eed2dee782d3..4b74ae460b8943c176b6563d374e1663f89a1d7b 100644 (file)
@@ -65,3 +65,36 @@ EOF
        test_set_editor "$(pwd)/fake-editor.sh"
        chmod a+x fake-editor.sh
 }
+
+# checks that the revisions in "$2" represent a linear range with the
+# subjects in "$1"
+test_linear_range () {
+       revlist_merges=$(git rev-list --merges "$2") &&
+       test -z "$revlist_merges" &&
+       expected=$1
+       set -- $(git log --reverse --format=%s "$2")
+       test "$expected" = "$*"
+}
+
+reset_rebase () {
+       test_might_fail git rebase --abort &&
+       git reset --hard &&
+       git clean -f
+}
+
+cherry_pick () {
+       git cherry-pick -n "$2" &&
+       git commit -m "$1" &&
+       git tag "$1"
+}
+
+revert () {
+       git revert -n "$2" &&
+       git commit -m "$1" &&
+       git tag "$1"
+}
+
+make_empty () {
+       git commit --allow-empty -m "$1" &&
+       git tag "$1"
+}
index 6a5ec32a2642253cb6bb8a05ff51fecc44aa4a00..ff86725b7beba19c138b33bd874d31fd540c026e 100755 (executable)
@@ -40,13 +40,6 @@ test_expect_success 'prepare repository with topic branches' '
        echo Side >>C &&
        git add C &&
        git commit -m "Add C" &&
-       git checkout -b nonlinear my-topic-branch &&
-       echo Edit >>B &&
-       git add B &&
-       git commit -m "Modify B" &&
-       git merge side &&
-       git checkout -b upstream-merged-nonlinear &&
-       git merge master &&
        git checkout -f my-topic-branch &&
        git tag topic
 '
@@ -66,28 +59,6 @@ test_expect_success 'rebase against master' '
        git rebase master
 '
 
-test_expect_success 'rebase against master twice' '
-       git rebase master >out &&
-       test_i18ngrep "Current branch my-topic-branch is up to date" out
-'
-
-test_expect_success 'rebase against master twice with --force' '
-       git rebase --force-rebase master >out &&
-       test_i18ngrep "Current branch my-topic-branch is up to date, rebase forced" out
-'
-
-test_expect_success 'rebase against master twice from another branch' '
-       git checkout my-topic-branch^ &&
-       git rebase master my-topic-branch >out &&
-       test_i18ngrep "Current branch my-topic-branch is up to date" out
-'
-
-test_expect_success 'rebase fast-forward to master' '
-       git checkout my-topic-branch^ &&
-       git rebase my-topic-branch >out &&
-       test_i18ngrep "Fast-forwarded HEAD to my-topic-branch" out
-'
-
 test_expect_success 'the rebase operation should not have destroyed author information' '
        ! (git log | grep "Author:" | grep "<>")
 '
@@ -106,31 +77,9 @@ test_expect_success 'rebase from ambiguous branch name' '
        git rebase master
 '
 
-test_expect_success 'rebase after merge master' '
-       git checkout --detach refs/tags/topic &&
-       git branch -D topic &&
-       git reset --hard topic &&
-       git merge master &&
-       git rebase master &&
-       ! (git show | grep "^Merge:")
-'
-
-test_expect_success 'rebase of history with merges is linearized' '
-       git checkout nonlinear &&
-       test 4 = $(git rev-list master.. | wc -l) &&
-       git rebase master &&
-       test 3 = $(git rev-list master.. | wc -l)
-'
-
-test_expect_success 'rebase of history with merges after upstream merge is linearized' '
-       git checkout upstream-merged-nonlinear &&
-       test 5 = $(git rev-list master.. | wc -l) &&
-       git rebase master &&
-       test 3 = $(git rev-list master.. | wc -l)
-'
-
 test_expect_success 'rebase a single mode change' '
        git checkout master &&
+       git branch -D topic &&
        echo 1 >X &&
        git add X &&
        test_tick &&
diff --git a/t/t3401-rebase-partial.sh b/t/t3401-rebase-partial.sh
deleted file mode 100755 (executable)
index 58f4823..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2006 Yann Dirson, based on t3400 by Amos Waterland
-#
-
-test_description='git rebase should detect patches integrated upstream
-
-This test cherry-picks one local change of two into master branch, and
-checks that git rebase succeeds with only the second patch in the
-local branch.
-'
-. ./test-lib.sh
-
-test_expect_success 'prepare repository with topic branch' '
-       test_commit A &&
-       git checkout -b my-topic-branch &&
-       test_commit B &&
-       test_commit C &&
-       git checkout -f master &&
-       test_commit A2 A.t
-'
-
-test_expect_success 'pick top patch from topic branch into master' '
-       git cherry-pick C &&
-       git checkout -f my-topic-branch
-'
-
-test_debug '
-       git cherry master &&
-       git format-patch -k --stdout --full-index master >/dev/null &&
-       gitk --all & sleep 1
-'
-
-test_expect_success 'rebase topic branch against new master and check git am did not get halted' '
-       git rebase master &&
-       test_path_is_missing .git/rebase-apply
-'
-
-test_expect_success 'rebase --merge topic branch that was partially merged upstream' '
-       git reset --hard C &&
-       git rebase --merge master &&
-       test_path_is_missing .git/rebase-merge
-'
-
-test_expect_success 'rebase ignores empty commit' '
-       git reset --hard A &&
-       git commit --allow-empty -m empty &&
-       test_commit D &&
-       git rebase C &&
-       test "$(git log --format=%s C..)" = "D"
-'
-
-test_expect_success 'rebase --keep-empty' '
-       git reset --hard D &&
-       git rebase --keep-empty C &&
-       test "$(git log --format=%s C..)" = "D
-empty"
-'
-
-test_expect_success 'rebase --keep-empty keeps empty even if already in upstream' '
-       git reset --hard A &&
-       git commit --allow-empty -m also-empty &&
-       git rebase --keep-empty D &&
-       test "$(git log --format=%s A..)" = "also-empty
-D
-empty"
-'
-
-test_done
index 79e8d3c5966bad34b317c6e35c301ecfaa3ab648..0d3c57340ebf1de3f8aff05c15e05df81b173b41 100755 (executable)
@@ -477,19 +477,11 @@ test_expect_success 'interrupted squash works as expected (case 2)' '
        test $one = $(git rev-parse HEAD~2)
 '
 
-test_expect_success 'ignore patch if in upstream' '
-       HEAD=$(git rev-parse HEAD) &&
-       git checkout -b has-cherry-picked HEAD^ &&
+test_expect_success '--continue tries to commit, even for "edit"' '
        echo unrelated > file7 &&
        git add file7 &&
        test_tick &&
        git commit -m "unrelated change" &&
-       git cherry-pick $HEAD &&
-       EXPECT_COUNT=1 git rebase -i $HEAD &&
-       test $HEAD = $(git rev-parse HEAD^)
-'
-
-test_expect_success '--continue tries to commit, even for "edit"' '
        parent=$(git rev-parse HEAD^) &&
        test_tick &&
        FAKE_LINES="edit 1" git rebase -i HEAD^ &&
index e6a9a0d436f3a28a7de6eaadd7f1f223ae740c64..0392e36d2364c8149f2a628a0901f113b7a5875f 100755 (executable)
@@ -4,27 +4,17 @@ test_description='messages from rebase operation'
 
 . ./test-lib.sh
 
-quick_one () {
-       echo "$1" >"file$1" &&
-       git add "file$1" &&
-       test_tick &&
-       git commit -m "$1"
-}
+test_expect_success 'setup' '
+       test_commit O fileO &&
+       test_commit X fileX &&
+       test_commit A fileA &&
+       test_commit B fileB &&
+       test_commit Y fileY &&
 
-test_expect_success setup '
-       quick_one O &&
-       git branch topic &&
-       quick_one X &&
-       quick_one A &&
-       quick_one B &&
-       quick_one Y &&
-
-       git checkout topic &&
-       quick_one A &&
-       quick_one B &&
-       quick_one Z &&
+       git checkout -b topic O &&
+       git cherry-pick A B &&
+       test_commit Z fileZ &&
        git tag start
-
 '
 
 cat >expect <<\EOF
@@ -34,12 +24,32 @@ Committed: 0003 Z
 EOF
 
 test_expect_success 'rebase -m' '
-
        git rebase -m master >report &&
        sed -n -e "/^Already applied: /p" \
                -e "/^Committed: /p" report >actual &&
        test_cmp expect actual
+'
+
+test_expect_success 'rebase against master twice' '
+       git rebase master >out &&
+       test_i18ngrep "Current branch topic is up to date" out
+'
+
+test_expect_success 'rebase against master twice with --force' '
+       git rebase --force-rebase master >out &&
+       test_i18ngrep "Current branch topic is up to date, rebase forced" out
+'
+
+test_expect_success 'rebase against master twice from another branch' '
+       git checkout topic^ &&
+       git rebase master topic >out &&
+       test_i18ngrep "Current branch topic is up to date" out
+'
 
+test_expect_success 'rebase fast-forward to master' '
+       git checkout topic^ &&
+       git rebase topic >out &&
+       test_i18ngrep "Fast-forwarded HEAD to topic" out
 '
 
 test_expect_success 'rebase --stat' '
index 6de4e2263f9ec65d2de2e28af6a12d5c1065f686..2e0c36415fe525663fdcc64675fd081c642732ed 100755 (executable)
@@ -11,14 +11,6 @@ Run "git rebase -p" and check that merges are properly carried along
 GIT_AUTHOR_EMAIL=bogus_email_address
 export GIT_AUTHOR_EMAIL
 
-# Clone 1 (trivial merge):
-#
-# A1--A2  <-- origin/master
-#  \   \
-#   B1--M  <-- topic
-#    \
-#     B2  <-- origin/topic
-#
 # Clone 2 (conflicting merge):
 #
 # A1--A2--B3   <-- origin/master
@@ -36,16 +28,6 @@ export GIT_AUTHOR_EMAIL
 #     \--A3    <-- topic2
 #      \
 #       B2     <-- origin/topic
-#
-# Clone 4 (merge using second parent as base):
-#
-# A1--A2--B3   <-- origin/master
-#  \
-#   B1--A3--M  <-- topic
-#    \     /
-#     \--A4    <-- topic2
-#      \
-#       B2     <-- origin/topic
 
 test_expect_success 'setup for merge-preserving rebase' \
        'echo First > A &&
@@ -58,20 +40,6 @@ test_expect_success 'setup for merge-preserving rebase' \
        git checkout -f master &&
        echo Third >> A &&
        git commit -a -m "Modify A2" &&
-
-       git clone ./. clone1 &&
-       (cd clone1 &&
-       git checkout -b topic origin/topic &&
-       git merge origin/master
-       ) &&
-
-       git clone ./. clone4 &&
-       (
-               cd clone4 &&
-               git checkout -b topic origin/topic &&
-               git merge origin/master
-       ) &&
-
        echo Fifth > B &&
        git add B &&
        git commit -m "Add different B" &&
@@ -101,16 +69,6 @@ test_expect_success 'setup for merge-preserving rebase' \
        git commit -a -m "Modify B2"
 '
 
-test_expect_success 'rebase -p fakes interactive rebase' '
-       (
-       cd clone1 &&
-       git fetch &&
-       git rebase -p origin/topic &&
-       test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
-       test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote-tracking branch " | wc -l)
-       )
-'
-
 test_expect_success '--continue works after a conflict' '
        (
        cd clone2 &&
@@ -138,15 +96,4 @@ test_expect_success 'rebase -p preserves no-ff merges' '
        )
 '
 
-test_expect_success 'rebase -p works when base inside second parent' '
-       (
-       cd clone4 &&
-       git fetch &&
-       git rebase -p HEAD^2 &&
-       test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
-       test 1 = $(git rev-list --all --pretty=oneline | grep "Modify B" | wc -l) &&
-       test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote-tracking branch " | wc -l)
-       )
-'
-
 test_done
diff --git a/t/t3421-rebase-topology-linear.sh b/t/t3421-rebase-topology-linear.sh
new file mode 100755 (executable)
index 0000000..9c55cba
--- /dev/null
@@ -0,0 +1,350 @@
+#!/bin/sh
+
+test_description='basic rebase topology tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+# a---b---c
+#      \
+#       d---e
+test_expect_success 'setup' '
+       test_commit a &&
+       test_commit b &&
+       test_commit c &&
+       git checkout b &&
+       test_commit d &&
+       test_commit e
+'
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "simple rebase $*" "
+               reset_rebase &&
+               git rebase $* c e &&
+               test_cmp_rev c HEAD~2 &&
+               test_linear_range 'd e' c..
+       "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* is no-op if upstream is an ancestor" "
+               reset_rebase &&
+               git rebase $* b e &&
+               test_cmp_rev e HEAD
+       "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* -f rewrites even if upstream is an ancestor" "
+               reset_rebase &&
+               git rebase $* -f b e &&
+               ! test_cmp_rev e HEAD &&
+               test_cmp_rev b HEAD~2 &&
+               test_linear_range 'd e' b..
+       "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* fast-forwards from ancestor of upstream" "
+               reset_rebase &&
+               git rebase $* e b &&
+               test_cmp_rev e HEAD
+       "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+#       f
+#      /
+# a---b---c---g---h
+#      \
+#       d---gp--i
+#
+# gp = cherry-picked g
+# h = reverted g
+#
+# Reverted patches are there for tests to be able to check if a commit
+# that introduced the same change as another commit is
+# dropped. Without reverted commits, we could get false positives
+# because applying the patch succeeds, but simply results in no
+# changes.
+test_expect_success 'setup of linear history for range selection tests' '
+       git checkout c &&
+       test_commit g &&
+       revert h g &&
+       git checkout d &&
+       cherry_pick gp g &&
+       test_commit i &&
+       git checkout b &&
+       test_commit f
+'
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* drops patches in upstream" "
+               reset_rebase &&
+               git rebase $* h i &&
+               test_cmp_rev h HEAD~2 &&
+               test_linear_range 'd i' h..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* can drop last patch if in upstream" "
+               reset_rebase &&
+               git rebase $* h gp &&
+               test_cmp_rev h HEAD^ &&
+               test_linear_range 'd' h..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* --onto drops patches in upstream" "
+               reset_rebase &&
+               git rebase $* --onto f h i &&
+               test_cmp_rev f HEAD~2 &&
+               test_linear_range 'd i' f..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* --onto does not drop patches in onto" "
+               reset_rebase &&
+               git rebase $* --onto h f i &&
+               test_cmp_rev h HEAD~3 &&
+               test_linear_range 'd gp i' h..
+       "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+# a---b---c---j!
+#      \
+#       d---k!--l
+#
+# ! = empty
+test_expect_success 'setup of linear history for empty commit tests' '
+       git checkout c &&
+       make_empty j &&
+       git checkout d &&
+       make_empty k &&
+       test_commit l
+'
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* drops empty commit" "
+               reset_rebase &&
+               git rebase $* c l &&
+               test_cmp_rev c HEAD~2 &&
+               test_linear_range 'd l' c..
+       "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* --keep-empty" "
+               reset_rebase &&
+               git rebase $* --keep-empty c l &&
+               test_cmp_rev c HEAD~3 &&
+               test_linear_range 'd k l' c..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* --keep-empty keeps empty even if already in upstream" "
+               reset_rebase &&
+               git rebase $* --keep-empty j l &&
+               test_cmp_rev j HEAD~3 &&
+               test_linear_range 'd k l' j..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase failure -i
+test_run_rebase failure -p
+
+#       m
+#      /
+# a---b---c---g
+#
+# x---y---bp
+#
+# bp = cherry-picked b
+# m = reverted b
+#
+# Reverted patches are there for tests to be able to check if a commit
+# that introduced the same change as another commit is
+# dropped. Without reverted commits, we could get false positives
+# because applying the patch succeeds, but simply results in no
+# changes.
+test_expect_success 'setup of linear history for test involving root' '
+       git checkout b &&
+       revert m b &&
+       git checkout --orphan disjoint &&
+       git rm -rf . &&
+       test_commit x &&
+       test_commit y &&
+       cherry_pick bp b
+'
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* --onto --root" "
+               reset_rebase &&
+               git rebase $* --onto c --root y &&
+               test_cmp_rev c HEAD~2 &&
+               test_linear_range 'x y' c..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* without --onto --root with disjoint history" "
+               reset_rebase &&
+               git rebase $* c y &&
+               test_cmp_rev c HEAD~2 &&
+               test_linear_range 'x y' c..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* --onto --root drops patch in onto" "
+               reset_rebase &&
+               git rebase $* --onto m --root bp &&
+               test_cmp_rev m HEAD~2 &&
+               test_linear_range 'x y' m..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* --onto --root with merge-base does not go to root" "
+               reset_rebase &&
+               git rebase $* --onto m --root g &&
+               test_cmp_rev m HEAD~2 &&
+               test_linear_range 'c g' m..
+       "
+}
+
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* without --onto --root with disjoint history drops patch in onto" "
+               reset_rebase &&
+               git rebase $* m bp &&
+               test_cmp_rev m HEAD~2 &&
+               test_linear_range 'x y' m..
+       "
+}
+test_run_rebase success ''
+test_run_rebase failure -m
+test_run_rebase success -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* --root on linear history is a no-op" "
+               reset_rebase &&
+               git rebase $* --root c &&
+               test_cmp_rev c HEAD
+       "
+}
+test_run_rebase failure ''
+test_run_rebase failure -m
+test_run_rebase failure -i
+test_run_rebase failure -p
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* -f --root on linear history causes re-write" "
+               reset_rebase &&
+               git rebase $* -f --root c &&
+               ! test_cmp_rev a HEAD~2 &&
+               test_linear_range 'a b c' HEAD
+       "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+test_run_rebase success -p
+
+test_done
diff --git a/t/t3425-rebase-topology-merges.sh b/t/t3425-rebase-topology-merges.sh
new file mode 100755 (executable)
index 0000000..1d195fb
--- /dev/null
@@ -0,0 +1,258 @@
+#!/bin/sh
+
+test_description='rebase topology tests with merges'
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+test_revision_subjects () {
+       expected="$1"
+       shift
+       set -- $(git log --format=%s --no-walk=unsorted "$@")
+       test "$expected" = "$*"
+}
+
+# a---b-----------c
+#      \           \
+#       d-------e   \
+#        \       \   \
+#         n---o---w---v
+#              \
+#               z
+test_expect_success 'setup of non-linear-history' '
+       test_commit a &&
+       test_commit b &&
+       test_commit c &&
+       git checkout b &&
+       test_commit d &&
+       test_commit e
+
+       git checkout c &&
+       test_commit g &&
+       revert h g &&
+       git checkout d &&
+       cherry_pick gp g &&
+       test_commit i &&
+       git checkout b &&
+       test_commit f
+
+       git checkout d &&
+       test_commit n &&
+       test_commit o &&
+       test_merge w e &&
+       test_merge v c &&
+       git checkout o &&
+       test_commit z
+'
+
+test_run_rebase () {
+       result=$1
+       shift
+       test_expect_$result "rebase $* after merge from upstream" "
+               reset_rebase &&
+               git rebase $* e w &&
+               test_cmp_rev e HEAD~2 &&
+               test_linear_range 'n o' e..
+       "
+}
+test_run_rebase success ''
+test_run_rebase success -m
+test_run_rebase success -i
+
+test_run_rebase () {
+       result=$1
+       shift
+       expected=$1
+       shift
+       test_expect_$result "rebase $* of non-linear history is linearized in place" "
+               reset_rebase &&
+               git rebase $* d w &&
+               test_cmp_rev d HEAD~3 &&
+               test_linear_range "\'"$expected"\'" d..
+       "
+}
+#TODO: make order consistent across all flavors of rebase
+test_run_rebase success 'e n o' ''
+test_run_rebase success 'e n o' -m
+test_run_rebase success 'n o e' -i
+
+test_run_rebase () {
+       result=$1
+       shift
+       expected=$1
+       shift
+       test_expect_$result "rebase $* of non-linear history is linearized upstream" "
+               reset_rebase &&
+               git rebase $* c w &&
+               test_cmp_rev c HEAD~4 &&
+               test_linear_range "\'"$expected"\'" c..
+       "
+}
+#TODO: make order consistent across all flavors of rebase
+test_run_rebase success 'd e n o' ''
+test_run_rebase success 'd e n o' -m
+test_run_rebase success 'd n o e' -i
+
+test_run_rebase () {
+       result=$1
+       shift
+       expected=$1
+       shift
+       test_expect_$result "rebase $* of non-linear history with merges after upstream merge is linearized" "
+               reset_rebase &&
+               git rebase $* c v &&
+               test_cmp_rev c HEAD~4 &&
+               test_linear_range "\'"$expected"\'" c..
+       "
+}
+#TODO: make order consistent across all flavors of rebase
+test_run_rebase success 'd e n o' ''
+test_run_rebase success 'd e n o' -m
+test_run_rebase success 'd n o e' -i
+
+test_expect_success "rebase -p is no-op in non-linear history" "
+       reset_rebase &&
+       git rebase -p d w &&
+       test_cmp_rev w HEAD
+"
+
+test_expect_success "rebase -p is no-op when base inside second parent" "
+       reset_rebase &&
+       git rebase -p e w &&
+       test_cmp_rev w HEAD
+"
+
+test_expect_failure "rebase -p --root on non-linear history is a no-op" "
+       reset_rebase &&
+       git rebase -p --root w &&
+       test_cmp_rev w HEAD
+"
+
+test_expect_success "rebase -p re-creates merge from side branch" "
+       reset_rebase &&
+       git rebase -p z w &&
+       test_cmp_rev z HEAD^ &&
+       test_cmp_rev w^2 HEAD^2
+"
+
+test_expect_success "rebase -p re-creates internal merge" "
+       reset_rebase &&
+       git rebase -p c w &&
+       test_cmp_rev c HEAD~4 &&
+       test_cmp_rev HEAD^2^ HEAD~3 &&
+       test_revision_subjects 'd n e o w' HEAD~3 HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+test_expect_success "rebase -p can re-create two branches on onto" "
+       reset_rebase &&
+       git rebase -p --onto c d w &&
+       test_cmp_rev c HEAD~3 &&
+       test_cmp_rev c HEAD^2^ &&
+       test_revision_subjects 'n e o w' HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+#       f
+#      /
+# a---b---c---g---h
+#      \
+#       d---gp--i
+#        \       \
+#         e-------u
+#
+# gp = cherry-picked g
+# h = reverted g
+test_expect_success 'setup of non-linear-history for patch-equivalence tests' '
+       git checkout e &&
+       test_merge u i
+'
+
+test_expect_success "rebase -p re-creates history around dropped commit matching upstream" "
+       reset_rebase &&
+       git rebase -p h u &&
+       test_cmp_rev h HEAD~3 &&
+       test_cmp_rev HEAD^2^ HEAD~2 &&
+       test_revision_subjects 'd i e u' HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+test_expect_success "rebase -p --onto in merged history drops patches in upstream" "
+       reset_rebase &&
+       git rebase -p --onto f h u &&
+       test_cmp_rev f HEAD~3 &&
+       test_cmp_rev HEAD^2^ HEAD~2 &&
+       test_revision_subjects 'd i e u' HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+test_expect_success "rebase -p --onto in merged history does not drop patches in onto" "
+       reset_rebase &&
+       git rebase -p --onto h f u &&
+       test_cmp_rev h HEAD~3 &&
+       test_cmp_rev HEAD^2~2 HEAD~2 &&
+       test_revision_subjects 'd gp i e u' HEAD~2 HEAD^2^ HEAD^2 HEAD^ HEAD
+"
+
+# a---b---c---g---h
+#      \
+#       d---gp--s
+#        \   \ /
+#         \   X
+#          \ / \
+#           e---t
+#
+# gp = cherry-picked g
+# h = reverted g
+test_expect_success 'setup of non-linear-history for dropping whole side' '
+       git checkout gp &&
+       test_merge s e &&
+       git checkout e &&
+       test_merge t gp
+'
+
+test_expect_failure "rebase -p drops merge commit when entire first-parent side is dropped" "
+       reset_rebase &&
+       git rebase -p h s &&
+       test_cmp_rev h HEAD~2 &&
+       test_linear_range 'd e' h..
+"
+
+test_expect_success "rebase -p drops merge commit when entire second-parent side is dropped" "
+       reset_rebase &&
+       git rebase -p h t &&
+       test_cmp_rev h HEAD~2 &&
+       test_linear_range 'd e' h..
+"
+
+# a---b---c
+#      \
+#       d---e
+#        \   \
+#         n---r
+#          \
+#           o
+#
+# r = tree-same with n
+test_expect_success 'setup of non-linear-history for empty commits' '
+       git checkout n &&
+       git merge --no-commit e &&
+       git reset n . &&
+       git commit -m r &&
+       git reset --hard &&
+       git clean -f &&
+       git tag r
+'
+
+test_expect_success "rebase -p re-creates empty internal merge commit" "
+       reset_rebase &&
+       git rebase -p c r &&
+       test_cmp_rev c HEAD~3 &&
+       test_cmp_rev HEAD^2^ HEAD~2 &&
+       test_revision_subjects 'd e n r' HEAD~2 HEAD^2 HEAD^ HEAD
+"
+
+test_expect_success "rebase -p re-creates empty merge commit" "
+       reset_rebase &&
+       git rebase -p o r &&
+       test_cmp_rev e HEAD^2 &&
+       test_cmp_rev o HEAD^ &&
+       test_revision_subjects 'r' HEAD
+"
+
+test_done