diff options
Diffstat (limited to 't/t3404-rebase-interactive.sh')
-rwxr-xr-x | t/t3404-rebase-interactive.sh | 410 |
1 files changed, 380 insertions, 30 deletions
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 4e3513709e..8078db6856 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -7,27 +7,38 @@ test_description='git rebase interactive This test runs git rebase "interactively", by faking an edit, and verifies that the result still makes sense. + +Initial setup: + + one - two - three - four (conflict-branch) + / + A - B - C - D - E (master) + | \ + | F - G - H (branch1) + | \ + |\ I (branch2) + | \ + | J - K - L - M (no-conflict-branch) + \ + N - O - P (no-ff-branch) + + where A, B, D and G all touch file1, and one, two, three, four all + touch file "conflict". ' . ./test-lib.sh . "$TEST_DIRECTORY"/lib-rebase.sh +test_cmp_rev () { + git rev-parse --verify "$1" >expect.rev && + git rev-parse --verify "$2" >actual.rev && + test_cmp expect.rev actual.rev +} + set_fake_editor -# Set up the repository like this: -# -# one - two - three - four (conflict-branch) -# / -# A - B - C - D - E (master) -# | \ -# | F - G - H (branch1) -# | \ -# \ I (branch2) -# \ -# J - K - L - M (no-conflict-branch) -# -# where A, B, D and G all touch file1, and one, two, three, four all -# touch file "conflict". +# WARNING: Modifications to the initial repository can change the SHA ID used +# in the expect2 file for the 'stop on conflicting pick' test. test_expect_success 'setup' ' test_commit A file1 && @@ -40,17 +51,71 @@ test_expect_success 'setup' ' test_commit G file1 && test_commit H file5 && git checkout -b branch2 F && - test_commit I file6 + test_commit I file6 && git checkout -b conflict-branch A && - for n in one two three four - do - test_commit $n conflict - done && + test_commit one conflict && + test_commit two conflict && + test_commit three conflict && + test_commit four conflict && git checkout -b no-conflict-branch A && - for n in J K L M - do - test_commit $n file$n - done + test_commit J fileJ && + test_commit K fileK && + test_commit L fileL && + test_commit M fileM && + git checkout -b no-ff-branch A && + test_commit N fileN && + test_commit O fileO && + test_commit P fileP +' + +# "exec" commands are ran with the user shell by default, but this may +# be non-POSIX. For example, if SHELL=zsh then ">file" doesn't work +# to create a file. Unseting SHELL avoids such non-portable behavior +# in tests. It must be exported for it to take effect where needed. +SHELL= +export SHELL + +test_expect_success 'rebase -i with the exec command' ' + git checkout master && + ( + FAKE_LINES="1 exec_>touch-one + 2 exec_>touch-two exec_false exec_>touch-three + 3 4 exec_>\"touch-file__name_with_spaces\";_>touch-after-semicolon 5" && + export FAKE_LINES && + test_must_fail git rebase -i A + ) && + test_path_is_file touch-one && + test_path_is_file touch-two && + test_path_is_missing touch-three " (should have stopped before)" && + test_cmp_rev C HEAD && + git rebase --continue && + test_path_is_file touch-three && + test_path_is_file "touch-file name with spaces" && + test_path_is_file touch-after-semicolon && + test_cmp_rev master HEAD && + rm -f touch-* +' + +test_expect_success 'rebase -i with the exec command runs from tree root' ' + git checkout master && + mkdir subdir && (cd subdir && + FAKE_LINES="1 exec_>touch-subdir" \ + git rebase -i HEAD^ + ) && + test_path_is_file touch-subdir && + rm -fr subdir +' + +test_expect_success 'rebase -i with the exec command checks tree cleanness' ' + git checkout master && + ( + FAKE_LINES="exec_echo_foo_>file1 1" && + export FAKE_LINES && + test_must_fail git rebase -i HEAD^ + ) && + test_cmp_rev master^ HEAD && + git reset --hard && + git rebase --continue ' test_expect_success 'no changes are a nop' ' @@ -113,7 +178,7 @@ cat > expect2 << EOF D ======= G ->>>>>>> 51047de... G +>>>>>>> 5d18e54... G EOF test_expect_success 'stop on conflicting pick' ' @@ -132,7 +197,18 @@ test_expect_success 'abort' ' git rebase --abort && test $(git rev-parse new-branch1) = $(git rev-parse HEAD) && test "$(git symbolic-ref -q HEAD)" = "refs/heads/branch1" && - ! test -d .git/rebase-merge + test_path_is_missing .git/rebase-merge +' + +test_expect_success 'abort with error when new base cannot be checked out' ' + git rm --cached file1 && + git commit -m "remove file in base" && + test_must_fail git rebase -i master > output 2>&1 && + grep "The following untracked working tree files would be overwritten by checkout:" \ + output && + grep "file1" output && + test_path_is_missing .git/rebase-merge && + git reset --hard HEAD^ ' test_expect_success 'retain authorship' ' @@ -170,6 +246,13 @@ test_expect_success '-p handles "no changes" gracefully' ' test $HEAD = $(git rev-parse HEAD) ' +test_expect_failure 'exchange two commits with -p' ' + git checkout H && + FAKE_LINES="2 1" git rebase -i -p HEAD~2 && + test H = $(git cat-file commit HEAD^ | sed -ne \$p) && + test G = $(git cat-file commit HEAD | sed -ne \$p) +' + test_expect_success 'preserve merges with -p' ' git checkout -b to-be-preserved master^ && : > unrelated-file && @@ -213,7 +296,7 @@ test_expect_success 'preserve merges with -p' ' ' test_expect_success 'edit ancestor with -p' ' - FAKE_LINES="1 edit 2 3 4" git rebase -i -p HEAD~3 && + FAKE_LINES="1 2 edit 3 4" git rebase -i -p HEAD~3 && echo 2 > unrelated-file && test_tick && git commit -m L2-modified --amend unrelated-file && @@ -235,13 +318,13 @@ test_expect_success '--continue tries to commit' ' ' test_expect_success 'verbose flag is heeded, even after --continue' ' - git reset --hard HEAD@{1} && + git reset --hard master@{1} && test_tick && test_must_fail git rebase -v -i --onto new-branch1 HEAD^ && echo resolved > file1 && git add file1 && git rebase --continue > output && - grep "^ file1 | 2 +-$" output + grep "^ file1 | 2 +-$" output ' test_expect_success 'multi-squash only fires up editor once' ' @@ -445,6 +528,20 @@ test_expect_success 'auto-amend only edited commits after "edit"' ' git rebase --abort ' +test_expect_success 'clean error after failed "exec"' ' + test_tick && + test_when_finished "git rebase --abort || :" && + ( + FAKE_LINES="1 exec_false" && + export FAKE_LINES && + test_must_fail git rebase -i HEAD^ + ) && + echo "edited again" > file7 && + git add file7 && + test_must_fail git rebase --continue 2>error && + grep "You have staged changes in your working tree." error +' + test_expect_success 'rebase a detached HEAD' ' grandparent=$(git rev-parse HEAD~2) && git checkout $(git rev-parse HEAD) && @@ -495,7 +592,7 @@ test_expect_success 'do "noop" when there is nothing to cherry-pick' ' git checkout -b branch4 HEAD && GIT_EDITOR=: git commit --amend \ - --author="Somebody else <somebody@else.com>" + --author="Somebody else <somebody@else.com>" && test $(git rev-parse branch3) != $(git rev-parse branch4) && git rebase -i branch3 && test $(git rev-parse branch3) = $(git rev-parse branch4) @@ -510,7 +607,7 @@ test_expect_success 'submodule rebase setup' ' git add elif && git commit -m "submodule initial" ) && echo 1 >file1 && - git add file1 sub + git add file1 sub && test_tick && git commit -m "One" && echo 2 >file1 && @@ -528,8 +625,38 @@ test_expect_success 'submodule rebase -i' ' FAKE_LINES="1 squash 2 3" git rebase -i A ' +test_expect_success 'submodule conflict setup' ' + git tag submodule-base && + git checkout HEAD^ && + ( + cd sub && git checkout HEAD^ && echo 4 >elif && + git add elif && git commit -m "submodule conflict" + ) && + git add sub && + test_tick && + git commit -m "Conflict in submodule" && + git tag submodule-topic +' + +test_expect_success 'rebase -i continue with only submodule staged' ' + test_must_fail git rebase -i submodule-base && + git add sub && + git rebase --continue && + test $(git rev-parse submodule-base) != $(git rev-parse HEAD) +' + +test_expect_success 'rebase -i continue with unstaged submodule' ' + git checkout submodule-topic && + git reset --hard && + test_must_fail git rebase -i submodule-base && + git reset && + git rebase --continue && + test $(git rev-parse submodule-base) = $(git rev-parse HEAD) +' + test_expect_success 'avoid unnecessary reset' ' git checkout master && + git reset --hard && test-chmtime =123456789 file3 && git update-index --refresh && HEAD=$(git rev-parse HEAD) && @@ -553,4 +680,227 @@ test_expect_success 'reword' ' git show HEAD~2 | grep "C changed" ' +test_expect_success 'rebase -i can copy notes' ' + git config notes.rewrite.rebase true && + git config notes.rewriteRef "refs/notes/*" && + test_commit n1 && + test_commit n2 && + test_commit n3 && + git notes add -m"a note" n3 && + git rebase --onto n1 n2 && + test "a note" = "$(git notes show HEAD)" +' + +cat >expect <<EOF +an earlier note + +a note +EOF + +test_expect_success 'rebase -i can copy notes over a fixup' ' + git reset --hard n3 && + git notes add -m"an earlier note" n2 && + GIT_NOTES_REWRITE_MODE=concatenate FAKE_LINES="1 fixup 2" git rebase -i n1 && + git notes show > output && + test_cmp expect output +' + +test_expect_success 'rebase while detaching HEAD' ' + git symbolic-ref HEAD && + grandparent=$(git rev-parse HEAD~2) && + test_tick && + FAKE_LINES="2 1" git rebase -i HEAD~2 HEAD^0 && + test $grandparent = $(git rev-parse HEAD~2) && + test_must_fail git symbolic-ref HEAD +' + +test_tick # Ensure that the rebased commits get a different timestamp. +test_expect_success 'always cherry-pick with --no-ff' ' + git checkout no-ff-branch && + git tag original-no-ff-branch && + git rebase -i --no-ff A && + touch empty && + for p in 0 1 2 + do + test ! $(git rev-parse HEAD~$p) = $(git rev-parse original-no-ff-branch~$p) && + git diff HEAD~$p original-no-ff-branch~$p > out && + test_cmp empty out + done && + test $(git rev-parse HEAD~3) = $(git rev-parse original-no-ff-branch~3) && + git diff HEAD~3 original-no-ff-branch~3 > out && + test_cmp empty out +' + +test_expect_success 'set up commits with funny messages' ' + git checkout -b funny A && + echo >>file1 && + test_tick && + git commit -a -m "end with slash\\" && + echo >>file1 && + test_tick && + git commit -a -m "something (\000) that looks like octal" && + echo >>file1 && + test_tick && + git commit -a -m "something (\n) that looks like a newline" && + echo >>file1 && + test_tick && + git commit -a -m "another commit" +' + +test_expect_success 'rebase-i history with funny messages' ' + git rev-list A..funny >expect && + test_tick && + FAKE_LINES="1 2 3 4" git rebase -i A && + git rev-list A.. >actual && + test_cmp expect actual +' + + +test_expect_success 'prepare for rebase -i --exec' ' + git checkout master && + git checkout -b execute && + test_commit one_exec main.txt one_exec && + test_commit two_exec main.txt two_exec && + test_commit three_exec main.txt three_exec +' + + +test_expect_success 'running "git rebase -i --exec git show HEAD"' ' + git rebase -i --exec "git show HEAD" HEAD~2 >actual && + ( + FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" && + export FAKE_LINES && + git rebase -i HEAD~2 >expect + ) && + sed -e "1,9d" expect >expected && + test_cmp expected actual +' + + +test_expect_success 'running "git rebase --exec git show HEAD -i"' ' + git reset --hard execute && + git rebase --exec "git show HEAD" -i HEAD~2 >actual && + ( + FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" && + export FAKE_LINES && + git rebase -i HEAD~2 >expect + ) && + sed -e "1,9d" expect >expected && + test_cmp expected actual +' + + +test_expect_success 'running "git rebase -ix git show HEAD"' ' + git reset --hard execute && + git rebase -ix "git show HEAD" HEAD~2 >actual && + ( + FAKE_LINES="1 exec_git_show_HEAD 2 exec_git_show_HEAD" && + export FAKE_LINES && + git rebase -i HEAD~2 >expect + ) && + sed -e "1,9d" expect >expected && + test_cmp expected actual +' + + +test_expect_success 'rebase -ix with several <CMD>' ' + git reset --hard execute && + git rebase -ix "git show HEAD; pwd" HEAD~2 >actual && + ( + FAKE_LINES="1 exec_git_show_HEAD;_pwd 2 exec_git_show_HEAD;_pwd" && + export FAKE_LINES && + git rebase -i HEAD~2 >expect + ) && + sed -e "1,9d" expect >expected && + test_cmp expected actual +' + + +test_expect_success 'rebase -ix with several instances of --exec' ' + git reset --hard execute && + git rebase -i --exec "git show HEAD" --exec "pwd" HEAD~2 >actual && + ( + FAKE_LINES="1 exec_git_show_HEAD exec_pwd 2 + exec_git_show_HEAD exec_pwd" && + export FAKE_LINES && + git rebase -i HEAD~2 >expect + ) && + sed -e "1,11d" expect >expected && + test_cmp expected actual +' + + +test_expect_success 'rebase -ix with --autosquash' ' + git reset --hard execute && + git checkout -b autosquash && + echo second >second.txt && + git add second.txt && + git commit -m "fixup! two_exec" && + echo bis >bis.txt && + git add bis.txt && + git commit -m "fixup! two_exec" && + ( + git checkout -b autosquash_actual && + git rebase -i --exec "git show HEAD" --autosquash HEAD~4 >actual + ) && + git checkout autosquash && + ( + git checkout -b autosquash_expected && + FAKE_LINES="1 fixup 3 fixup 4 exec_git_show_HEAD 2 exec_git_show_HEAD" && + export FAKE_LINES && + git rebase -i HEAD~4 >expect + ) && + sed -e "1,13d" expect >expected && + test_cmp expected actual +' + + +test_expect_success 'rebase --exec without -i shows error message' ' + git reset --hard execute && + test_must_fail git rebase --exec "git show HEAD" HEAD~2 2>actual && + echo "--exec option must be used with --interactive option" >expected && + test_i18ncmp expected actual +' + + +test_expect_success 'rebase -i --exec without <CMD>' ' + git reset --hard execute && + test_must_fail git rebase -i --exec 2>tmp && + sed -e "1d" tmp >actual && + test_must_fail git rebase -h >expected && + test_cmp expected actual && + git checkout master +' + +test_expect_success 'rebase -i --root re-order and drop commits' ' + git checkout E && + FAKE_LINES="3 1 2 5" git rebase -i --root && + test E = $(git cat-file commit HEAD | sed -ne \$p) && + test B = $(git cat-file commit HEAD^ | sed -ne \$p) && + test A = $(git cat-file commit HEAD^^ | sed -ne \$p) && + test C = $(git cat-file commit HEAD^^^ | sed -ne \$p) && + test 0 = $(git cat-file commit HEAD^^^ | grep -c ^parent\ ) +' + +test_expect_success 'rebase -i --root retain root commit author and message' ' + git checkout A && + echo B >file7 && + git add file7 && + GIT_AUTHOR_NAME="Twerp Snog" git commit -m "different author" && + FAKE_LINES="2" git rebase -i --root && + git cat-file commit HEAD | grep -q "^author Twerp Snog" && + git cat-file commit HEAD | grep -q "^different author$" +' + +test_expect_success 'rebase -i --root temporary sentinel commit' ' + git checkout B && + ( + FAKE_LINES="2" && + export FAKE_LINES && + test_must_fail git rebase -i --root + ) && + git cat-file commit HEAD | grep "^tree 4b825dc642cb" && + git rebase --abort +' + test_done |