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