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/$_x40/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 'cherry-pick --abort to cancel multiple revert' '
151 pristine_detach anotherpick &&
152 test_expect_code 1 git revert base..picked &&
153 git cherry-pick --abort &&
154 test_path_is_missing .git/sequencer &&
155 test_cmp_rev anotherpick HEAD &&
156 git update-index --refresh &&
157 git diff-index --exit-code HEAD
158'
159
160test_expect_success 'revert --abort works, too' '
161 pristine_detach anotherpick &&
162 test_expect_code 1 git revert base..picked &&
163 git revert --abort &&
164 test_path_is_missing .git/sequencer &&
165 test_cmp_rev anotherpick HEAD
166'
167
168test_expect_success '--abort to cancel single revert' '
169 pristine_detach anotherpick &&
170 test_expect_code 1 git revert picked &&
171 git revert --abort &&
172 test_path_is_missing .git/sequencer &&
173 test_cmp_rev anotherpick HEAD &&
174 git update-index --refresh &&
175 git diff-index --exit-code HEAD
176'
177
178test_expect_success '--abort keeps unrelated change, easy case' '
179 pristine_detach unrelatedpick &&
180 echo changed >expect &&
181 test_expect_code 1 git cherry-pick picked..yetanotherpick &&
182 echo changed >unrelated &&
183 git cherry-pick --abort &&
184 test_cmp expect unrelated
185'
186
187test_expect_success '--abort refuses to clobber unrelated change, harder case' '
188 pristine_detach initial &&
189 echo changed >expect &&
190 test_expect_code 1 git cherry-pick base..anotherpick &&
191 echo changed >unrelated &&
192 test_must_fail git cherry-pick --abort &&
193 test_cmp expect unrelated &&
194 git rev-list HEAD >log &&
195 test_line_count = 2 log &&
196 test_must_fail git update-index --refresh &&
197
198 git checkout unrelated &&
199 git cherry-pick --abort &&
200 test_cmp_rev initial HEAD
201'
202
203test_expect_success 'cherry-pick still writes sequencer state when one commit is left' '
204 pristine_detach initial &&
205 test_expect_code 1 git cherry-pick base..picked &&
206 test_path_is_dir .git/sequencer &&
207 echo "resolved" >foo &&
208 git add foo &&
209 git commit &&
210 {
211 git rev-list HEAD |
212 git diff-tree --root --stdin |
213 sed "s/$_x40/OBJID/g"
214 } >actual &&
215 cat >expect <<-\EOF &&
216 OBJID
217 :100644 100644 OBJID OBJID M foo
218 OBJID
219 :100644 100644 OBJID OBJID M unrelated
220 OBJID
221 :000000 100644 OBJID OBJID A foo
222 :000000 100644 OBJID OBJID A unrelated
223 EOF
224 test_cmp expect actual
225'
226
227test_expect_success '--abort after last commit in sequence' '
228 pristine_detach initial &&
229 test_expect_code 1 git cherry-pick base..picked &&
230 git cherry-pick --abort &&
231 test_path_is_missing .git/sequencer &&
232 test_cmp_rev initial HEAD &&
233 git update-index --refresh &&
234 git diff-index --exit-code HEAD
235'
236
237test_expect_success 'cherry-pick does not implicitly stomp an existing operation' '
238 pristine_detach initial &&
239 test_expect_code 1 git cherry-pick base..anotherpick &&
240 test-chmtime -v +0 .git/sequencer >expect &&
241 test_expect_code 128 git cherry-pick unrelatedpick &&
242 test-chmtime -v +0 .git/sequencer >actual &&
243 test_cmp expect actual
244'
245
246test_expect_success '--continue complains when no cherry-pick is in progress' '
247 pristine_detach initial &&
248 test_expect_code 128 git cherry-pick --continue
249'
250
251test_expect_success '--continue complains when there are unresolved conflicts' '
252 pristine_detach initial &&
253 test_expect_code 1 git cherry-pick base..anotherpick &&
254 test_expect_code 128 git cherry-pick --continue
255'
256
257test_expect_success '--continue of single cherry-pick' '
258 pristine_detach initial &&
259 echo c >expect &&
260 test_must_fail git cherry-pick picked &&
261 echo c >foo &&
262 git add foo &&
263 git cherry-pick --continue &&
264
265 test_cmp expect foo &&
266 test_cmp_rev initial HEAD^ &&
267 git diff --exit-code HEAD &&
268 test_must_fail git rev-parse --verify CHERRY_PICK_HEAD
269'
270
271test_expect_success '--continue of single revert' '
272 pristine_detach initial &&
273 echo resolved >expect &&
274 echo "Revert \"picked\"" >expect.msg &&
275 test_must_fail git revert picked &&
276 echo resolved >foo &&
277 git add foo &&
278 git cherry-pick --continue &&
279
280 git diff --exit-code HEAD &&
281 test_cmp expect foo &&
282 test_cmp_rev initial HEAD^ &&
283 git diff-tree -s --pretty=tformat:%s HEAD >msg &&
284 test_cmp expect.msg msg &&
285 test_must_fail git rev-parse --verify CHERRY_PICK_HEAD &&
286 test_must_fail git rev-parse --verify REVERT_HEAD
287'
288
289test_expect_success '--continue after resolving conflicts' '
290 pristine_detach initial &&
291 echo d >expect &&
292 cat >expect.log <<-\EOF &&
293 OBJID
294 :100644 100644 OBJID OBJID M foo
295 OBJID
296 :100644 100644 OBJID OBJID M foo
297 OBJID
298 :100644 100644 OBJID OBJID M unrelated
299 OBJID
300 :000000 100644 OBJID OBJID A foo
301 :000000 100644 OBJID OBJID A unrelated
302 EOF
303 test_must_fail git cherry-pick base..anotherpick &&
304 echo c >foo &&
305 git add foo &&
306 git cherry-pick --continue &&
307 {
308 git rev-list HEAD |
309 git diff-tree --root --stdin |
310 sed "s/$_x40/OBJID/g"
311 } >actual.log &&
312 test_cmp expect foo &&
313 test_cmp expect.log actual.log
314'
315
316test_expect_success '--continue after resolving conflicts and committing' '
317 pristine_detach initial &&
318 test_expect_code 1 git cherry-pick base..anotherpick &&
319 echo "c" >foo &&
320 git add foo &&
321 git commit &&
322 git cherry-pick --continue &&
323 test_path_is_missing .git/sequencer &&
324 {
325 git rev-list HEAD |
326 git diff-tree --root --stdin |
327 sed "s/$_x40/OBJID/g"
328 } >actual &&
329 cat >expect <<-\EOF &&
330 OBJID
331 :100644 100644 OBJID OBJID M foo
332 OBJID
333 :100644 100644 OBJID OBJID M foo
334 OBJID
335 :100644 100644 OBJID OBJID M unrelated
336 OBJID
337 :000000 100644 OBJID OBJID A foo
338 :000000 100644 OBJID OBJID A unrelated
339 EOF
340 test_cmp expect actual
341'
342
343test_expect_success '--continue asks for help after resolving patch to nil' '
344 pristine_detach conflicting &&
345 test_must_fail git cherry-pick initial..picked &&
346
347 test_cmp_rev unrelatedpick CHERRY_PICK_HEAD &&
348 git checkout HEAD -- unrelated &&
349 test_must_fail git cherry-pick --continue 2>msg &&
350 test_i18ngrep "The previous cherry-pick is now empty" msg
351'
352
353test_expect_success 'follow advice and skip nil patch' '
354 pristine_detach conflicting &&
355 test_must_fail git cherry-pick initial..picked &&
356
357 git checkout HEAD -- unrelated &&
358 test_must_fail git cherry-pick --continue &&
359 git reset &&
360 git cherry-pick --continue &&
361
362 git rev-list initial..HEAD >commits &&
363 test_line_count = 3 commits
364'
365
366test_expect_success '--continue respects opts' '
367 pristine_detach initial &&
368 test_expect_code 1 git cherry-pick -x base..anotherpick &&
369 echo "c" >foo &&
370 git add foo &&
371 git commit &&
372 git cherry-pick --continue &&
373 test_path_is_missing .git/sequencer &&
374 git cat-file commit HEAD >anotherpick_msg &&
375 git cat-file commit HEAD~1 >picked_msg &&
376 git cat-file commit HEAD~2 >unrelatedpick_msg &&
377 git cat-file commit HEAD~3 >initial_msg &&
378 test_must_fail grep "cherry picked from" initial_msg &&
379 grep "cherry picked from" unrelatedpick_msg &&
380 grep "cherry picked from" picked_msg &&
381 grep "cherry picked from" anotherpick_msg
382'
383
384test_expect_success '--continue of single-pick respects -x' '
385 pristine_detach initial &&
386 test_must_fail git cherry-pick -x picked &&
387 echo c >foo &&
388 git add foo &&
389 git cherry-pick --continue &&
390 test_path_is_missing .git/sequencer &&
391 git cat-file commit HEAD >msg &&
392 grep "cherry picked from" msg
393'
394
395test_expect_success '--continue respects -x in first commit in multi-pick' '
396 pristine_detach initial &&
397 test_must_fail git cherry-pick -x picked anotherpick &&
398 echo c >foo &&
399 git add foo &&
400 git cherry-pick --continue &&
401 test_path_is_missing .git/sequencer &&
402 git cat-file commit HEAD^ >msg &&
403 picked=$(git rev-parse --verify picked) &&
404 grep "cherry picked from.*$picked" msg
405'
406
407test_expect_failure '--signoff is automatically propagated to resolved conflict' '
408 pristine_detach initial &&
409 test_expect_code 1 git cherry-pick --signoff base..anotherpick &&
410 echo "c" >foo &&
411 git add foo &&
412 git commit &&
413 git cherry-pick --continue &&
414 test_path_is_missing .git/sequencer &&
415 git cat-file commit HEAD >anotherpick_msg &&
416 git cat-file commit HEAD~1 >picked_msg &&
417 git cat-file commit HEAD~2 >unrelatedpick_msg &&
418 git cat-file commit HEAD~3 >initial_msg &&
419 test_must_fail grep "Signed-off-by:" initial_msg &&
420 grep "Signed-off-by:" unrelatedpick_msg &&
421 test_must_fail grep "Signed-off-by:" picked_msg &&
422 grep "Signed-off-by:" anotherpick_msg
423'
424
425test_expect_failure '--signoff dropped for implicit commit of resolution, multi-pick case' '
426 pristine_detach initial &&
427 test_must_fail git cherry-pick -s picked anotherpick &&
428 echo c >foo &&
429 git add foo &&
430 git cherry-pick --continue &&
431
432 git diff --exit-code HEAD &&
433 test_cmp_rev initial HEAD^^ &&
434 git cat-file commit HEAD^ >msg &&
435 ! grep Signed-off-by: msg
436'
437
438test_expect_failure 'sign-off needs to be reaffirmed after conflict resolution, single-pick case' '
439 pristine_detach initial &&
440 test_must_fail git cherry-pick -s picked &&
441 echo c >foo &&
442 git add foo &&
443 git cherry-pick --continue &&
444
445 git diff --exit-code HEAD &&
446 test_cmp_rev initial HEAD^ &&
447 git cat-file commit HEAD >msg &&
448 ! grep Signed-off-by: msg
449'
450
451test_expect_success 'malformed instruction sheet 1' '
452 pristine_detach initial &&
453 test_expect_code 1 git cherry-pick base..anotherpick &&
454 echo "resolved" >foo &&
455 git add foo &&
456 git commit &&
457 sed "s/pick /pick/" .git/sequencer/todo >new_sheet &&
458 cp new_sheet .git/sequencer/todo &&
459 test_expect_code 128 git cherry-pick --continue
460'
461
462test_expect_success 'malformed instruction sheet 2' '
463 pristine_detach initial &&
464 test_expect_code 1 git cherry-pick base..anotherpick &&
465 echo "resolved" >foo &&
466 git add foo &&
467 git commit &&
468 sed "s/pick/revert/" .git/sequencer/todo >new_sheet &&
469 cp new_sheet .git/sequencer/todo &&
470 test_expect_code 128 git cherry-pick --continue
471'
472
473test_expect_success 'empty commit set' '
474 pristine_detach initial &&
475 test_expect_code 128 git cherry-pick base..base
476'
477
478test_expect_success 'malformed instruction sheet 3' '
479 pristine_detach initial &&
480 test_expect_code 1 git cherry-pick base..anotherpick &&
481 echo "resolved" >foo &&
482 git add foo &&
483 git commit &&
484 sed "s/pick \([0-9a-f]*\)/pick $_r10/" .git/sequencer/todo >new_sheet &&
485 cp new_sheet .git/sequencer/todo &&
486 test_expect_code 128 git cherry-pick --continue
487'
488
489test_expect_success 'instruction sheet, fat-fingers version' '
490 pristine_detach initial &&
491 test_expect_code 1 git cherry-pick base..anotherpick &&
492 echo "c" >foo &&
493 git add foo &&
494 git commit &&
495 sed "s/pick \([0-9a-f]*\)/pick \1 /" .git/sequencer/todo >new_sheet &&
496 cp new_sheet .git/sequencer/todo &&
497 git cherry-pick --continue
498'
499
500test_expect_success 'commit descriptions in insn sheet are optional' '
501 pristine_detach initial &&
502 test_expect_code 1 git cherry-pick base..anotherpick &&
503 echo "c" >foo &&
504 git add foo &&
505 git commit &&
506 cut -d" " -f1,2 .git/sequencer/todo >new_sheet &&
507 cp new_sheet .git/sequencer/todo &&
508 git cherry-pick --continue &&
509 test_path_is_missing .git/sequencer &&
510 git rev-list HEAD >commits &&
511 test_line_count = 4 commits
512'
513
514test_done