Merge branch 'jt/tag-following-with-proto-v2-fix'
[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            (master)
16        \       /
17          F - G                (second)
18 '
19 . ./test-lib.sh
20 . "$TEST_DIRECTORY"/lib-rebase.sh
21
22 test_cmp_graph () {
23         cat >expect &&
24         git log --graph --boundary --format=%s "$@" >output &&
25         sed "s/ *$//" <output >output.trimmed &&
26         test_cmp expect output.trimmed
27 }
28
29 test_expect_success 'setup' '
30         write_script replace-editor.sh <<-\EOF &&
31         mv "$1" "$(git rev-parse --git-path ORIGINAL-TODO)"
32         cp script-from-scratch "$1"
33         EOF
34
35         test_commit A &&
36         git checkout -b first &&
37         test_commit B &&
38         git checkout master &&
39         test_commit C &&
40         test_commit D &&
41         git merge --no-commit B &&
42         test_tick &&
43         git commit -m E &&
44         git tag -m E E &&
45         git checkout -b second C &&
46         test_commit F &&
47         test_commit G &&
48         git checkout master &&
49         git merge --no-commit G &&
50         test_tick &&
51         git commit -m H &&
52         git tag -m H H
53 '
54
55 test_expect_success 'create completely different structure' '
56         cat >script-from-scratch <<-\EOF &&
57         label onto
58
59         # onebranch
60         pick G
61         pick D
62         label onebranch
63
64         # second
65         reset onto
66         pick B
67         label second
68
69         reset onto
70         merge -C H second
71         merge onebranch # Merge the topic branch '\''onebranch'\''
72         EOF
73         test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
74         test_tick &&
75         git rebase -i -r A &&
76         test_cmp_graph <<-\EOF
77         *   Merge the topic branch '\''onebranch'\''
78         |\
79         | * D
80         | * G
81         * |   H
82         |\ \
83         | |/
84         |/|
85         | * B
86         |/
87         * A
88         EOF
89 '
90
91 test_expect_success 'generate correct todo list' '
92         cat >expect <<-\EOF &&
93         label onto
94
95         reset onto
96         pick d9df450 B
97         label E
98
99         reset onto
100         pick 5dee784 C
101         label branch-point
102         pick ca2c861 F
103         pick 088b00a G
104         label H
105
106         reset branch-point # C
107         pick 12bd07b D
108         merge -C 2051b56 E # E
109         merge -C 233d48a H # H
110
111         EOF
112
113         grep -v "^#" <.git/ORIGINAL-TODO >output &&
114         test_cmp expect output
115 '
116
117 test_expect_success '`reset` refuses to overwrite untracked files' '
118         git checkout -b refuse-to-reset &&
119         test_commit dont-overwrite-untracked &&
120         git checkout @{-1} &&
121         : >dont-overwrite-untracked.t &&
122         echo "reset refs/tags/dont-overwrite-untracked" >script-from-scratch &&
123         test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
124         test_must_fail git rebase -r HEAD &&
125         git rebase --abort
126 '
127
128 test_expect_success 'failed `merge` writes patch (may be rescheduled, too)' '
129         test_when_finished "test_might_fail git rebase --abort" &&
130         git checkout -b conflicting-merge A &&
131
132         : fail because of conflicting untracked file &&
133         >G.t &&
134         echo "merge -C H G" >script-from-scratch &&
135         test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
136         test_tick &&
137         test_must_fail git rebase -ir HEAD &&
138         grep "^merge -C .* G$" .git/rebase-merge/done &&
139         grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
140         test_path_is_file .git/rebase-merge/patch &&
141
142         : fail because of merge conflict &&
143         rm G.t .git/rebase-merge/patch &&
144         git reset --hard &&
145         test_commit conflicting-G G.t not-G conflicting-G &&
146         test_must_fail git rebase --continue &&
147         ! grep "^merge -C .* G$" .git/rebase-merge/git-rebase-todo &&
148         test_path_is_file .git/rebase-merge/patch
149 '
150
151 test_expect_success 'with a branch tip that was cherry-picked already' '
152         git checkout -b already-upstream master &&
153         base="$(git rev-parse --verify HEAD)" &&
154
155         test_commit A1 &&
156         test_commit A2 &&
157         git reset --hard $base &&
158         test_commit B1 &&
159         test_tick &&
160         git merge -m "Merge branch A" A2 &&
161
162         git checkout -b upstream-with-a2 $base &&
163         test_tick &&
164         git cherry-pick A2 &&
165
166         git checkout already-upstream &&
167         test_tick &&
168         git rebase -i -r upstream-with-a2 &&
169         test_cmp_graph upstream-with-a2.. <<-\EOF
170         *   Merge branch A
171         |\
172         | * A1
173         * | B1
174         |/
175         o A2
176         EOF
177 '
178
179 test_expect_success 'do not rebase cousins unless asked for' '
180         git checkout -b cousins master &&
181         before="$(git rev-parse --verify HEAD)" &&
182         test_tick &&
183         git rebase -r HEAD^ &&
184         test_cmp_rev HEAD $before &&
185         test_tick &&
186         git rebase --rebase-merges=rebase-cousins HEAD^ &&
187         test_cmp_graph HEAD^.. <<-\EOF
188         *   Merge the topic branch '\''onebranch'\''
189         |\
190         | * D
191         | * G
192         |/
193         o H
194         EOF
195 '
196
197 test_expect_success 'refs/rewritten/* is worktree-local' '
198         git worktree add wt &&
199         cat >wt/script-from-scratch <<-\EOF &&
200         label xyz
201         exec GIT_DIR=../.git git rev-parse --verify refs/rewritten/xyz >a || :
202         exec git rev-parse --verify refs/rewritten/xyz >b
203         EOF
204
205         test_config -C wt sequence.editor \""$PWD"/replace-editor.sh\" &&
206         git -C wt rebase -i HEAD &&
207         test_must_be_empty wt/a &&
208         test_cmp_rev HEAD "$(cat wt/b)"
209 '
210
211 test_expect_success 'post-rewrite hook and fixups work for merges' '
212         git checkout -b post-rewrite &&
213         test_commit same1 &&
214         git reset --hard HEAD^ &&
215         test_commit same2 &&
216         git merge -m "to fix up" same1 &&
217         echo same old same old >same2.t &&
218         test_tick &&
219         git commit --fixup HEAD same2.t &&
220         fixup="$(git rev-parse HEAD)" &&
221
222         mkdir -p .git/hooks &&
223         test_when_finished "rm .git/hooks/post-rewrite" &&
224         echo "cat >actual" | write_script .git/hooks/post-rewrite &&
225
226         test_tick &&
227         git rebase -i --autosquash -r HEAD^^^ &&
228         printf "%s %s\n%s %s\n%s %s\n%s %s\n" >expect $(git rev-parse \
229                 $fixup^^2 HEAD^2 \
230                 $fixup^^ HEAD^ \
231                 $fixup^ HEAD \
232                 $fixup HEAD) &&
233         test_cmp expect actual
234 '
235
236 test_expect_success 'refuse to merge ancestors of HEAD' '
237         echo "merge HEAD^" >script-from-scratch &&
238         test_config -C wt sequence.editor \""$PWD"/replace-editor.sh\" &&
239         before="$(git rev-parse HEAD)" &&
240         git rebase -i HEAD &&
241         test_cmp_rev HEAD $before
242 '
243
244 test_expect_success 'root commits' '
245         git checkout --orphan unrelated &&
246         (GIT_AUTHOR_NAME="Parsnip" GIT_AUTHOR_EMAIL="root@example.com" \
247          test_commit second-root) &&
248         test_commit third-root &&
249         cat >script-from-scratch <<-\EOF &&
250         pick third-root
251         label first-branch
252         reset [new root]
253         pick second-root
254         merge first-branch # Merge the 3rd root
255         EOF
256         test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
257         test_tick &&
258         git rebase -i --force --root -r &&
259         test "Parsnip" = "$(git show -s --format=%an HEAD^)" &&
260         test $(git rev-parse second-root^0) != $(git rev-parse HEAD^) &&
261         test $(git rev-parse second-root:second-root.t) = \
262                 $(git rev-parse HEAD^:second-root.t) &&
263         test_cmp_graph HEAD <<-\EOF &&
264         *   Merge the 3rd root
265         |\
266         | * third-root
267         * second-root
268         EOF
269
270         : fast forward if possible &&
271         before="$(git rev-parse --verify HEAD)" &&
272         test_might_fail git config --unset sequence.editor &&
273         test_tick &&
274         git rebase -i --root -r &&
275         test_cmp_rev HEAD $before
276 '
277
278 test_expect_success 'a "merge" into a root commit is a fast-forward' '
279         head=$(git rev-parse HEAD) &&
280         cat >script-from-scratch <<-EOF &&
281         reset [new root]
282         merge $head
283         EOF
284         test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
285         test_tick &&
286         git rebase -i -r HEAD^ &&
287         test_cmp_rev HEAD $head
288 '
289
290 test_expect_success 'A root commit can be a cousin, treat it that way' '
291         git checkout --orphan khnum &&
292         test_commit yama &&
293         git checkout -b asherah master &&
294         test_commit shamkat &&
295         git merge --allow-unrelated-histories khnum &&
296         test_tick &&
297         git rebase -f -r HEAD^ &&
298         ! test_cmp_rev HEAD^2 khnum &&
299         test_cmp_graph HEAD^.. <<-\EOF &&
300         *   Merge branch '\''khnum'\'' into asherah
301         |\
302         | * yama
303         o shamkat
304         EOF
305         test_tick &&
306         git rebase --rebase-merges=rebase-cousins HEAD^ &&
307         test_cmp_graph HEAD^.. <<-\EOF
308         *   Merge branch '\''khnum'\'' into asherah
309         |\
310         | * yama
311         |/
312         o shamkat
313         EOF
314 '
315
316 test_expect_success 'labels that are object IDs are rewritten' '
317         git checkout -b third B &&
318         test_commit I &&
319         third=$(git rev-parse HEAD) &&
320         git checkout -b labels master &&
321         git merge --no-commit third &&
322         test_tick &&
323         git commit -m "Merge commit '\''$third'\'' into labels" &&
324         echo noop >script-from-scratch &&
325         test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
326         test_tick &&
327         git rebase -i -r A &&
328         grep "^label $third-" .git/ORIGINAL-TODO &&
329         ! grep "^label $third$" .git/ORIGINAL-TODO
330 '
331
332 test_expect_success 'octopus merges' '
333         git checkout -b three &&
334         test_commit before-octopus &&
335         test_commit three &&
336         git checkout -b two HEAD^ &&
337         test_commit two &&
338         git checkout -b one HEAD^ &&
339         test_commit one &&
340         test_tick &&
341         (GIT_AUTHOR_NAME="Hank" GIT_AUTHOR_EMAIL="hank@sea.world" \
342          git merge -m "Tüntenfüsch" two three) &&
343
344         : fast forward if possible &&
345         before="$(git rev-parse --verify HEAD)" &&
346         test_tick &&
347         git rebase -i -r HEAD^^ &&
348         test_cmp_rev HEAD $before &&
349
350         test_tick &&
351         git rebase -i --force -r HEAD^^ &&
352         test "Hank" = "$(git show -s --format=%an HEAD)" &&
353         test "$before" != $(git rev-parse HEAD) &&
354         test_cmp_graph HEAD^^.. <<-\EOF
355         *-.   Tüntenfüsch
356         |\ \
357         | | * three
358         | * | two
359         | |/
360         * | one
361         |/
362         o before-octopus
363         EOF
364 '
365
366 test_done