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