Merge branch 'jk/parse-options-concat'
[git] / t / t3510-cherry-pick-sequence.sh
1 #!/bin/sh
2
3 test_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
20 pristine_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
27 test_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
41 test_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
50 test_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
62 test_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
86 test_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
92 test_expect_success '--quit does not complain when no cherry-pick is in progress' '
93         pristine_detach initial &&
94         git cherry-pick --quit
95 '
96
97 test_expect_success '--abort requires cherry-pick in progress' '
98         pristine_detach initial &&
99         test_must_fail git cherry-pick --abort
100 '
101
102 test_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
109 test_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
130 test_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
140 test_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
150 test_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
160 test_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
168 test_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
178 test_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
187 test_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
203 test_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
227 test_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
237 test_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
246 test_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
251 test_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
257 test_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
271 test_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
289 test_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
316 test_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
343 test_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
353 test_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
366 test_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
384 test_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
395 test_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
407 test_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
425 test_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
438 test_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
451 test_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
462 test_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
473 test_expect_success 'empty commit set' '
474         pristine_detach initial &&
475         test_expect_code 128 git cherry-pick base..base
476 '
477
478 test_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
489 test_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
500 test_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
514 test_done