Merge branch 'jc/maint-fmt-merge-msg-no-edit-lose-credit'
[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_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
33 test_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
47 test_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
56 test_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
68 test_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 initial..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
92 test_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
98 test_expect_success '--quit does not complain when no cherry-pick is in progress' '
99         pristine_detach initial &&
100         git cherry-pick --quit
101 '
102
103 test_expect_success '--abort requires cherry-pick in progress' '
104         pristine_detach initial &&
105         test_must_fail git cherry-pick --abort
106 '
107
108 test_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
115 test_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
136 test_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
146 test_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
156 test_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
166 test_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
174 test_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
184 test_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
193 test_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
209 test_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
233 test_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
243 test_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
252 test_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
257 test_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
263 test_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
277 test_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
295 test_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
322 test_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
349 test_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
359 test_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
372 test_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
390 test_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
401 test_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
413 test_expect_failure '--signoff is 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
431 test_expect_failure '--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
444 test_expect_failure '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
457 test_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
468 test_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
479 test_expect_success 'empty commit set' '
480         pristine_detach initial &&
481         test_expect_code 128 git cherry-pick base..base
482 '
483
484 test_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
495 test_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
506 test_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
520 test_done