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