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