Merge branch 'jh/simple-ipc-sans-pthread'
[git] / t / t3430-rebase-merges.sh
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            (main)
16    \    \       /
17     \    F - G                (second)
18      \
19       Conflicting-G
20 '
21 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
22 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
23
24 . ./test-lib.sh
25 . "$TEST_DIRECTORY"/lib-rebase.sh
26 . "$TEST_DIRECTORY"/lib-log-graph.sh
27
28 test_cmp_graph () {
29         cat >expect &&
30         lib_test_cmp_graph --boundary --format=%s "$@"
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 &&
42         b=$(git rev-parse --short HEAD) &&
43         git checkout main &&
44         test_commit C &&
45         c=$(git rev-parse --short HEAD) &&
46         test_commit D &&
47         d=$(git rev-parse --short HEAD) &&
48         git merge --no-commit B &&
49         test_tick &&
50         git commit -m E &&
51         git tag -m E E &&
52         e=$(git rev-parse --short HEAD) &&
53         git checkout -b second C &&
54         test_commit F &&
55         f=$(git rev-parse --short HEAD) &&
56         test_commit G &&
57         g=$(git rev-parse --short HEAD) &&
58         git checkout main &&
59         git merge --no-commit G &&
60         test_tick &&
61         git commit -m H &&
62         h=$(git rev-parse --short HEAD) &&
63         git tag -m H H &&
64         git checkout A &&
65         test_commit conflicting-G G.t
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 &&
88         git rebase -i -r A main &&
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' '
105         cat >expect <<-EOF &&
106         label onto
107
108         reset onto
109         pick $b B
110         label E
111
112         reset onto
113         pick $c C
114         label branch-point
115         pick $f F
116         pick $g G
117         label H
118
119         reset branch-point # C
120         pick $d D
121         merge -C $e E # E
122         merge -C $h H # H
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\" &&
137         test_must_fail git rebase -ir HEAD &&
138         git rebase --abort
139 '
140
141 test_expect_success 'failed `merge -C` writes patch (may be rescheduled, too)' '
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 &&
157         git reset --hard conflicting-G &&
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
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
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
188 test_expect_success 'with a branch tip that was cherry-picked already' '
189         git checkout -b already-upstream main &&
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
216 test_expect_success 'do not rebase cousins unless asked for' '
217         git checkout -b cousins main &&
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
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
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
264 test_expect_success 'post-rewrite hook and fixups work for merges' '
265         git checkout -b post-rewrite H &&
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
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
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 &&
311         git rebase -i --force-rebase --root -r &&
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
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
343 test_expect_success 'A root commit can be a cousin, treat it that way' '
344         git checkout --orphan khnum &&
345         test_commit yama &&
346         git checkout -b asherah main &&
347         test_commit shamkat &&
348         git merge --allow-unrelated-histories khnum &&
349         test_tick &&
350         git rebase -f -r HEAD^ &&
351         test_cmp_rev ! HEAD^2 khnum &&
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 '
368
369 test_expect_success 'labels that are object IDs are rewritten' '
370         git checkout -b third B &&
371         test_commit I &&
372         third=$(git rev-parse HEAD) &&
373         git checkout -b labels main &&
374         git merge --no-commit third &&
375         test_tick &&
376         git commit -m "Merge commit '\''$third'\'' into labels" &&
377         echo noop >script-from-scratch &&
378         test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
379         test_tick &&
380         git rebase -i -r A &&
381         grep "^label $third-" .git/ORIGINAL-TODO &&
382         ! grep "^label $third$" .git/ORIGINAL-TODO
383 '
384
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 &&
404         git rebase -i --force-rebase -r HEAD^^ &&
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         | |/
413         * / one
414         |/
415         o before-octopus
416         EOF
417 '
418
419 test_expect_success 'with --autosquash and --exec' '
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)"
426         content="$(git diff HEAD^ HEAD | tail -n 1)"
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
436 test_expect_success '--continue after resolving conflicts after a merge' '
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
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
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
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
500 test_done