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