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