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