3 # Copyright (c) 2018 Johannes E. Schindelin
6 test_description='git rebase -i --rebase-merges
8 This test runs git rebase "interactively", retaining the branch structure by
9 recreating merge commits.
15 A - C - D - E - H (master)
22 . "$TEST_DIRECTORY"/lib-rebase.sh
26 git log --graph --boundary --format=%s "$@" >output &&
27 sed "s/ *$//" <output >output.trimmed &&
28 test_cmp expect output.trimmed
31 test_expect_success 'setup' '
32 write_script replace-editor.sh <<-\EOF &&
33 mv "$1" "$(git rev-parse --git-path ORIGINAL-TODO)"
34 cp script-from-scratch "$1"
38 git checkout -b first &&
40 b=$(git rev-parse --short HEAD) &&
41 git checkout master &&
43 c=$(git rev-parse --short HEAD) &&
45 d=$(git rev-parse --short HEAD) &&
46 git merge --no-commit B &&
50 e=$(git rev-parse --short HEAD) &&
51 git checkout -b second C &&
53 f=$(git rev-parse --short HEAD) &&
55 g=$(git rev-parse --short HEAD) &&
56 git checkout master &&
57 git merge --no-commit G &&
60 h=$(git rev-parse --short HEAD) &&
63 test_commit conflicting-G G.t
66 test_expect_success 'create completely different structure' '
67 cat >script-from-scratch <<-\EOF &&
82 merge onebranch # Merge the topic branch '\''onebranch'\''
84 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
86 git rebase -i -r A master &&
87 test_cmp_graph <<-\EOF
88 * Merge the topic branch '\''onebranch'\''
102 test_expect_success 'generate correct todo list' '
103 cat >expect <<-EOF &&
117 reset branch-point # C
124 grep -v "^#" <.git/ORIGINAL-TODO >output &&
125 test_cmp expect output
128 test_expect_success '`reset` refuses to overwrite untracked files' '
129 git checkout -b refuse-to-reset &&
130 test_commit dont-overwrite-untracked &&
131 git checkout @{-1} &&
132 : >dont-overwrite-untracked.t &&
133 echo "reset refs/tags/dont-overwrite-untracked" >script-from-scratch &&
134 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
135 test_must_fail git rebase -ir HEAD &&
139 test_expect_success 'failed `merge -C` writes patch (may be rescheduled, too)' '
140 test_when_finished "test_might_fail git rebase --abort" &&
141 git checkout -b conflicting-merge A &&
143 : fail because of conflicting untracked file &&
145 echo "merge -C H G" >script-from-scratch &&
146 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
148 test_must_fail git rebase -ir HEAD &&
149 grep "^merge -C .* G$" .git/rebase-merge/done &&
150 grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
151 test_path_is_file .git/rebase-merge/patch &&
153 : fail because of merge conflict &&
154 rm G.t .git/rebase-merge/patch &&
155 git reset --hard conflicting-G &&
156 test_must_fail git rebase --continue &&
157 ! grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
158 test_path_is_file .git/rebase-merge/patch
161 test_expect_success 'failed `merge <branch>` does not crash' '
162 test_when_finished "test_might_fail git rebase --abort" &&
163 git checkout conflicting-G &&
165 echo "merge G" >script-from-scratch &&
166 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
168 test_must_fail git rebase -ir HEAD &&
169 ! grep "^merge G$" .git/rebase-merge/git-rebase-todo &&
170 grep "^Merge branch ${SQ}G${SQ}$" .git/rebase-merge/message
173 test_expect_success 'fast-forward merge -c still rewords' '
174 git checkout -b fast-forward-merge-c H &&
177 FAKE_COMMIT_MESSAGE=edited \
178 GIT_SEQUENCE_EDITOR="echo merge -c H G >" \
181 echo edited >expected &&
182 git log --pretty=format:%B -1 >actual &&
183 test_cmp expected actual
186 test_expect_success 'with a branch tip that was cherry-picked already' '
187 git checkout -b already-upstream master &&
188 base="$(git rev-parse --verify HEAD)" &&
192 git reset --hard $base &&
195 git merge -m "Merge branch A" A2 &&
197 git checkout -b upstream-with-a2 $base &&
199 git cherry-pick A2 &&
201 git checkout already-upstream &&
203 git rebase -i -r upstream-with-a2 &&
204 test_cmp_graph upstream-with-a2.. <<-\EOF
214 test_expect_success 'do not rebase cousins unless asked for' '
215 git checkout -b cousins master &&
216 before="$(git rev-parse --verify HEAD)" &&
218 git rebase -r HEAD^ &&
219 test_cmp_rev HEAD $before &&
221 git rebase --rebase-merges=rebase-cousins HEAD^ &&
222 test_cmp_graph HEAD^.. <<-\EOF
223 * Merge the topic branch '\''onebranch'\''
232 test_expect_success 'refs/rewritten/* is worktree-local' '
233 git worktree add wt &&
234 cat >wt/script-from-scratch <<-\EOF &&
236 exec GIT_DIR=../.git git rev-parse --verify refs/rewritten/xyz >a || :
237 exec git rev-parse --verify refs/rewritten/xyz >b
240 test_config -C wt sequence.editor \""$PWD"/replace-editor.sh\" &&
241 git -C wt rebase -i HEAD &&
242 test_must_be_empty wt/a &&
243 test_cmp_rev HEAD "$(cat wt/b)"
246 test_expect_success '--abort cleans up refs/rewritten' '
247 git checkout -b abort-cleans-refs-rewritten H &&
248 GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
249 git rev-parse --verify refs/rewritten/onto &&
250 git rebase --abort &&
251 test_must_fail git rev-parse --verify refs/rewritten/onto
254 test_expect_success '--quit cleans up refs/rewritten' '
255 git checkout -b quit-cleans-refs-rewritten H &&
256 GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
257 git rev-parse --verify refs/rewritten/onto &&
259 test_must_fail git rev-parse --verify refs/rewritten/onto
262 test_expect_success 'post-rewrite hook and fixups work for merges' '
263 git checkout -b post-rewrite H &&
265 git reset --hard HEAD^ &&
267 git merge -m "to fix up" same1 &&
268 echo same old same old >same2.t &&
270 git commit --fixup HEAD same2.t &&
271 fixup="$(git rev-parse HEAD)" &&
273 mkdir -p .git/hooks &&
274 test_when_finished "rm .git/hooks/post-rewrite" &&
275 echo "cat >actual" | write_script .git/hooks/post-rewrite &&
278 git rebase -i --autosquash -r HEAD^^^ &&
279 printf "%s %s\n%s %s\n%s %s\n%s %s\n" >expect $(git rev-parse \
284 test_cmp expect actual
287 test_expect_success 'refuse to merge ancestors of HEAD' '
288 echo "merge HEAD^" >script-from-scratch &&
289 test_config -C wt sequence.editor \""$PWD"/replace-editor.sh\" &&
290 before="$(git rev-parse HEAD)" &&
291 git rebase -i HEAD &&
292 test_cmp_rev HEAD $before
295 test_expect_success 'root commits' '
296 git checkout --orphan unrelated &&
297 (GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
298 test_commit second-root) &&
299 test_commit third-root &&
300 cat >script-from-scratch <<-\EOF &&
305 merge first-branch # Merge the 3rd root
307 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
309 git rebase -i --force-rebase --root -r &&
310 test "Parsnip" = "$(git show -s --format=%an HEAD^)" &&
311 test $(git rev-parse second-root^0) != $(git rev-parse HEAD^) &&
312 test $(git rev-parse second-root:second-root.t) = \
313 $(git rev-parse HEAD^:second-root.t) &&
314 test_cmp_graph HEAD <<-\EOF &&
321 : fast forward if possible &&
322 before="$(git rev-parse --verify HEAD)" &&
323 test_might_fail git config --unset sequence.editor &&
325 git rebase -i --root -r &&
326 test_cmp_rev HEAD $before
329 test_expect_success 'a "merge" into a root commit is a fast-forward' '
330 head=$(git rev-parse HEAD) &&
331 cat >script-from-scratch <<-EOF &&
335 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
337 git rebase -i -r HEAD^ &&
338 test_cmp_rev HEAD $head
341 test_expect_success 'A root commit can be a cousin, treat it that way' '
342 git checkout --orphan khnum &&
344 git checkout -b asherah master &&
345 test_commit shamkat &&
346 git merge --allow-unrelated-histories khnum &&
348 git rebase -f -r HEAD^ &&
349 test_cmp_rev ! HEAD^2 khnum &&
350 test_cmp_graph HEAD^.. <<-\EOF &&
351 * Merge branch '\''khnum'\'' into asherah
357 git rebase --rebase-merges=rebase-cousins HEAD^ &&
358 test_cmp_graph HEAD^.. <<-\EOF
359 * Merge branch '\''khnum'\'' into asherah
367 test_expect_success 'labels that are object IDs are rewritten' '
368 git checkout -b third B &&
370 third=$(git rev-parse HEAD) &&
371 git checkout -b labels master &&
372 git merge --no-commit third &&
374 git commit -m "Merge commit '\''$third'\'' into labels" &&
375 echo noop >script-from-scratch &&
376 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
378 git rebase -i -r A &&
379 grep "^label $third-" .git/ORIGINAL-TODO &&
380 ! grep "^label $third$" .git/ORIGINAL-TODO
383 test_expect_success 'octopus merges' '
384 git checkout -b three &&
385 test_commit before-octopus &&
387 git checkout -b two HEAD^ &&
389 git checkout -b one HEAD^ &&
392 (GIT_AUTHOR_NAME="Hank" GIT_AUTHOR_EMAIL="hank@sea.world" \
393 git merge -m "Tüntenfüsch" two three) &&
395 : fast forward if possible &&
396 before="$(git rev-parse --verify HEAD)" &&
398 git rebase -i -r HEAD^^ &&
399 test_cmp_rev HEAD $before &&
402 git rebase -i --force-rebase -r HEAD^^ &&
403 test "Hank" = "$(git show -s --format=%an HEAD)" &&
404 test "$before" != $(git rev-parse HEAD) &&
405 test_cmp_graph HEAD^^.. <<-\EOF
417 test_expect_success 'with --autosquash and --exec' '
418 git checkout -b with-exec H &&
421 git commit --fixup B B.t &&
422 write_script show.sh <<-\EOF &&
423 subject="$(git show -s --format=%s HEAD)"
424 content="$(git diff HEAD^! | tail -n 1)"
425 echo "$subject: $content"
428 git rebase -ir --autosquash --exec ./show.sh A >actual &&
429 grep "B: +Booh" actual &&
430 grep "E: +Booh" actual &&
434 test_expect_success '--continue after resolving conflicts after a merge' '
435 git checkout -b already-has-g E &&
436 git cherry-pick E..G &&
439 git checkout -b conflicts-in-merge H &&
440 test_commit H2 H2.t conflicts H2-conflict &&
441 test_must_fail git rebase -r already-has-g &&
442 grep conflicts H2.t &&
443 echo resolved >H2.t &&
445 git rebase --continue &&
446 test_must_fail git rev-parse --verify HEAD^2 &&
447 test_path_is_missing .git/MERGE_HEAD
450 test_expect_success '--rebase-merges with strategies' '
451 git checkout -b with-a-strategy F &&
453 git merge -m "Merge conflicting-G" conflicting-G &&
455 : first, test with a merge strategy option &&
456 git rebase -ir -Xtheirs G &&
457 echo conflicting-G >expect &&
458 test_cmp expect G.t &&
460 : now, try with a merge strategy other than recursive &&
461 git reset --hard @{1} &&
462 write_script git-merge-override <<-\EOF &&
463 echo overridden$1 >>G.t
466 PATH="$PWD:$PATH" git rebase -ir -s override -Xxopt G &&
467 test_write_lines G overridden--xopt >expect &&
471 test_expect_success '--rebase-merges with commit that can generate bad characters for filename' '
472 git checkout -b colon-in-label E &&
473 git merge -m "colon: this should work" G &&
474 git rebase --rebase-merges --force-rebase E
477 test_expect_success '--rebase-merges with message matched with onto label' '
478 git checkout -b onto-label E &&
479 git merge -m onto G &&
480 git rebase --rebase-merges --force-rebase E &&
481 test_cmp_graph <<-\EOF