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