1#!/bin/sh
2
3test_description='Test cherry-pick continuation features
4
5 + conflicting: rewrites unrelated to conflicting
6 + yetanotherpick: rewrites foo to e
7 + anotherpick: rewrites foo to d
8 + picked: rewrites foo to c
9 + unrelatedpick: rewrites unrelated to reallyunrelated
10 + base: rewrites foo to b
11 + initial: writes foo as a, unrelated as unrelated
12
13'
14
15. ./test-lib.sh
16
17pristine_detach () {
18 git cherry-pick --quit &&
19 git checkout -f "$1^0" &&
20 git read-tree -u --reset HEAD &&
21 git clean -d -f -f -q -x
22}
23
24test_cmp_rev () {
25 git rev-parse --verify "$1" >expect.rev &&
26 git rev-parse --verify "$2" >actual.rev &&
27 test_cmp expect.rev actual.rev
28}
29
30test_expect_success setup '
31 git config advice.detachedhead false
32 echo unrelated >unrelated &&
33 git add unrelated &&
34 test_commit initial foo a &&
35 test_commit base foo b &&
36 test_commit unrelatedpick unrelated reallyunrelated &&
37 test_commit picked foo c &&
38 test_commit anotherpick foo d &&
39 test_commit yetanotherpick foo e &&
40 pristine_detach initial &&
41 test_commit conflicting unrelated
42'
43
44test_expect_success 'cherry-pick persists data on failure' '
45 pristine_detach initial &&
46 test_must_fail git cherry-pick -s base..anotherpick &&
47 test_path_is_dir .git/sequencer &&
48 test_path_is_file .git/sequencer/head &&
49 test_path_is_file .git/sequencer/todo &&
50 test_path_is_file .git/sequencer/opts
51'
52
53test_expect_success 'cherry-pick mid-cherry-pick-sequence' '
54 pristine_detach initial &&
55 test_must_fail git cherry-pick base..anotherpick &&
56 test_cmp_rev picked CHERRY_PICK_HEAD &&
57 # "oops, I forgot that these patches rely on the change from base"
58 git checkout HEAD foo &&
59 git cherry-pick base &&
60 git cherry-pick picked &&
61 git cherry-pick --continue &&
62 git diff --exit-code anotherpick
63'
64
65test_expect_success 'cherry-pick persists opts correctly' '
66 pristine_detach initial &&
67 test_must_fail git cherry-pick -s -m 1 --strategy=recursive -X patience -X ours base..anotherpick &&
68 test_path_is_dir .git/sequencer &&
69 test_path_is_file .git/sequencer/head &&
70 test_path_is_file .git/sequencer/todo &&
71 test_path_is_file .git/sequencer/opts &&
72 echo "true" >expect &&
73 git config --file=.git/sequencer/opts --get-all options.signoff >actual &&
74 test_cmp expect actual &&
75 echo "1" >expect &&
76 git config --file=.git/sequencer/opts --get-all options.mainline >actual &&
77 test_cmp expect actual &&
78 echo "recursive" >expect &&
79 git config --file=.git/sequencer/opts --get-all options.strategy >actual &&
80 test_cmp expect actual &&
81 cat >expect <<-\EOF &&
82 patience
83 ours
84 EOF
85 git config --file=.git/sequencer/opts --get-all options.strategy-option >actual &&
86 test_cmp expect actual
87'
88
89test_expect_success 'cherry-pick cleans up sequencer state upon success' '
90 pristine_detach initial &&
91 git cherry-pick initial..picked &&
92 test_path_is_missing .git/sequencer
93'
94
95test_expect_success '--quit does not complain when no cherry-pick is in progress' '
96 pristine_detach initial &&
97 git cherry-pick --quit
98'
99
100test_expect_success '--abort requires cherry-pick in progress' '
101 pristine_detach initial &&
102 test_must_fail git cherry-pick --abort
103'
104
105test_expect_success '--quit cleans up sequencer state' '
106 pristine_detach initial &&
107 test_must_fail git cherry-pick base..picked &&
108 git cherry-pick --quit &&
109 test_path_is_missing .git/sequencer
110'
111
112test_expect_success '--quit keeps HEAD and conflicted index intact' '
113 pristine_detach initial &&
114 cat >expect <<-\EOF &&
115 OBJID
116 :100644 100644 OBJID OBJID M unrelated
117 OBJID
118 :000000 100644 OBJID OBJID A foo
119 :000000 100644 OBJID OBJID A unrelated
120 EOF
121 test_must_fail git cherry-pick base..picked &&
122 git cherry-pick --quit &&
123 test_path_is_missing .git/sequencer &&
124 test_must_fail git update-index --refresh &&
125 {
126 git rev-list HEAD |
127 git diff-tree --root --stdin |
128 sed "s/$_x40/OBJID/g"
129 } >actual &&
130 test_cmp expect actual
131'
132
133test_expect_success '--abort to cancel multiple cherry-pick' '
134 pristine_detach initial &&
135 test_must_fail git cherry-pick base..anotherpick &&
136 git cherry-pick --abort &&
137 test_path_is_missing .git/sequencer &&
138 test_cmp_rev initial HEAD &&
139 git update-index --refresh &&
140 git diff-index --exit-code HEAD
141'
142
143test_expect_success '--abort to cancel single cherry-pick' '
144 pristine_detach initial &&
145 test_must_fail git cherry-pick picked &&
146 git cherry-pick --abort &&
147 test_path_is_missing .git/sequencer &&
148 test_cmp_rev initial HEAD &&
149 git update-index --refresh &&
150 git diff-index --exit-code HEAD
151'
152
153test_expect_success 'cherry-pick --abort to cancel multiple revert' '
154 pristine_detach anotherpick &&
155 test_must_fail git revert base..picked &&
156 git cherry-pick --abort &&
157 test_path_is_missing .git/sequencer &&
158 test_cmp_rev anotherpick HEAD &&
159 git update-index --refresh &&
160 git diff-index --exit-code HEAD
161'
162
163test_expect_success 'revert --abort works, too' '
164 pristine_detach anotherpick &&
165 test_must_fail git revert base..picked &&
166 git revert --abort &&
167 test_path_is_missing .git/sequencer &&
168 test_cmp_rev anotherpick HEAD
169'
170
171test_expect_success '--abort to cancel single revert' '
172 pristine_detach anotherpick &&
173 test_must_fail git revert picked &&
174 git revert --abort &&
175 test_path_is_missing .git/sequencer &&
176 test_cmp_rev anotherpick HEAD &&
177 git update-index --refresh &&
178 git diff-index --exit-code HEAD
179'
180
181test_expect_success '--abort keeps unrelated change, easy case' '
182 pristine_detach unrelatedpick &&
183 echo changed >expect &&
184 test_must_fail git cherry-pick picked..yetanotherpick &&
185 echo changed >unrelated &&
186 git cherry-pick --abort &&
187 test_cmp expect unrelated
188'
189
190test_expect_success '--abort refuses to clobber unrelated change, harder case' '
191 pristine_detach initial &&
192 echo changed >expect &&
193 test_must_fail git cherry-pick base..anotherpick &&
194 echo changed >unrelated &&
195 test_must_fail git cherry-pick --abort &&
196 test_cmp expect unrelated &&
197 git rev-list HEAD >log &&
198 test_line_count = 2 log &&
199 test_must_fail git update-index --refresh &&
200
201 git checkout unrelated &&
202 git cherry-pick --abort &&
203 test_cmp_rev initial HEAD
204'
205
206test_expect_success 'cherry-pick still writes sequencer state when one commit is left' '
207 pristine_detach initial &&
208 test_must_fail git cherry-pick base..picked &&
209 test_path_is_dir .git/sequencer &&
210 echo "resolved" >foo &&
211 git add foo &&
212 git commit &&
213 {
214 git rev-list HEAD |
215 git diff-tree --root --stdin |
216 sed "s/$_x40/OBJID/g"
217 } >actual &&
218 cat >expect <<-\EOF &&
219 OBJID
220 :100644 100644 OBJID OBJID M foo
221 OBJID
222 :100644 100644 OBJID OBJID M unrelated
223 OBJID
224 :000000 100644 OBJID OBJID A foo
225 :000000 100644 OBJID OBJID A unrelated
226 EOF
227 test_cmp expect actual
228'
229
230test_expect_success '--abort after last commit in sequence' '
231 pristine_detach initial &&
232 test_must_fail git cherry-pick base..picked &&
233 git cherry-pick --abort &&
234 test_path_is_missing .git/sequencer &&
235 test_cmp_rev initial HEAD &&
236 git update-index --refresh &&
237 git diff-index --exit-code HEAD
238'
239
240test_expect_success 'cherry-pick does not implicitly stomp an existing operation' '
241 pristine_detach initial &&
242 test_must_fail git cherry-pick base..anotherpick &&
243 test-chmtime -v +0 .git/sequencer >expect &&
244 test_must_fail git cherry-pick unrelatedpick &&
245 test-chmtime -v +0 .git/sequencer >actual &&
246 test_cmp expect actual
247'
248
249test_expect_success '--continue complains when no cherry-pick is in progress' '
250 pristine_detach initial &&
251 test_must_fail git cherry-pick --continue
252'
253
254test_expect_success '--continue complains when there are unresolved conflicts' '
255 pristine_detach initial &&
256 test_must_fail git cherry-pick base..anotherpick &&
257 test_must_fail git cherry-pick --continue
258'
259
260test_expect_success '--continue of single cherry-pick' '
261 pristine_detach initial &&
262 echo c >expect &&
263 test_must_fail git cherry-pick picked &&
264 echo c >foo &&
265 git add foo &&
266 git cherry-pick --continue &&
267
268 test_cmp expect foo &&
269 test_cmp_rev initial HEAD^ &&
270 git diff --exit-code HEAD &&
271 test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
272'
273
274test_expect_success '--continue of single revert' '
275 pristine_detach initial &&
276 echo resolved >expect &&
277 echo "Revert \"picked\"" >expect.msg &&
278 test_must_fail git revert picked &&
279 echo resolved >foo &&
280 git add foo &&
281 git cherry-pick --continue &&
282
283 git diff --exit-code HEAD &&
284 test_cmp expect foo &&
285 test_cmp_rev initial HEAD^ &&
286 git diff-tree -s --pretty=tformat:%s HEAD >msg &&
287 test_cmp expect.msg msg &&
288 test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
289 test_must_fail git rev-parse --verify REVERT_HEAD
290'
291
292test_expect_success '--continue after resolving conflicts' '
293 pristine_detach initial &&
294 echo d >expect &&
295 cat >expect.log <<-\EOF &&
296 OBJID
297 :100644 100644 OBJID OBJID M foo
298 OBJID
299 :100644 100644 OBJID OBJID M foo
300 OBJID
301 :100644 100644 OBJID OBJID M unrelated
302 OBJID
303 :000000 100644 OBJID OBJID A foo
304 :000000 100644 OBJID OBJID A unrelated
305 EOF
306 test_must_fail git cherry-pick base..anotherpick &&
307 echo c >foo &&
308 git add foo &&
309 git cherry-pick --continue &&
310 {
311 git rev-list HEAD |
312 git diff-tree --root --stdin |
313 sed "s/$_x40/OBJID/g"
314 } >actual.log &&
315 test_cmp expect foo &&
316 test_cmp expect.log actual.log
317'
318
319test_expect_success '--continue after resolving conflicts and committing' '
320 pristine_detach initial &&
321 test_must_fail git cherry-pick base..anotherpick &&
322 echo "c" >foo &&
323 git add foo &&
324 git commit &&
325 git cherry-pick --continue &&
326 test_path_is_missing .git/sequencer &&
327 {
328 git rev-list HEAD |
329 git diff-tree --root --stdin |
330 sed "s/$_x40/OBJID/g"
331 } >actual &&
332 cat >expect <<-\EOF &&
333 OBJID
334 :100644 100644 OBJID OBJID M foo
335 OBJID
336 :100644 100644 OBJID OBJID M foo
337 OBJID
338 :100644 100644 OBJID OBJID M unrelated
339 OBJID
340 :000000 100644 OBJID OBJID A foo
341 :000000 100644 OBJID OBJID A unrelated
342 EOF
343 test_cmp expect actual
344'
345
346test_expect_success '--continue asks for help after resolving patch to nil' '
347 pristine_detach conflicting &&
348 test_must_fail git cherry-pick initial..picked &&
349
350 test_cmp_rev unrelatedpick CHERRY_PICK_HEAD &&
351 git checkout HEAD -- unrelated &&
352 test_must_fail git cherry-pick --continue 2>msg &&
353 test_i18ngrep "The previous cherry-pick is now empty" msg
354'
355
356test_expect_success 'follow advice and skip nil patch' '
357 pristine_detach conflicting &&
358 test_must_fail git cherry-pick initial..picked &&
359
360 git checkout HEAD -- unrelated &&
361 test_must_fail git cherry-pick --continue &&
362 git reset &&
363 git cherry-pick --continue &&
364
365 git rev-list initial..HEAD >commits &&
366 test_line_count = 3 commits
367'
368
369test_expect_success '--continue respects opts' '
370 pristine_detach initial &&
371 test_must_fail git cherry-pick -x base..anotherpick &&
372 echo "c" >foo &&
373 git add foo &&
374 git commit &&
375 git cherry-pick --continue &&
376 test_path_is_missing .git/sequencer &&
377 git cat-file commit HEAD >anotherpick_msg &&
378 git cat-file commit HEAD~1 >picked_msg &&
379 git cat-file commit HEAD~2 >unrelatedpick_msg &&
380 git cat-file commit HEAD~3 >initial_msg &&
381 test_must_fail grep "cherry picked from" initial_msg &&
382 grep "cherry picked from" unrelatedpick_msg &&
383 grep "cherry picked from" picked_msg &&
384 grep "cherry picked from" anotherpick_msg
385'
386
387test_expect_success '--continue of single-pick respects -x' '
388 pristine_detach initial &&
389 test_must_fail git cherry-pick -x picked &&
390 echo c >foo &&
391 git add foo &&
392 git cherry-pick --continue &&
393 test_path_is_missing .git/sequencer &&
394 git cat-file commit HEAD >msg &&
395 grep "cherry picked from" msg
396'
397
398test_expect_success '--continue respects -x in first commit in multi-pick' '
399 pristine_detach initial &&
400 test_must_fail git cherry-pick -x picked anotherpick &&
401 echo c >foo &&
402 git add foo &&
403 git cherry-pick --continue &&
404 test_path_is_missing .git/sequencer &&
405 git cat-file commit HEAD^ >msg &&
406 picked=$(git rev-parse --verify picked) &&
407 grep "cherry picked from.*$picked" msg
408'
409
410test_expect_success '--signoff is not automatically propagated to resolved conflict' '
411 pristine_detach initial &&
412 test_must_fail git cherry-pick --signoff base..anotherpick &&
413 echo "c" >foo &&
414 git add foo &&
415 git commit &&
416 git cherry-pick --continue &&
417 test_path_is_missing .git/sequencer &&
418 git cat-file commit HEAD >anotherpick_msg &&
419 git cat-file commit HEAD~1 >picked_msg &&
420 git cat-file commit HEAD~2 >unrelatedpick_msg &&
421 git cat-file commit HEAD~3 >initial_msg &&
422 test_must_fail grep "Signed-off-by:" initial_msg &&
423 grep "Signed-off-by:" unrelatedpick_msg &&
424 test_must_fail grep "Signed-off-by:" picked_msg &&
425 grep "Signed-off-by:" anotherpick_msg
426'
427
428test_expect_success '--signoff dropped for implicit commit of resolution, multi-pick case' '
429 pristine_detach initial &&
430 test_must_fail git cherry-pick -s picked anotherpick &&
431 echo c >foo &&
432 git add foo &&
433 git cherry-pick --continue &&
434
435 git diff --exit-code HEAD &&
436 test_cmp_rev initial HEAD^^ &&
437 git cat-file commit HEAD^ >msg &&
438 ! grep Signed-off-by: msg
439'
440
441test_expect_success 'sign-off needs to be reaffirmed after conflict resolution, single-pick case' '
442 pristine_detach initial &&
443 test_must_fail git cherry-pick -s picked &&
444 echo c >foo &&
445 git add foo &&
446 git cherry-pick --continue &&
447
448 git diff --exit-code HEAD &&
449 test_cmp_rev initial HEAD^ &&
450 git cat-file commit HEAD >msg &&
451 ! grep Signed-off-by: msg
452'
453
454test_expect_success 'malformed instruction sheet 1' '
455 pristine_detach initial &&
456 test_must_fail git cherry-pick base..anotherpick &&
457 echo "resolved" >foo &&
458 git add foo &&
459 git commit &&
460 sed "s/pick /pick/" .git/sequencer/todo >new_sheet &&
461 cp new_sheet .git/sequencer/todo &&
462 test_must_fail git cherry-pick --continue
463'
464
465test_expect_success 'malformed instruction sheet 2' '
466 pristine_detach initial &&
467 test_must_fail git cherry-pick base..anotherpick &&
468 echo "resolved" >foo &&
469 git add foo &&
470 git commit &&
471 sed "s/pick/revert/" .git/sequencer/todo >new_sheet &&
472 cp new_sheet .git/sequencer/todo &&
473 test_must_fail git cherry-pick --continue
474'
475
476test_expect_success 'empty commit set' '
477 pristine_detach initial &&
478 test_expect_code 128 git cherry-pick base..base
479'
480
481test_done