1#!/bin/sh
2#
3# Copyright (c) 2018 Johannes E. Schindelin
4#
5
6test_description='git rebase -i --rebase-merges
7
8This test runs git rebase "interactively", retaining the branch structure by
9recreating merge commits.
10
11Initial 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
22test_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
29test_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
55test_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
91test_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
117test_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
128test_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
151test_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
179test_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
197test_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
211test_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
236test_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
244test_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
278test_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
290test_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
316test_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
332test_done