]>
Commit | Line | Data |
---|---|---|
8f6aed71 JS |
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 | / \ | |
d1c02d93 | 15 | A - C - D - E - H (main) |
d54e1898 PW |
16 | \ \ / |
17 | \ F - G (second) | |
18 | \ | |
19 | Conflicting-G | |
8f6aed71 | 20 | ' |
d1c02d93 | 21 | GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main |
334afbc7 JS |
22 | export 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 | |
28 | test_cmp_graph () { | |
29 | cat >expect && | |
989eea95 | 30 | lib_test_cmp_graph --boundary --format=%s "$@" |
8f6aed71 JS |
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 && | |
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 | ||
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 && | |
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 | ||
104 | test_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 | ||
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\" && | |
891d4a03 | 137 | test_must_fail git rebase -ir HEAD && |
8f6aed71 JS |
138 | git rebase --abort |
139 | ' | |
140 | ||
bc9238bb | 141 | test_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 |
163 | test_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 |
175 | test_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 | 188 | test_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 | 216 | test_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 |
234 | test_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 |
248 | test_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 | ||
256 | test_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 | 264 | test_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 |
289 | test_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 |
297 | test_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 |
331 | test_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 |
343 | test_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 | 369 | test_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 |
385 | test_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 | 419 | test_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 | 436 | test_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 |
452 | test_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 |
473 | test_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 |
479 | test_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 | 500 | test_done |