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 git checkout master &&
43 git merge --no-commit B &&
47 git checkout -b second C &&
50 git checkout master &&
51 git merge --no-commit G &&
56 test_commit conflicting-G G.t
59 test_expect_success
'create completely different structure' '
60 cat >script-from-scratch <<-\EOF &&
75 merge onebranch # Merge the topic branch '\''onebranch'\''
77 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
79 git rebase -i -r A master &&
80 test_cmp_graph <<-\EOF
81 * Merge the topic branch '\''onebranch'\''
95 test_expect_success
'generate correct todo list' '
96 cat >expect <<-\EOF &&
110 reset branch-point # C
112 merge -C 2051b56 E # E
113 merge -C 233d48a H # H
117 grep -v "^#" <.git/ORIGINAL-TODO >output &&
118 test_cmp expect output
121 test_expect_success
'`reset` refuses to overwrite untracked files' '
122 git checkout -b refuse-to-reset &&
123 test_commit dont-overwrite-untracked &&
124 git checkout @{-1} &&
125 : >dont-overwrite-untracked.t &&
126 echo "reset refs/tags/dont-overwrite-untracked" >script-from-scratch &&
127 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
128 test_must_fail git rebase -ir HEAD &&
132 test_expect_success
'failed `merge -C` writes patch (may be rescheduled, too)' '
133 test_when_finished "test_might_fail git rebase --abort" &&
134 git checkout -b conflicting-merge A &&
136 : fail because of conflicting untracked file &&
138 echo "merge -C H G" >script-from-scratch &&
139 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
141 test_must_fail git rebase -ir HEAD &&
142 grep "^merge -C .* G$" .git/rebase-merge/done &&
143 grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
144 test_path_is_file .git/rebase-merge/patch &&
146 : fail because of merge conflict &&
147 rm G.t .git/rebase-merge/patch &&
148 git reset --hard conflicting-G &&
149 test_must_fail git rebase --continue &&
150 ! grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
151 test_path_is_file .git/rebase-merge/patch
155 test_expect_success
'failed `merge <branch>` does not crash' '
156 test_when_finished "test_might_fail git rebase --abort" &&
157 git checkout conflicting-G &&
159 echo "merge G" >script-from-scratch &&
160 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
162 test_must_fail git rebase -ir HEAD &&
163 ! grep "^merge G$" .git/rebase-merge/git-rebase-todo &&
164 grep "^Merge branch ${SQ}G${SQ}$" .git/rebase-merge/message
167 test_expect_success
'fast-forward merge -c still rewords' '
168 git checkout -b fast-forward-merge-c H &&
171 FAKE_COMMIT_MESSAGE=edited \
172 GIT_SEQUENCE_EDITOR="echo merge -c H G >" \
175 echo edited >expected &&
176 git log --pretty=format:%B -1 >actual &&
177 test_cmp expected actual
180 test_expect_success
'with a branch tip that was cherry-picked already' '
181 git checkout -b already-upstream master &&
182 base="$(git rev-parse --verify HEAD)" &&
186 git reset --hard $base &&
189 git merge -m "Merge branch A" A2 &&
191 git checkout -b upstream-with-a2 $base &&
193 git cherry-pick A2 &&
195 git checkout already-upstream &&
197 git rebase -i -r upstream-with-a2 &&
198 test_cmp_graph upstream-with-a2.. <<-\EOF
208 test_expect_success
'do not rebase cousins unless asked for' '
209 git checkout -b cousins master &&
210 before="$(git rev-parse --verify HEAD)" &&
212 git rebase -r HEAD^ &&
213 test_cmp_rev HEAD $before &&
215 git rebase --rebase-merges=rebase-cousins HEAD^ &&
216 test_cmp_graph HEAD^.. <<-\EOF
217 * Merge the topic branch '\''onebranch'\''
226 test_expect_success
'refs/rewritten/* is worktree-local' '
227 git worktree add wt &&
228 cat >wt/script-from-scratch <<-\EOF &&
230 exec GIT_DIR=../.git git rev-parse --verify refs/rewritten/xyz >a || :
231 exec git rev-parse --verify refs/rewritten/xyz >b
234 test_config -C wt sequence.editor \""$PWD"/replace-editor.sh\" &&
235 git -C wt rebase -i HEAD &&
236 test_must_be_empty wt/a &&
237 test_cmp_rev HEAD "$(cat wt/b)"
240 test_expect_success
'--abort cleans up refs/rewritten' '
241 git checkout -b abort-cleans-refs-rewritten H &&
242 GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
243 git rev-parse --verify refs/rewritten/onto &&
244 git rebase --abort &&
245 test_must_fail git rev-parse --verify refs/rewritten/onto
248 test_expect_success
'--quit cleans up refs/rewritten' '
249 git checkout -b quit-cleans-refs-rewritten H &&
250 GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
251 git rev-parse --verify refs/rewritten/onto &&
253 test_must_fail git rev-parse --verify refs/rewritten/onto
256 test_expect_success
'post-rewrite hook and fixups work for merges' '
257 git checkout -b post-rewrite H &&
259 git reset --hard HEAD^ &&
261 git merge -m "to fix up" same1 &&
262 echo same old same old >same2.t &&
264 git commit --fixup HEAD same2.t &&
265 fixup="$(git rev-parse HEAD)" &&
267 mkdir -p .git/hooks &&
268 test_when_finished "rm .git/hooks/post-rewrite" &&
269 echo "cat >actual" | write_script .git/hooks/post-rewrite &&
272 git rebase -i --autosquash -r HEAD^^^ &&
273 printf "%s %s\n%s %s\n%s %s\n%s %s\n" >expect $(git rev-parse \
278 test_cmp expect actual
281 test_expect_success
'refuse to merge ancestors of HEAD' '
282 echo "merge HEAD^" >script-from-scratch &&
283 test_config -C wt sequence.editor \""$PWD"/replace-editor.sh\" &&
284 before="$(git rev-parse HEAD)" &&
285 git rebase -i HEAD &&
286 test_cmp_rev HEAD $before
289 test_expect_success
'root commits' '
290 git checkout --orphan unrelated &&
291 (GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
292 test_commit second-root) &&
293 test_commit third-root &&
294 cat >script-from-scratch <<-\EOF &&
299 merge first-branch # Merge the 3rd root
301 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
303 git rebase -i --force-rebase --root -r &&
304 test "Parsnip" = "$(git show -s --format=%an HEAD^)" &&
305 test $(git rev-parse second-root^0) != $(git rev-parse HEAD^) &&
306 test $(git rev-parse second-root:second-root.t) = \
307 $(git rev-parse HEAD^:second-root.t) &&
308 test_cmp_graph HEAD <<-\EOF &&
315 : fast forward if possible &&
316 before="$(git rev-parse --verify HEAD)" &&
317 test_might_fail git config --unset sequence.editor &&
319 git rebase -i --root -r &&
320 test_cmp_rev HEAD $before
323 test_expect_success
'a "merge" into a root commit is a fast-forward' '
324 head=$(git rev-parse HEAD) &&
325 cat >script-from-scratch <<-EOF &&
329 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
331 git rebase -i -r HEAD^ &&
332 test_cmp_rev HEAD $head
335 test_expect_success
'A root commit can be a cousin, treat it that way' '
336 git checkout --orphan khnum &&
338 git checkout -b asherah master &&
339 test_commit shamkat &&
340 git merge --allow-unrelated-histories khnum &&
342 git rebase -f -r HEAD^ &&
343 ! test_cmp_rev HEAD^2 khnum &&
344 test_cmp_graph HEAD^.. <<-\EOF &&
345 * Merge branch '\''khnum'\'' into asherah
351 git rebase --rebase-merges=rebase-cousins HEAD^ &&
352 test_cmp_graph HEAD^.. <<-\EOF
353 * Merge branch '\''khnum'\'' into asherah
361 test_expect_success
'labels that are object IDs are rewritten' '
362 git checkout -b third B &&
364 third=$(git rev-parse HEAD) &&
365 git checkout -b labels master &&
366 git merge --no-commit third &&
368 git commit -m "Merge commit '\''$third'\'' into labels" &&
369 echo noop >script-from-scratch &&
370 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
372 git rebase -i -r A &&
373 grep "^label $third-" .git/ORIGINAL-TODO &&
374 ! grep "^label $third$" .git/ORIGINAL-TODO
377 test_expect_success
'octopus merges' '
378 git checkout -b three &&
379 test_commit before-octopus &&
381 git checkout -b two HEAD^ &&
383 git checkout -b one HEAD^ &&
386 (GIT_AUTHOR_NAME="Hank" GIT_AUTHOR_EMAIL="hank@sea.world" \
387 git merge -m "Tüntenfüsch" two three) &&
389 : fast forward if possible &&
390 before="$(git rev-parse --verify HEAD)" &&
392 git rebase -i -r HEAD^^ &&
393 test_cmp_rev HEAD $before &&
396 git rebase -i --force-rebase -r HEAD^^ &&
397 test "Hank" = "$(git show -s --format=%an HEAD)" &&
398 test "$before" != $(git rev-parse HEAD) &&
399 test_cmp_graph HEAD^^.. <<-\EOF
411 test_expect_success
'with --autosquash and --exec' '
412 git checkout -b with-exec H &&
415 git commit --fixup B B.t &&
416 write_script show.sh <<-\EOF &&
417 subject="$(git show -s --format=%s HEAD)"
418 content="$(git diff HEAD^! | tail -n 1)"
419 echo "$subject: $content"
422 git rebase -ir --autosquash --exec ./show.sh A >actual &&
423 grep "B: +Booh" actual &&
424 grep "E: +Booh" actual &&
428 test_expect_success
'--continue after resolving conflicts after a merge' '
429 git checkout -b already-has-g E &&
430 git cherry-pick E..G &&
433 git checkout -b conflicts-in-merge H &&
434 test_commit H2 H2.t conflicts H2-conflict &&
435 test_must_fail git rebase -r already-has-g &&
436 grep conflicts H2.t &&
437 echo resolved >H2.t &&
439 git rebase --continue &&
440 test_must_fail git rev-parse --verify HEAD^2 &&
441 test_path_is_missing .git/MERGE_HEAD