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