]> git.ipfire.org Git - thirdparty/git.git/blame - t/t3430-rebase-merges.sh
Merge branch 'zh/difftool-skip-to'
[thirdparty/git.git] / t / t3430-rebase-merges.sh
CommitLineData
8f6aed71
JS
1#!/bin/sh
2#
3# Copyright (c) 2018 Johannes E. Schindelin
4#
5
6test_description='git rebase -i --rebase-merges
7
8This test runs git rebase "interactively", retaining the branch structure by
9recreating merge commits.
10
11Initial setup:
12
13 -- B -- (first)
14 / \
d1c02d93 15 A - C - D - E - H (main)
d54e1898
PW
16 \ \ /
17 \ F - G (second)
18 \
19 Conflicting-G
8f6aed71 20'
d1c02d93 21GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
334afbc7
JS
22export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
23
8f6aed71
JS
24. ./test-lib.sh
25. "$TEST_DIRECTORY"/lib-rebase.sh
989eea95 26. "$TEST_DIRECTORY"/lib-log-graph.sh
8f6aed71
JS
27
28test_cmp_graph () {
29 cat >expect &&
989eea95 30 lib_test_cmp_graph --boundary --format=%s "$@"
8f6aed71
JS
31}
32
33test_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 &&
9a6738c0 42 b=$(git rev-parse --short HEAD) &&
d1c02d93 43 git checkout main &&
8f6aed71 44 test_commit C &&
9a6738c0 45 c=$(git rev-parse --short HEAD) &&
8f6aed71 46 test_commit D &&
9a6738c0 47 d=$(git rev-parse --short HEAD) &&
8f6aed71
JS
48 git merge --no-commit B &&
49 test_tick &&
50 git commit -m E &&
51 git tag -m E E &&
9a6738c0 52 e=$(git rev-parse --short HEAD) &&
8f6aed71
JS
53 git checkout -b second C &&
54 test_commit F &&
9a6738c0 55 f=$(git rev-parse --short HEAD) &&
8f6aed71 56 test_commit G &&
9a6738c0 57 g=$(git rev-parse --short HEAD) &&
d1c02d93 58 git checkout main &&
8f6aed71
JS
59 git merge --no-commit G &&
60 test_tick &&
61 git commit -m H &&
9a6738c0 62 h=$(git rev-parse --short HEAD) &&
d54e1898
PW
63 git tag -m H H &&
64 git checkout A &&
65 test_commit conflicting-G G.t
8f6aed71
JS
66'
67
68test_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 &&
d1c02d93 88 git rebase -i -r A main &&
8f6aed71
JS
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
104test_expect_success 'generate correct todo list' '
9a6738c0 105 cat >expect <<-EOF &&
8f6aed71
JS
106 label onto
107
108 reset onto
9a6738c0 109 pick $b B
8f6aed71
JS
110 label E
111
112 reset onto
9a6738c0 113 pick $c C
8f6aed71 114 label branch-point
9a6738c0 115 pick $f F
116 pick $g G
8f6aed71
JS
117 label H
118
119 reset branch-point # C
9a6738c0 120 pick $d D
121 merge -C $e E # E
122 merge -C $h H # H
8f6aed71
JS
123
124 EOF
125
126 grep -v "^#" <.git/ORIGINAL-TODO >output &&
127 test_cmp expect output
128'
129
130test_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\" &&
891d4a03 137 test_must_fail git rebase -ir HEAD &&
8f6aed71
JS
138 git rebase --abort
139'
140
bc9238bb 141test_expect_success 'failed `merge -C` writes patch (may be rescheduled, too)' '
8f6aed71
JS
142 test_when_finished "test_might_fail git rebase --abort" &&
143 git checkout -b conflicting-merge A &&
144
145 : fail because of conflicting untracked file &&
146 >G.t &&
147 echo "merge -C H G" >script-from-scratch &&
148 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
149 test_tick &&
150 test_must_fail git rebase -ir HEAD &&
151 grep "^merge -C .* G$" .git/rebase-merge/done &&
152 grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
153 test_path_is_file .git/rebase-merge/patch &&
154
155 : fail because of merge conflict &&
156 rm G.t .git/rebase-merge/patch &&
d54e1898 157 git reset --hard conflicting-G &&
8f6aed71
JS
158 test_must_fail git rebase --continue &&
159 ! grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
160 test_path_is_file .git/rebase-merge/patch
161'
162
bc9238bb
PW
163test_expect_success 'failed `merge <branch>` does not crash' '
164 test_when_finished "test_might_fail git rebase --abort" &&
165 git checkout conflicting-G &&
166
167 echo "merge G" >script-from-scratch &&
168 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
169 test_tick &&
170 test_must_fail git rebase -ir HEAD &&
171 ! grep "^merge G$" .git/rebase-merge/git-rebase-todo &&
172 grep "^Merge branch ${SQ}G${SQ}$" .git/rebase-merge/message
173'
174
6df8df08
PW
175test_expect_success 'fast-forward merge -c still rewords' '
176 git checkout -b fast-forward-merge-c H &&
177 (
178 set_fake_editor &&
179 FAKE_COMMIT_MESSAGE=edited \
180 GIT_SEQUENCE_EDITOR="echo merge -c H G >" \
181 git rebase -ir @^
182 ) &&
183 echo edited >expected &&
184 git log --pretty=format:%B -1 >actual &&
185 test_cmp expected actual
186'
187
8f6aed71 188test_expect_success 'with a branch tip that was cherry-picked already' '
d1c02d93 189 git checkout -b already-upstream main &&
8f6aed71
JS
190 base="$(git rev-parse --verify HEAD)" &&
191
192 test_commit A1 &&
193 test_commit A2 &&
194 git reset --hard $base &&
195 test_commit B1 &&
196 test_tick &&
197 git merge -m "Merge branch A" A2 &&
198
199 git checkout -b upstream-with-a2 $base &&
200 test_tick &&
201 git cherry-pick A2 &&
202
203 git checkout already-upstream &&
204 test_tick &&
205 git rebase -i -r upstream-with-a2 &&
206 test_cmp_graph upstream-with-a2.. <<-\EOF
207 * Merge branch A
208 |\
209 | * A1
210 * | B1
211 |/
212 o A2
213 EOF
214'
215
7543f6f4 216test_expect_success 'do not rebase cousins unless asked for' '
d1c02d93 217 git checkout -b cousins main &&
7543f6f4
JS
218 before="$(git rev-parse --verify HEAD)" &&
219 test_tick &&
220 git rebase -r HEAD^ &&
221 test_cmp_rev HEAD $before &&
222 test_tick &&
223 git rebase --rebase-merges=rebase-cousins HEAD^ &&
224 test_cmp_graph HEAD^.. <<-\EOF
225 * Merge the topic branch '\''onebranch'\''
226 |\
227 | * D
228 | * G
229 |/
230 o H
231 EOF
232'
233
a9be29c9
JS
234test_expect_success 'refs/rewritten/* is worktree-local' '
235 git worktree add wt &&
236 cat >wt/script-from-scratch <<-\EOF &&
237 label xyz
238 exec GIT_DIR=../.git git rev-parse --verify refs/rewritten/xyz >a || :
239 exec git rev-parse --verify refs/rewritten/xyz >b
240 EOF
241
242 test_config -C wt sequence.editor \""$PWD"/replace-editor.sh\" &&
243 git -C wt rebase -i HEAD &&
244 test_must_be_empty wt/a &&
245 test_cmp_rev HEAD "$(cat wt/b)"
246'
247
d559f502
PW
248test_expect_success '--abort cleans up refs/rewritten' '
249 git checkout -b abort-cleans-refs-rewritten H &&
250 GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
251 git rev-parse --verify refs/rewritten/onto &&
252 git rebase --abort &&
253 test_must_fail git rev-parse --verify refs/rewritten/onto
254'
255
256test_expect_success '--quit cleans up refs/rewritten' '
257 git checkout -b quit-cleans-refs-rewritten H &&
258 GIT_SEQUENCE_EDITOR="echo break >>" git rebase -ir @^ &&
259 git rev-parse --verify refs/rewritten/onto &&
260 git rebase --quit &&
261 test_must_fail git rev-parse --verify refs/rewritten/onto
262'
263
537e7d61 264test_expect_success 'post-rewrite hook and fixups work for merges' '
d559f502 265 git checkout -b post-rewrite H &&
537e7d61
JS
266 test_commit same1 &&
267 git reset --hard HEAD^ &&
268 test_commit same2 &&
269 git merge -m "to fix up" same1 &&
270 echo same old same old >same2.t &&
271 test_tick &&
272 git commit --fixup HEAD same2.t &&
273 fixup="$(git rev-parse HEAD)" &&
274
275 mkdir -p .git/hooks &&
276 test_when_finished "rm .git/hooks/post-rewrite" &&
277 echo "cat >actual" | write_script .git/hooks/post-rewrite &&
278
279 test_tick &&
280 git rebase -i --autosquash -r HEAD^^^ &&
281 printf "%s %s\n%s %s\n%s %s\n%s %s\n" >expect $(git rev-parse \
282 $fixup^^2 HEAD^2 \
283 $fixup^^ HEAD^ \
284 $fixup^ HEAD \
285 $fixup HEAD) &&
286 test_cmp expect actual
287'
288
7ccdf65b
JS
289test_expect_success 'refuse to merge ancestors of HEAD' '
290 echo "merge HEAD^" >script-from-scratch &&
291 test_config -C wt sequence.editor \""$PWD"/replace-editor.sh\" &&
292 before="$(git rev-parse HEAD)" &&
293 git rebase -i HEAD &&
294 test_cmp_rev HEAD $before
295'
296
ebddf393
JS
297test_expect_success 'root commits' '
298 git checkout --orphan unrelated &&
299 (GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
300 test_commit second-root) &&
301 test_commit third-root &&
302 cat >script-from-scratch <<-\EOF &&
303 pick third-root
304 label first-branch
305 reset [new root]
306 pick second-root
307 merge first-branch # Merge the 3rd root
308 EOF
309 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
310 test_tick &&
f927ae6c 311 git rebase -i --force-rebase --root -r &&
ebddf393
JS
312 test "Parsnip" = "$(git show -s --format=%an HEAD^)" &&
313 test $(git rev-parse second-root^0) != $(git rev-parse HEAD^) &&
314 test $(git rev-parse second-root:second-root.t) = \
315 $(git rev-parse HEAD^:second-root.t) &&
316 test_cmp_graph HEAD <<-\EOF &&
317 * Merge the 3rd root
318 |\
319 | * third-root
320 * second-root
321 EOF
322
323 : fast forward if possible &&
324 before="$(git rev-parse --verify HEAD)" &&
325 test_might_fail git config --unset sequence.editor &&
326 test_tick &&
327 git rebase -i --root -r &&
328 test_cmp_rev HEAD $before
329'
330
9c85a1c2
JS
331test_expect_success 'a "merge" into a root commit is a fast-forward' '
332 head=$(git rev-parse HEAD) &&
333 cat >script-from-scratch <<-EOF &&
334 reset [new root]
335 merge $head
336 EOF
337 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
338 test_tick &&
339 git rebase -i -r HEAD^ &&
340 test_cmp_rev HEAD $head
341'
342
8fa6eea0
JS
343test_expect_success 'A root commit can be a cousin, treat it that way' '
344 git checkout --orphan khnum &&
345 test_commit yama &&
d1c02d93 346 git checkout -b asherah main &&
8fa6eea0
JS
347 test_commit shamkat &&
348 git merge --allow-unrelated-histories khnum &&
349 test_tick &&
350 git rebase -f -r HEAD^ &&
2c9e125b 351 test_cmp_rev ! HEAD^2 khnum &&
8fa6eea0
JS
352 test_cmp_graph HEAD^.. <<-\EOF &&
353 * Merge branch '\''khnum'\'' into asherah
354 |\
355 | * yama
356 o shamkat
357 EOF
358 test_tick &&
359 git rebase --rebase-merges=rebase-cousins HEAD^ &&
360 test_cmp_graph HEAD^.. <<-\EOF
361 * Merge branch '\''khnum'\'' into asherah
362 |\
363 | * yama
364 |/
365 o shamkat
366 EOF
367'
9c85a1c2 368
5971b083 369test_expect_success 'labels that are object IDs are rewritten' '
370 git checkout -b third B &&
5971b083 371 test_commit I &&
372 third=$(git rev-parse HEAD) &&
d1c02d93 373 git checkout -b labels main &&
5971b083 374 git merge --no-commit third &&
375 test_tick &&
376 git commit -m "Merge commit '\''$third'\'' into labels" &&
0c5a779c 377 echo noop >script-from-scratch &&
5971b083 378 test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
379 test_tick &&
380 git rebase -i -r A &&
0c5a779c 381 grep "^label $third-" .git/ORIGINAL-TODO &&
5971b083 382 ! grep "^label $third$" .git/ORIGINAL-TODO
383'
384
2b6ad0f4
JS
385test_expect_success 'octopus merges' '
386 git checkout -b three &&
387 test_commit before-octopus &&
388 test_commit three &&
389 git checkout -b two HEAD^ &&
390 test_commit two &&
391 git checkout -b one HEAD^ &&
392 test_commit one &&
393 test_tick &&
394 (GIT_AUTHOR_NAME="Hank" GIT_AUTHOR_EMAIL="hank@sea.world" \
395 git merge -m "Tüntenfüsch" two three) &&
396
397 : fast forward if possible &&
398 before="$(git rev-parse --verify HEAD)" &&
399 test_tick &&
400 git rebase -i -r HEAD^^ &&
401 test_cmp_rev HEAD $before &&
402
403 test_tick &&
f927ae6c 404 git rebase -i --force-rebase -r HEAD^^ &&
2b6ad0f4
JS
405 test "Hank" = "$(git show -s --format=%an HEAD)" &&
406 test "$before" != $(git rev-parse HEAD) &&
407 test_cmp_graph HEAD^^.. <<-\EOF
408 *-. Tüntenfüsch
409 |\ \
410 | | * three
411 | * | two
412 | |/
479db18b 413 * / one
2b6ad0f4
JS
414 |/
415 o before-octopus
416 EOF
417'
418
1ace63bc 419test_expect_success 'with --autosquash and --exec' '
f0880f77
JS
420 git checkout -b with-exec H &&
421 echo Booh >B.t &&
422 test_tick &&
423 git commit --fixup B B.t &&
424 write_script show.sh <<-\EOF &&
425 subject="$(git show -s --format=%s HEAD)"
bafa2d74 426 content="$(git diff HEAD^ HEAD | tail -n 1)"
f0880f77
JS
427 echo "$subject: $content"
428 EOF
429 test_tick &&
430 git rebase -ir --autosquash --exec ./show.sh A >actual &&
431 grep "B: +Booh" actual &&
432 grep "E: +Booh" actual &&
433 grep "G: +G" actual
434'
435
85f8d9da 436test_expect_success '--continue after resolving conflicts after a merge' '
f08110dd
JS
437 git checkout -b already-has-g E &&
438 git cherry-pick E..G &&
439 test_commit H2 &&
440
441 git checkout -b conflicts-in-merge H &&
442 test_commit H2 H2.t conflicts H2-conflict &&
443 test_must_fail git rebase -r already-has-g &&
444 grep conflicts H2.t &&
445 echo resolved >H2.t &&
446 git add -u &&
447 git rebase --continue &&
448 test_must_fail git rev-parse --verify HEAD^2 &&
449 test_path_is_missing .git/MERGE_HEAD
450'
451
e145d993
JS
452test_expect_success '--rebase-merges with strategies' '
453 git checkout -b with-a-strategy F &&
454 test_tick &&
455 git merge -m "Merge conflicting-G" conflicting-G &&
456
457 : first, test with a merge strategy option &&
458 git rebase -ir -Xtheirs G &&
459 echo conflicting-G >expect &&
460 test_cmp expect G.t &&
461
462 : now, try with a merge strategy other than recursive &&
463 git reset --hard @{1} &&
464 write_script git-merge-override <<-\EOF &&
465 echo overridden$1 >>G.t
466 git add G.t
467 EOF
468 PATH="$PWD:$PATH" git rebase -ir -s override -Xxopt G &&
469 test_write_lines G overridden--xopt >expect &&
470 test_cmp expect G.t
471'
472
cd552227
MR
473test_expect_success '--rebase-merges with commit that can generate bad characters for filename' '
474 git checkout -b colon-in-label E &&
475 git merge -m "colon: this should work" G &&
476 git rebase --rebase-merges --force-rebase E
477'
478
e02058a7
ĐTCD
479test_expect_success '--rebase-merges with message matched with onto label' '
480 git checkout -b onto-label E &&
481 git merge -m onto G &&
482 git rebase --rebase-merges --force-rebase E &&
483 test_cmp_graph <<-\EOF
484 * onto
485 |\
486 | * G
487 | * F
488 * | E
489 |\ \
490 | * | B
491 * | | D
492 | |/
493 |/|
494 * | C
495 |/
496 * A
497 EOF
498'
499
8f6aed71 500test_done