]> git.ipfire.org Git - thirdparty/git.git/blob - t/t3430-rebase-merges.sh
Merge branch 'jc/doc-clarify-git-default-hash-variable'
[thirdparty/git.git] / t / t3430-rebase-merges.sh
1 #!/bin/sh
2 #
3 # Copyright (c) 2018 Johannes E. Schindelin
4 #
5
6 test_description='git rebase -i --rebase-merges
7
8 This test runs git rebase "interactively", retaining the branch structure by
9 recreating merge commits.
10
11 Initial setup:
12
13 -- B -- (first)
14 / \
15 A - C - D - E - H (main)
16 \ \ /
17 \ F - G (second)
18 \
19 Conflicting-G
20 '
21 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
22 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
23
24 . ./test-lib.sh
25 . "$TEST_DIRECTORY"/lib-rebase.sh
26 . "$TEST_DIRECTORY"/lib-log-graph.sh
27
28 test_cmp_graph () {
29 cat >expect &&
30 lib_test_cmp_graph --boundary --format=%s "$@"
31 }
32
33 test_expect_success 'setup' '
34 write_script replace-editor.sh <<-\EOF &&
35 mv "$1" "$(git rev-parse --git-path ORIGINAL-TODO)"
36 cp script-from-scratch "$1"
37 EOF
38
39 test_commit A &&
40 git checkout -b first &&
41 test_commit B &&
42 b=$(git rev-parse --short HEAD) &&
43 git checkout main &&
44 test_commit C &&
45 c=$(git rev-parse --short HEAD) &&
46 test_commit D &&
47 d=$(git rev-parse --short HEAD) &&
48 git merge --no-commit B &&
49 test_tick &&
50 git commit -m E &&
51 git tag -m E E &&
52 e=$(git rev-parse --short HEAD) &&
53 git checkout -b second C &&
54 test_commit F &&
55 f=$(git rev-parse --short HEAD) &&
56 test_commit G &&
57 g=$(git rev-parse --short HEAD) &&
58 git checkout main &&
59 git merge --no-commit G &&
60 test_tick &&
61 git commit -m H &&
62 h=$(git rev-parse --short HEAD) &&
63 git tag -m H H &&
64 git checkout A &&
65 test_commit conflicting-G G.t
66 '
67
68 test_expect_success 'create completely different structure' '
69 cat >script-from-scratch <<-\EOF &&
70 label onto
71
72 # onebranch
73 pick G
74 pick D
75 label onebranch
76
77 # second
78 reset onto
79 pick B
80 label second
81
82 reset onto
83 merge -C H second
84 merge onebranch # Merge the topic branch '\''onebranch'\''
85 EOF
86 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
87 test_tick &&
88 git rebase -i -r A main &&
89 test_cmp_graph <<-\EOF
90 * Merge the topic branch '\''onebranch'\''
91 |\
92 | * D
93 | * G
94 * | H
95 |\ \
96 | |/
97 |/|
98 | * B
99 |/
100 * A
101 EOF
102 '
103
104 test_expect_success 'generate correct todo list' '
105 cat >expect <<-EOF &&
106 label onto
107
108 reset onto
109 pick $b B
110 label E
111
112 reset onto
113 pick $c C
114 label branch-point
115 pick $f F
116 pick $g G
117 label H
118
119 reset branch-point # C
120 pick $d D
121 merge -C $e E # E
122 merge -C $h H # H
123
124 EOF
125
126 grep -v "^#" <.git/ORIGINAL-TODO >output &&
127 test_cmp expect output
128 '
129
130 test_expect_success '`reset` refuses to overwrite untracked files' '
131 git checkout -b refuse-to-reset &&
132 test_commit dont-overwrite-untracked &&
133 git checkout @{-1} &&
134 : >dont-overwrite-untracked.t &&
135 echo "reset refs/tags/dont-overwrite-untracked" >script-from-scratch &&
136 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
137 test_must_fail git rebase -ir HEAD &&
138 git rebase --abort
139 '
140
141 test_expect_success '`reset` rejects trees' '
142 test_when_finished "test_might_fail git rebase --abort" &&
143 test_must_fail env GIT_SEQUENCE_EDITOR="echo reset A^{tree} >" \
144 git rebase -i B C >out 2>err &&
145 grep "object .* is a tree" err &&
146 test_must_be_empty out
147 '
148
149 test_expect_success '`reset` only looks for labels under refs/rewritten/' '
150 test_when_finished "test_might_fail git rebase --abort" &&
151 git branch refs/rewritten/my-label A &&
152 test_must_fail env GIT_SEQUENCE_EDITOR="echo reset my-label >" \
153 git rebase -i B C >out 2>err &&
154 grep "could not resolve ${SQ}my-label${SQ}" err &&
155 test_must_be_empty out
156 '
157
158 test_expect_success 'failed `merge -C` writes patch (may be rescheduled, too)' '
159 test_when_finished "test_might_fail git rebase --abort" &&
160 git checkout -b conflicting-merge A &&
161
162 : fail because of conflicting untracked file &&
163 >G.t &&
164 echo "merge -C H G" >script-from-scratch &&
165 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
166 test_tick &&
167 test_must_fail git rebase -ir HEAD &&
168 grep "^merge -C .* G$" .git/rebase-merge/done &&
169 grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
170 test_path_is_file .git/rebase-merge/patch &&
171
172 : fail because of merge conflict &&
173 rm G.t .git/rebase-merge/patch &&
174 git reset --hard conflicting-G &&
175 test_must_fail git rebase --continue &&
176 ! grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
177 test_path_is_file .git/rebase-merge/patch
178 '
179
180 test_expect_success 'failed `merge <branch>` does not crash' '
181 test_when_finished "test_might_fail git rebase --abort" &&
182 git checkout conflicting-G &&
183
184 echo "merge G" >script-from-scratch &&
185 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
186 test_tick &&
187 test_must_fail git rebase -ir HEAD &&
188 ! grep "^merge G$" .git/rebase-merge/git-rebase-todo &&
189 grep "^Merge branch ${SQ}G${SQ}$" .git/rebase-merge/message
190 '
191
192 test_expect_success 'merge -c commits before rewording and reloads todo-list' '
193 cat >script-from-scratch <<-\EOF &&
194 merge -c E B
195 merge -c H G
196 EOF
197
198 git checkout -b merge-c H &&
199 (
200 set_reword_editor &&
201 GIT_SEQUENCE_EDITOR="\"$PWD/replace-editor.sh\"" \
202 git rebase -i -r D
203 ) &&
204 check_reworded_commits E H
205 '
206
207 test_expect_success 'merge -c rewords when a strategy is given' '
208 git checkout -b merge-c-with-strategy H &&
209 write_script git-merge-override <<-\EOF &&
210 echo overridden$1 >G.t
211 git add G.t
212 EOF
213
214 PATH="$PWD:$PATH" \
215 GIT_SEQUENCE_EDITOR="echo merge -c H G >" \
216 GIT_EDITOR="echo edited >>" \
217 git rebase --no-ff -ir -s override -Xxopt E &&
218 test_write_lines overridden--xopt >expect &&
219 test_cmp expect G.t &&
220 test_write_lines H "" edited "" >expect &&
221 git log --format=%B -1 >actual &&
222 test_cmp expect actual
223
224 '
225 test_expect_success 'with a branch tip that was cherry-picked already' '
226 git checkout -b already-upstream main &&
227 base="$(git rev-parse --verify HEAD)" &&
228
229 test_commit A1 &&
230 test_commit A2 &&
231 git reset --hard $base &&
232 test_commit B1 &&
233 test_tick &&
234 git merge -m "Merge branch A" A2 &&
235
236 git checkout -b upstream-with-a2 $base &&
237 test_tick &&
238 git cherry-pick A2 &&
239
240 git checkout already-upstream &&
241 test_tick &&
242 git rebase -i -r upstream-with-a2 &&
243 test_cmp_graph upstream-with-a2.. <<-\EOF
244 * Merge branch A
245 |\
246 | * A1
247 * | B1
248 |/
249 o A2
250 EOF
251 '
252
253 test_expect_success '--no-rebase-merges countermands --rebase-merges' '
254 git checkout -b no-rebase-merges E &&
255 git rebase --rebase-merges --no-rebase-merges C &&
256 test_cmp_graph C.. <<-\EOF
257 * B
258 * D
259 o C
260 EOF
261 '
262
263 test_expect_success 'do not rebase cousins unless asked for' '
264 git checkout -b cousins main &&
265 before="$(git rev-parse --verify HEAD)" &&
266 test_tick &&
267 git rebase -r HEAD^ &&
268 test_cmp_rev HEAD $before &&
269 test_tick &&
270 git rebase --rebase-merges=rebase-cousins HEAD^ &&
271 test_cmp_graph HEAD^.. <<-\EOF
272 * Merge the topic branch '\''onebranch'\''
273 |\
274 | * D
275 | * G
276 |/
277 o H
278 EOF
279 '
280
281 test_expect_success 'rebase.rebaseMerges=rebase-cousins is equivalent to --rebase-merges=rebase-cousins' '
282 test_config rebase.rebaseMerges rebase-cousins &&
283 git checkout -b config-rebase-cousins main &&
284 git rebase HEAD^ &&
285 test_cmp_graph HEAD^.. <<-\EOF
286 * Merge the topic branch '\''onebranch'\''
287 |\
288 | * D
289 | * G
290 |/
291 o H
292 EOF
293 '
294
295 test_expect_success '--no-rebase-merges overrides rebase.rebaseMerges=no-rebase-cousins' '
296 test_config rebase.rebaseMerges no-rebase-cousins &&
297 git checkout -b override-config-no-rebase-cousins E &&
298 git rebase --no-rebase-merges C &&
299 test_cmp_graph C.. <<-\EOF
300 * B
301 * D
302 o C
303 EOF
304 '
305
306 test_expect_success '--rebase-merges overrides rebase.rebaseMerges=rebase-cousins' '
307 test_config rebase.rebaseMerges rebase-cousins &&
308 git checkout -b override-config-rebase-cousins E &&
309 before="$(git rev-parse --verify HEAD)" &&
310 test_tick &&
311 git rebase --rebase-merges C &&
312 test_cmp_rev HEAD $before
313 '
314
315 test_expect_success 'refs/rewritten/* is worktree-local' '
316 git worktree add wt &&
317 cat >wt/script-from-scratch <<-\EOF &&
318 label xyz
319 exec GIT_DIR=../.git git rev-parse --verify refs/rewritten/xyz >a || :
320 exec git rev-parse --verify refs/rewritten/xyz >b
321 EOF
322
323 test_config -C wt sequence.editor \""$PWD"/replace-editor.sh\" &&
324 git -C wt rebase -i HEAD &&
325 test_must_be_empty wt/a &&
326 test_cmp_rev HEAD "$(cat wt/b)"
327 '
328
329 test_expect_success '--abort cleans up refs/rewritten' '
330 git checkout -b abort-cleans-refs-rewritten H &&
331 GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
332 git rev-parse --verify refs/rewritten/onto &&
333 git rebase --abort &&
334 test_must_fail git rev-parse --verify refs/rewritten/onto
335 '
336
337 test_expect_success '--quit cleans up refs/rewritten' '
338 git checkout -b quit-cleans-refs-rewritten H &&
339 GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
340 git rev-parse --verify refs/rewritten/onto &&
341 git rebase --quit &&
342 test_must_fail git rev-parse --verify refs/rewritten/onto
343 '
344
345 test_expect_success 'post-rewrite hook and fixups work for merges' '
346 git checkout -b post-rewrite H &&
347 test_commit same1 &&
348 git reset --hard HEAD^ &&
349 test_commit same2 &&
350 git merge -m "to fix up" same1 &&
351 echo same old same old >same2.t &&
352 test_tick &&
353 git commit --fixup HEAD same2.t &&
354 fixup="$(git rev-parse HEAD)" &&
355
356 test_hook post-rewrite <<-\EOF &&
357 cat >actual
358 EOF
359
360 test_tick &&
361 git rebase -i --autosquash -r HEAD^^^ &&
362 printf "%s %s\n%s %s\n%s %s\n%s %s\n" >expect $(git rev-parse \
363 $fixup^^2 HEAD^2 \
364 $fixup^^ HEAD^ \
365 $fixup^ HEAD \
366 $fixup HEAD) &&
367 test_cmp expect actual
368 '
369
370 test_expect_success 'refuse to merge ancestors of HEAD' '
371 echo "merge HEAD^" >script-from-scratch &&
372 test_config -C wt sequence.editor \""$PWD"/replace-editor.sh\" &&
373 before="$(git rev-parse HEAD)" &&
374 git rebase -i HEAD &&
375 test_cmp_rev HEAD $before
376 '
377
378 test_expect_success 'root commits' '
379 git checkout --orphan unrelated &&
380 (GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
381 test_commit second-root) &&
382 test_commit third-root &&
383 cat >script-from-scratch <<-\EOF &&
384 pick third-root
385 label first-branch
386 reset [new root]
387 pick second-root
388 merge first-branch # Merge the 3rd root
389 EOF
390 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
391 test_tick &&
392 git rebase -i --force-rebase --root -r &&
393 test "Parsnip" = "$(git show -s --format=%an HEAD^)" &&
394 test $(git rev-parse second-root^0) != $(git rev-parse HEAD^) &&
395 test $(git rev-parse second-root:second-root.t) = \
396 $(git rev-parse HEAD^:second-root.t) &&
397 test_cmp_graph HEAD <<-\EOF &&
398 * Merge the 3rd root
399 |\
400 | * third-root
401 * second-root
402 EOF
403
404 : fast forward if possible &&
405 before="$(git rev-parse --verify HEAD)" &&
406 test_might_fail git config --unset sequence.editor &&
407 test_tick &&
408 git rebase -i --root -r &&
409 test_cmp_rev HEAD $before
410 '
411
412 test_expect_success 'a "merge" into a root commit is a fast-forward' '
413 head=$(git rev-parse HEAD) &&
414 cat >script-from-scratch <<-EOF &&
415 reset [new root]
416 merge $head
417 EOF
418 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
419 test_tick &&
420 git rebase -i -r HEAD^ &&
421 test_cmp_rev HEAD $head
422 '
423
424 test_expect_success 'A root commit can be a cousin, treat it that way' '
425 git checkout --orphan khnum &&
426 test_commit yama &&
427 git checkout -b asherah main &&
428 test_commit shamkat &&
429 git merge --allow-unrelated-histories khnum &&
430 test_tick &&
431 git rebase -f -r HEAD^ &&
432 test_cmp_rev ! HEAD^2 khnum &&
433 test_cmp_graph HEAD^.. <<-\EOF &&
434 * Merge branch '\''khnum'\'' into asherah
435 |\
436 | * yama
437 o shamkat
438 EOF
439 test_tick &&
440 git rebase --rebase-merges=rebase-cousins HEAD^ &&
441 test_cmp_graph HEAD^.. <<-\EOF
442 * Merge branch '\''khnum'\'' into asherah
443 |\
444 | * yama
445 |/
446 o shamkat
447 EOF
448 '
449
450 test_expect_success 'labels that are object IDs are rewritten' '
451 git checkout -b third B &&
452 test_commit I &&
453 third=$(git rev-parse HEAD) &&
454 git checkout -b labels main &&
455 git merge --no-commit third &&
456 test_tick &&
457 git commit -m "Merge commit '\''$third'\'' into labels" &&
458 echo noop >script-from-scratch &&
459 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
460 test_tick &&
461 git rebase -i -r A &&
462 grep "^label $third-" .git/ORIGINAL-TODO &&
463 ! grep "^label $third$" .git/ORIGINAL-TODO
464 '
465
466 test_expect_success 'octopus merges' '
467 git checkout -b three &&
468 test_commit before-octopus &&
469 test_commit three &&
470 git checkout -b two HEAD^ &&
471 test_commit two &&
472 git checkout -b one HEAD^ &&
473 test_commit one &&
474 test_tick &&
475 (GIT_AUTHOR_NAME="Hank" GIT_AUTHOR_EMAIL="hank@sea.world" \
476 git merge -m "Tüntenfüsch" two three) &&
477
478 : fast forward if possible &&
479 before="$(git rev-parse --verify HEAD)" &&
480 test_tick &&
481 git rebase -i -r HEAD^^ &&
482 test_cmp_rev HEAD $before &&
483
484 test_tick &&
485 git rebase -i --force-rebase -r HEAD^^ &&
486 test "Hank" = "$(git show -s --format=%an HEAD)" &&
487 test "$before" != $(git rev-parse HEAD) &&
488 test_cmp_graph HEAD^^.. <<-\EOF
489 *-. Tüntenfüsch
490 |\ \
491 | | * three
492 | * | two
493 | |/
494 * / one
495 |/
496 o before-octopus
497 EOF
498 '
499
500 test_expect_success 'with --autosquash and --exec' '
501 git checkout -b with-exec H &&
502 echo Booh >B.t &&
503 test_tick &&
504 git commit --fixup B B.t &&
505 write_script show.sh <<-\EOF &&
506 subject="$(git show -s --format=%s HEAD)"
507 content="$(git diff HEAD^ HEAD | tail -n 1)"
508 echo "$subject: $content"
509 EOF
510 test_tick &&
511 git rebase -ir --autosquash --exec ./show.sh A >actual &&
512 grep "B: +Booh" actual &&
513 grep "E: +Booh" actual &&
514 grep "G: +G" actual
515 '
516
517 test_expect_success '--continue after resolving conflicts after a merge' '
518 git checkout -b already-has-g E &&
519 git cherry-pick E..G &&
520 test_commit H2 &&
521
522 git checkout -b conflicts-in-merge H &&
523 test_commit H2 H2.t conflicts H2-conflict &&
524 test_must_fail git rebase -r already-has-g &&
525 grep conflicts H2.t &&
526 echo resolved >H2.t &&
527 git add -u &&
528 git rebase --continue &&
529 test_must_fail git rev-parse --verify HEAD^2 &&
530 test_path_is_missing .git/MERGE_HEAD
531 '
532
533 test_expect_success '--rebase-merges with strategies' '
534 git checkout -b with-a-strategy F &&
535 test_tick &&
536 git merge -m "Merge conflicting-G" conflicting-G &&
537
538 : first, test with a merge strategy option &&
539 git rebase -ir -Xtheirs G &&
540 echo conflicting-G >expect &&
541 test_cmp expect G.t &&
542
543 : now, try with a merge strategy other than recursive &&
544 git reset --hard @{1} &&
545 write_script git-merge-override <<-\EOF &&
546 echo overridden$1 >>G.t
547 git add G.t
548 EOF
549 PATH="$PWD:$PATH" git rebase -ir -s override -Xxopt G &&
550 test_write_lines G overridden--xopt >expect &&
551 test_cmp expect G.t
552 '
553
554 test_expect_success '--rebase-merges with commit that can generate bad characters for filename' '
555 git checkout -b colon-in-label E &&
556 git merge -m "colon: this should work" G &&
557 git rebase --rebase-merges --force-rebase E
558 '
559
560 test_expect_success '--rebase-merges with message matched with onto label' '
561 git checkout -b onto-label E &&
562 git merge -m onto G &&
563 git rebase --rebase-merges --force-rebase E &&
564 test_cmp_graph <<-\EOF
565 * onto
566 |\
567 | * G
568 | * F
569 * | E
570 |\ \
571 | * | B
572 * | | D
573 | |/
574 |/|
575 * | C
576 |/
577 * A
578 EOF
579 '
580
581 test_done