The second batch
[git] / t / t3701-add-interactive.sh
1 #!/bin/sh
2
3 test_description='add -i basic tests'
4 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
5 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
6
7 . ./test-lib.sh
8 . "$TEST_DIRECTORY"/lib-terminal.sh
9
10 if ! test_have_prereq PERL
11 then
12         skip_all='skipping add -i tests, perl not available'
13         test_done
14 fi
15
16 diff_cmp () {
17         for x
18         do
19                 sed  -e '/^index/s/[0-9a-f]*[1-9a-f][0-9a-f]*\.\./1234567../' \
20                      -e '/^index/s/\.\.[0-9a-f]*[1-9a-f][0-9a-f]*/..9abcdef/' \
21                      -e '/^index/s/ 00*\.\./ 0000000../' \
22                      -e '/^index/s/\.\.00*$/..0000000/' \
23                      -e '/^index/s/\.\.00* /..0000000 /' \
24                      "$x" >"$x.filtered"
25         done
26         test_cmp "$1.filtered" "$2.filtered"
27 }
28
29 # This function uses a trick to manipulate the interactive add to use color:
30 # the `want_color()` function special-cases the situation where a pager was
31 # spawned and Git now wants to output colored text: to detect that situation,
32 # the environment variable `GIT_PAGER_IN_USE` is set. However, color is
33 # suppressed despite that environment variable if the `TERM` variable
34 # indicates a dumb terminal, so we set that variable, too.
35
36 force_color () {
37         # The first element of $@ may be a shell function, as a result POSIX
38         # does not guarantee that "one-shot assignment" will not persist after
39         # the function call. Thus, we prevent these variables from escaping
40         # this function's context with this subshell.
41         (
42                 GIT_PAGER_IN_USE=true &&
43                 TERM=vt100 &&
44                 export GIT_PAGER_IN_USE TERM &&
45                 "$@"
46         )
47 }
48
49 test_expect_success 'setup (initial)' '
50         echo content >file &&
51         git add file &&
52         echo more >>file &&
53         echo lines >>file
54 '
55 test_expect_success 'status works (initial)' '
56         git add -i </dev/null >output &&
57         grep "+1/-0 *+2/-0 file" output
58 '
59
60 test_expect_success 'setup expected' '
61         cat >expected <<-\EOF
62         new file mode 100644
63         index 0000000..d95f3ad
64         --- /dev/null
65         +++ b/file
66         @@ -0,0 +1 @@
67         +content
68         EOF
69 '
70
71 test_expect_success 'diff works (initial)' '
72         test_write_lines d 1 | git add -i >output &&
73         sed -ne "/new file/,/content/p" <output >diff &&
74         diff_cmp expected diff
75 '
76 test_expect_success 'revert works (initial)' '
77         git add file &&
78         test_write_lines r 1 | git add -i &&
79         git ls-files >output &&
80         ! grep . output
81 '
82
83 test_expect_success 'add untracked (multiple)' '
84         test_when_finished "git reset && rm [1-9]" &&
85         touch $(test_seq 9) &&
86         test_write_lines a "2-5 8-" | git add -i -- [1-9] &&
87         test_write_lines 2 3 4 5 8 9 >expected &&
88         git ls-files [1-9] >output &&
89         test_cmp expected output
90 '
91
92 test_expect_success 'setup (commit)' '
93         echo baseline >file &&
94         git add file &&
95         git commit -m commit &&
96         echo content >>file &&
97         git add file &&
98         echo more >>file &&
99         echo lines >>file
100 '
101 test_expect_success 'status works (commit)' '
102         git add -i </dev/null >output &&
103         grep "+1/-0 *+2/-0 file" output
104 '
105
106 test_expect_success 'setup expected' '
107         cat >expected <<-\EOF
108         index 180b47c..b6f2c08 100644
109         --- a/file
110         +++ b/file
111         @@ -1 +1,2 @@
112          baseline
113         +content
114         EOF
115 '
116
117 test_expect_success 'diff works (commit)' '
118         test_write_lines d 1 | git add -i >output &&
119         sed -ne "/^index/,/content/p" <output >diff &&
120         diff_cmp expected diff
121 '
122 test_expect_success 'revert works (commit)' '
123         git add file &&
124         test_write_lines r 1 | git add -i &&
125         git add -i </dev/null >output &&
126         grep "unchanged *+3/-0 file" output
127 '
128
129 test_expect_success 'setup expected' '
130         cat >expected <<-\EOF
131         EOF
132 '
133
134 test_expect_success 'dummy edit works' '
135         test_set_editor : &&
136         test_write_lines e a | git add -p &&
137         git diff > diff &&
138         diff_cmp expected diff
139 '
140
141 test_expect_success 'setup patch' '
142         cat >patch <<-\EOF
143         @@ -1,1 +1,4 @@
144          this
145         +patch
146         -does not
147          apply
148         EOF
149 '
150
151 test_expect_success 'setup fake editor' '
152         write_script "fake_editor.sh" <<-\EOF &&
153         mv -f "$1" oldpatch &&
154         mv -f patch "$1"
155         EOF
156         test_set_editor "$(pwd)/fake_editor.sh"
157 '
158
159 test_expect_success 'bad edit rejected' '
160         git reset &&
161         test_write_lines e n d | git add -p >output &&
162         grep "hunk does not apply" output
163 '
164
165 test_expect_success 'setup patch' '
166         cat >patch <<-\EOF
167         this patch
168         is garbage
169         EOF
170 '
171
172 test_expect_success 'garbage edit rejected' '
173         git reset &&
174         test_write_lines e n d | git add -p >output &&
175         grep "hunk does not apply" output
176 '
177
178 test_expect_success 'setup patch' '
179         cat >patch <<-\EOF
180         @@ -1,0 +1,0 @@
181          baseline
182         +content
183         +newcontent
184         +lines
185         EOF
186 '
187
188 test_expect_success 'setup expected' '
189         cat >expected <<-\EOF
190         diff --git a/file b/file
191         index b5dd6c9..f910ae9 100644
192         --- a/file
193         +++ b/file
194         @@ -1,4 +1,4 @@
195          baseline
196          content
197         -newcontent
198         +more
199          lines
200         EOF
201 '
202
203 test_expect_success 'real edit works' '
204         test_write_lines e n d | git add -p &&
205         git diff >output &&
206         diff_cmp expected output
207 '
208
209 test_expect_success 'setup file' '
210         test_write_lines a "" b "" c >file &&
211         git add file &&
212         test_write_lines a "" d "" c >file
213 '
214
215 test_expect_success 'setup patch' '
216         SP=" " &&
217         NULL="" &&
218         cat >patch <<-EOF
219         @@ -1,4 +1,4 @@
220          a
221         $NULL
222         -b
223         +f
224         $SP
225         c
226         EOF
227 '
228
229 test_expect_success 'setup expected' '
230         cat >expected <<-EOF
231         diff --git a/file b/file
232         index b5dd6c9..f910ae9 100644
233         --- a/file
234         +++ b/file
235         @@ -1,5 +1,5 @@
236          a
237         $SP
238         -f
239         +d
240         $SP
241          c
242         EOF
243 '
244
245 test_expect_success 'edit can strip spaces from empty context lines' '
246         test_write_lines e n q | git add -p 2>error &&
247         test_must_be_empty error &&
248         git diff >output &&
249         diff_cmp expected output
250 '
251
252 test_expect_success 'skip files similarly as commit -a' '
253         git reset &&
254         echo file >.gitignore &&
255         echo changed >file &&
256         echo y | git add -p file &&
257         git diff >output &&
258         git reset &&
259         git commit -am commit &&
260         git diff >expected &&
261         diff_cmp expected output &&
262         git reset --hard HEAD^
263 '
264 rm -f .gitignore
265
266 test_expect_success FILEMODE 'patch does not affect mode' '
267         git reset --hard &&
268         echo content >>file &&
269         chmod +x file &&
270         printf "n\\ny\\n" | git add -p &&
271         git show :file | grep content &&
272         git diff file | grep "new mode"
273 '
274
275 test_expect_success FILEMODE 'stage mode but not hunk' '
276         git reset --hard &&
277         echo content >>file &&
278         chmod +x file &&
279         printf "y\\nn\\n" | git add -p &&
280         git diff --cached file | grep "new mode" &&
281         git diff          file | grep "+content"
282 '
283
284
285 test_expect_success FILEMODE 'stage mode and hunk' '
286         git reset --hard &&
287         echo content >>file &&
288         chmod +x file &&
289         printf "y\\ny\\n" | git add -p &&
290         git diff --cached file | grep "new mode" &&
291         git diff --cached file | grep "+content" &&
292         test -z "$(git diff file)"
293 '
294
295 # end of tests disabled when filemode is not usable
296
297 test_expect_success 'different prompts for mode change/deleted' '
298         git reset --hard &&
299         >file &&
300         >deleted &&
301         git add --chmod=+x file deleted &&
302         echo changed >file &&
303         rm deleted &&
304         test_write_lines n n n |
305         git -c core.filemode=true add -p >actual &&
306         sed -n "s/^\(([0-9/]*) Stage .*?\).*/\1/p" actual >actual.filtered &&
307         cat >expect <<-\EOF &&
308         (1/1) Stage deletion [y,n,q,a,d,?]?
309         (1/2) Stage mode change [y,n,q,a,d,j,J,g,/,?]?
310         (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]?
311         EOF
312         test_cmp expect actual.filtered
313 '
314
315 test_expect_success 'correct message when there is nothing to do' '
316         git reset --hard &&
317         git add -p 2>err &&
318         test_i18ngrep "No changes" err &&
319         printf "\\0123" >binary &&
320         git add binary &&
321         printf "\\0abc" >binary &&
322         git add -p 2>err &&
323         test_i18ngrep "Only binary files changed" err
324 '
325
326 test_expect_success 'setup again' '
327         git reset --hard &&
328         test_chmod +x file &&
329         echo content >>file
330 '
331
332 # Write the patch file with a new line at the top and bottom
333 test_expect_success 'setup patch' '
334         cat >patch <<-\EOF
335         index 180b47c..b6f2c08 100644
336         --- a/file
337         +++ b/file
338         @@ -1,2 +1,4 @@
339         +firstline
340          baseline
341          content
342         +lastline
343         \ No newline at end of file
344         EOF
345 '
346
347 # Expected output, diff is similar to the patch but w/ diff at the top
348 test_expect_success 'setup expected' '
349         echo diff --git a/file b/file >expected &&
350         cat patch |sed "/^index/s/ 100644/ 100755/" >>expected &&
351         cat >expected-output <<-\EOF
352         --- a/file
353         +++ b/file
354         @@ -1,2 +1,4 @@
355         +firstline
356          baseline
357          content
358         +lastline
359         \ No newline at end of file
360         @@ -1,2 +1,3 @@
361         +firstline
362          baseline
363          content
364         @@ -1,2 +2,3 @@
365          baseline
366          content
367         +lastline
368         \ No newline at end of file
369         EOF
370 '
371
372 # Test splitting the first patch, then adding both
373 test_expect_success 'add first line works' '
374         git commit -am "clear local changes" &&
375         git apply patch &&
376         printf "%s\n" s y y | git add -p file 2>error |
377                 sed -n -e "s/^([1-2]\/[1-2]) Stage this hunk[^@]*\(@@ .*\)/\1/" \
378                        -e "/^[-+@ \\\\]"/p  >output &&
379         test_must_be_empty error &&
380         git diff --cached >diff &&
381         diff_cmp expected diff &&
382         test_cmp expected-output output
383 '
384
385 test_expect_success 'setup expected' '
386         cat >expected <<-\EOF
387         diff --git a/non-empty b/non-empty
388         deleted file mode 100644
389         index d95f3ad..0000000
390         --- a/non-empty
391         +++ /dev/null
392         @@ -1 +0,0 @@
393         -content
394         EOF
395 '
396
397 test_expect_success 'deleting a non-empty file' '
398         git reset --hard &&
399         echo content >non-empty &&
400         git add non-empty &&
401         git commit -m non-empty &&
402         rm non-empty &&
403         echo y | git add -p non-empty &&
404         git diff --cached >diff &&
405         diff_cmp expected diff
406 '
407
408 test_expect_success 'setup expected' '
409         cat >expected <<-\EOF
410         diff --git a/empty b/empty
411         deleted file mode 100644
412         index e69de29..0000000
413         EOF
414 '
415
416 test_expect_success 'deleting an empty file' '
417         git reset --hard &&
418         > empty &&
419         git add empty &&
420         git commit -m empty &&
421         rm empty &&
422         echo y | git add -p empty &&
423         git diff --cached >diff &&
424         diff_cmp expected diff
425 '
426
427 test_expect_success 'adding an empty file' '
428         git init added &&
429         (
430                 cd added &&
431                 test_commit initial &&
432                 >empty &&
433                 git add empty &&
434                 test_tick &&
435                 git commit -m empty &&
436                 git tag added-file &&
437                 git reset --hard HEAD^ &&
438                 test_path_is_missing empty &&
439
440                 echo y | git checkout -p added-file -- >actual &&
441                 test_path_is_file empty &&
442                 test_i18ngrep "Apply addition to index and worktree" actual
443         )
444 '
445
446 test_expect_success 'split hunk setup' '
447         git reset --hard &&
448         test_write_lines 10 20 30 40 50 60 >test &&
449         git add test &&
450         test_tick &&
451         git commit -m test &&
452
453         test_write_lines 10 15 20 21 22 23 24 30 40 50 60 >test
454 '
455
456 test_expect_success 'goto hunk' '
457         test_when_finished "git reset" &&
458         tr _ " " >expect <<-EOF &&
459         (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? + 1:  -1,2 +1,3          +15
460         _ 2:  -2,4 +3,8          +21
461         go to which hunk? @@ -1,2 +1,3 @@
462         _10
463         +15
464         _20
465         (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?_
466         EOF
467         test_write_lines s y g 1 | git add -p >actual &&
468         tail -n 7 <actual >actual.trimmed &&
469         test_cmp expect actual.trimmed
470 '
471
472 test_expect_success 'navigate to hunk via regex' '
473         test_when_finished "git reset" &&
474         tr _ " " >expect <<-EOF &&
475         (2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? @@ -1,2 +1,3 @@
476         _10
477         +15
478         _20
479         (1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?_
480         EOF
481         test_write_lines s y /1,2 | git add -p >actual &&
482         tail -n 5 <actual >actual.trimmed &&
483         test_cmp expect actual.trimmed
484 '
485
486 test_expect_success 'split hunk "add -p (edit)"' '
487         # Split, say Edit and do nothing.  Then:
488         #
489         # 1. Broken version results in a patch that does not apply and
490         # only takes [y/n] (edit again) so the first q is discarded
491         # and then n attempts to discard the edit. Repeat q enough
492         # times to get out.
493         #
494         # 2. Correct version applies the (not)edited version, and asks
495         #    about the next hunk, against which we say q and program
496         #    exits.
497         printf "%s\n" s e     q n q q |
498         EDITOR=: git add -p &&
499         git diff >actual &&
500         ! grep "^+15" actual
501 '
502
503 test_expect_failure 'split hunk "add -p (no, yes, edit)"' '
504         test_write_lines 5 10 20 21 30 31 40 50 60 >test &&
505         git reset &&
506         # test sequence is s(plit), n(o), y(es), e(dit)
507         # q n q q is there to make sure we exit at the end.
508         printf "%s\n" s n y e   q n q q |
509         EDITOR=: git add -p 2>error &&
510         test_must_be_empty error &&
511         git diff >actual &&
512         ! grep "^+31" actual
513 '
514
515 test_expect_success 'split hunk with incomplete line at end' '
516         git reset --hard &&
517         printf "missing LF" >>test &&
518         git add test &&
519         test_write_lines before 10 20 30 40 50 60 70 >test &&
520         git grep --cached missing &&
521         test_write_lines s n y q | git add -p &&
522         test_must_fail git grep --cached missing &&
523         git grep before &&
524         test_must_fail git grep --cached before
525 '
526
527 test_expect_failure 'edit, adding lines to the first hunk' '
528         test_write_lines 10 11 20 30 40 50 51 60 >test &&
529         git reset &&
530         tr _ " " >patch <<-EOF &&
531         @@ -1,5 +1,6 @@
532         _10
533         +11
534         +12
535         _20
536         +21
537         +22
538         _30
539         EOF
540         # test sequence is s(plit), e(dit), n(o)
541         # q n q q is there to make sure we exit at the end.
542         printf "%s\n" s e n   q n q q |
543         EDITOR=./fake_editor.sh git add -p 2>error &&
544         test_must_be_empty error &&
545         git diff --cached >actual &&
546         grep "^+22" actual
547 '
548
549 test_expect_success 'patch mode ignores unmerged entries' '
550         git reset --hard &&
551         test_commit conflict &&
552         test_commit non-conflict &&
553         git checkout -b side &&
554         test_commit side conflict.t &&
555         git checkout main &&
556         test_commit main conflict.t &&
557         test_must_fail git merge side &&
558         echo changed >non-conflict.t &&
559         echo y | git add -p >output &&
560         ! grep a/conflict.t output &&
561         cat >expected <<-\EOF &&
562         * Unmerged path conflict.t
563         diff --git a/non-conflict.t b/non-conflict.t
564         index f766221..5ea2ed4 100644
565         --- a/non-conflict.t
566         +++ b/non-conflict.t
567         @@ -1 +1 @@
568         -non-conflict
569         +changed
570         EOF
571         git diff --cached >diff &&
572         diff_cmp expected diff
573 '
574
575 test_expect_success 'index is refreshed after applying patch' '
576         git reset --hard &&
577         echo content >test &&
578         printf y | git add -p &&
579         git diff-files --exit-code
580 '
581
582 test_expect_success 'diffs can be colorized' '
583         git reset --hard &&
584
585         echo content >test &&
586         printf y >y &&
587         force_color git add -p >output 2>&1 <y &&
588         git diff-files --exit-code &&
589
590         # We do not want to depend on the exact coloring scheme
591         # git uses for diffs, so just check that we saw some kind of color.
592         grep "$(printf "\\033")" output
593 '
594
595 test_expect_success 'colors can be overridden' '
596         git reset --hard &&
597         test_when_finished "git rm -f color-test" &&
598         test_write_lines context old more-context >color-test &&
599         git add color-test &&
600         test_write_lines context new more-context another-one >color-test &&
601
602         echo trigger an error message >input &&
603         force_color git \
604                 -c color.interactive.error=blue \
605                 add -i 2>err.raw <input &&
606         test_decode_color <err.raw >err &&
607         grep "<BLUE>Huh (trigger)?<RESET>" err &&
608
609         test_write_lines help quit >input &&
610         force_color git \
611                 -c color.interactive.header=red \
612                 -c color.interactive.help=green \
613                 -c color.interactive.prompt=yellow \
614                 add -i >actual.raw <input &&
615         test_decode_color <actual.raw >actual &&
616         cat >expect <<-\EOF &&
617         <RED>           staged     unstaged path<RESET>
618           1:        +3/-0        +2/-1 color-test
619
620         <RED>*** Commands ***<RESET>
621           1: <YELLOW>s<RESET>tatus        2: <YELLOW>u<RESET>pdate        3: <YELLOW>r<RESET>evert        4: <YELLOW>a<RESET>dd untracked
622           5: <YELLOW>p<RESET>atch         6: <YELLOW>d<RESET>iff          7: <YELLOW>q<RESET>uit          8: <YELLOW>h<RESET>elp
623         <YELLOW>What now<RESET>> <GREEN>status        - show paths with changes<RESET>
624         <GREEN>update        - add working tree state to the staged set of changes<RESET>
625         <GREEN>revert        - revert staged set of changes back to the HEAD version<RESET>
626         <GREEN>patch         - pick hunks and update selectively<RESET>
627         <GREEN>diff          - view diff between HEAD and index<RESET>
628         <GREEN>add untracked - add contents of untracked files to the staged set of changes<RESET>
629         <RED>*** Commands ***<RESET>
630           1: <YELLOW>s<RESET>tatus        2: <YELLOW>u<RESET>pdate        3: <YELLOW>r<RESET>evert        4: <YELLOW>a<RESET>dd untracked
631           5: <YELLOW>p<RESET>atch         6: <YELLOW>d<RESET>iff          7: <YELLOW>q<RESET>uit          8: <YELLOW>h<RESET>elp
632         <YELLOW>What now<RESET>> Bye.
633         EOF
634         test_cmp expect actual &&
635
636         : exercise recolor_hunk by editing and then look at the hunk again &&
637         test_write_lines s e K q >input &&
638         force_color git \
639                 -c color.interactive.prompt=yellow \
640                 -c color.diff.meta=italic \
641                 -c color.diff.frag=magenta \
642                 -c color.diff.context=cyan \
643                 -c color.diff.old=bold \
644                 -c color.diff.new=blue \
645                 -c core.editor=touch \
646                 add -p >actual.raw <input &&
647         test_decode_color <actual.raw >actual.decoded &&
648         sed "s/index [0-9a-f]*\\.\\.[0-9a-f]* 100644/<INDEX-LINE>/" <actual.decoded >actual &&
649         cat >expect <<-\EOF &&
650         <ITALIC>diff --git a/color-test b/color-test<RESET>
651         <ITALIC><INDEX-LINE><RESET>
652         <ITALIC>--- a/color-test<RESET>
653         <ITALIC>+++ b/color-test<RESET>
654         <MAGENTA>@@ -1,3 +1,4 @@<RESET>
655         <CYAN> context<RESET>
656         <BOLD>-old<RESET>
657         <BLUE>+<RESET><BLUE>new<RESET>
658         <CYAN> more-context<RESET>
659         <BLUE>+<RESET><BLUE>another-one<RESET>
660         <YELLOW>(1/1) Stage this hunk [y,n,q,a,d,s,e,?]? <RESET><BOLD>Split into 2 hunks.<RESET>
661         <MAGENTA>@@ -1,3 +1,3 @@<RESET>
662         <CYAN> context<RESET>
663         <BOLD>-old<RESET>
664         <BLUE>+<RESET><BLUE>new<RESET>
665         <CYAN> more-context<RESET>
666         <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? <RESET><MAGENTA>@@ -3 +3,2 @@<RESET>
667         <CYAN> more-context<RESET>
668         <BLUE>+<RESET><BLUE>another-one<RESET>
669         <YELLOW>(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? <RESET><MAGENTA>@@ -1,3 +1,3 @@<RESET>
670         <CYAN> context<RESET>
671         <BOLD>-old<RESET>
672         <BLUE>+new<RESET>
673         <CYAN> more-context<RESET>
674         <YELLOW>(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? <RESET>
675         EOF
676         test_cmp expect actual
677 '
678
679 test_expect_success 'colorized diffs respect diff.wsErrorHighlight' '
680         git reset --hard &&
681
682         echo "old " >test &&
683         git add test &&
684         echo "new " >test &&
685
686         printf y >y &&
687         force_color git -c diff.wsErrorHighlight=all add -p >output.raw 2>&1 <y &&
688         test_decode_color <output.raw >output &&
689         grep "old<" output
690 '
691
692 test_expect_success 'diffFilter filters diff' '
693         git reset --hard &&
694
695         echo content >test &&
696         test_config interactive.diffFilter "sed s/^/foo:/" &&
697         printf y >y &&
698         force_color git add -p >output 2>&1 <y &&
699
700         # avoid depending on the exact coloring or content of the prompts,
701         # and just make sure we saw our diff prefixed
702         grep foo:.*content output
703 '
704
705 test_expect_success 'detect bogus diffFilter output' '
706         git reset --hard &&
707
708         echo content >test &&
709         test_config interactive.diffFilter "sed 1d" &&
710         printf y >y &&
711         force_color test_must_fail git add -p <y
712 '
713
714 test_expect_success 'diff.algorithm is passed to `git diff-files`' '
715         git reset --hard &&
716
717         >file &&
718         git add file &&
719         echo changed >file &&
720         test_must_fail git -c diff.algorithm=bogus add -p 2>err &&
721         test_i18ngrep "error: option diff-algorithm accepts " err
722 '
723
724 test_expect_success 'patch-mode via -i prompts for files' '
725         git reset --hard &&
726
727         echo one >file &&
728         echo two >test &&
729         git add -i <<-\EOF &&
730         patch
731         test
732
733         y
734         quit
735         EOF
736
737         echo test >expect &&
738         git diff --cached --name-only >actual &&
739         diff_cmp expect actual
740 '
741
742 test_expect_success 'add -p handles globs' '
743         git reset --hard &&
744
745         mkdir -p subdir &&
746         echo base >one.c &&
747         echo base >subdir/two.c &&
748         git add "*.c" &&
749         git commit -m base &&
750
751         echo change >one.c &&
752         echo change >subdir/two.c &&
753         git add -p "*.c" <<-\EOF &&
754         y
755         y
756         EOF
757
758         cat >expect <<-\EOF &&
759         one.c
760         subdir/two.c
761         EOF
762         git diff --cached --name-only >actual &&
763         test_cmp expect actual
764 '
765
766 test_expect_success 'add -p handles relative paths' '
767         git reset --hard &&
768
769         echo base >relpath.c &&
770         git add "*.c" &&
771         git commit -m relpath &&
772
773         echo change >relpath.c &&
774         mkdir -p subdir &&
775         git -C subdir add -p .. 2>error <<-\EOF &&
776         y
777         EOF
778
779         test_must_be_empty error &&
780
781         cat >expect <<-\EOF &&
782         relpath.c
783         EOF
784         git diff --cached --name-only >actual &&
785         test_cmp expect actual
786 '
787
788 test_expect_success 'add -p does not expand argument lists' '
789         git reset --hard &&
790
791         echo content >not-changed &&
792         git add not-changed &&
793         git commit -m "add not-changed file" &&
794
795         echo change >file &&
796         GIT_TRACE=$(pwd)/trace.out git add -p . <<-\EOF &&
797         y
798         EOF
799
800         # we know that "file" must be mentioned since we actually
801         # update it, but we want to be sure that our "." pathspec
802         # was not expanded into the argument list of any command.
803         # So look only for "not-changed".
804         ! grep -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out
805 '
806
807 test_expect_success 'hunk-editing handles custom comment char' '
808         git reset --hard &&
809         echo change >>file &&
810         test_config core.commentChar "\$" &&
811         echo e | GIT_EDITOR=true git add -p &&
812         git diff --exit-code
813 '
814
815 test_expect_success 'add -p works even with color.ui=always' '
816         git reset --hard &&
817         echo change >>file &&
818         test_config color.ui always &&
819         echo y | git add -p &&
820         echo file >expect &&
821         git diff --cached --name-only >actual &&
822         test_cmp expect actual
823 '
824
825 test_expect_success 'setup different kinds of dirty submodules' '
826         test_create_repo for-submodules &&
827         (
828                 cd for-submodules &&
829                 test_commit initial &&
830                 test_create_repo dirty-head &&
831                 (
832                         cd dirty-head &&
833                         test_commit initial
834                 ) &&
835                 cp -R dirty-head dirty-otherwise &&
836                 cp -R dirty-head dirty-both-ways &&
837                 git add dirty-head &&
838                 git add dirty-otherwise dirty-both-ways &&
839                 git commit -m initial &&
840
841                 cd dirty-head &&
842                 test_commit updated &&
843                 cd ../dirty-both-ways &&
844                 test_commit updated &&
845                 echo dirty >>initial &&
846                 : >untracked &&
847                 cd ../dirty-otherwise &&
848                 echo dirty >>initial &&
849                 : >untracked
850         ) &&
851         git -C for-submodules diff-files --name-only >actual &&
852         cat >expected <<-\EOF &&
853         dirty-both-ways
854         dirty-head
855         EOF
856         test_cmp expected actual &&
857         git -C for-submodules diff-files --name-only --ignore-submodules=none >actual &&
858         cat >expected <<-\EOF &&
859         dirty-both-ways
860         dirty-head
861         dirty-otherwise
862         EOF
863         test_cmp expected actual &&
864         git -C for-submodules diff-files --name-only --ignore-submodules=dirty >actual &&
865         cat >expected <<-\EOF &&
866         dirty-both-ways
867         dirty-head
868         EOF
869         test_cmp expected actual
870 '
871
872 test_expect_success 'status ignores dirty submodules (except HEAD)' '
873         git -C for-submodules add -i </dev/null >output &&
874         grep dirty-head output &&
875         grep dirty-both-ways output &&
876         ! grep dirty-otherwise output
877 '
878
879 test_expect_success 'set up pathological context' '
880         git reset --hard &&
881         test_write_lines a a a a a a a a a a a >a &&
882         git add a &&
883         git commit -m a &&
884         test_write_lines c b a a a a a a a b a a a a >a &&
885         test_write_lines     a a a a a a a b a a a a >expected-1 &&
886         test_write_lines   b a a a a a a a b a a a a >expected-2 &&
887         # check editing can cope with missing header and deleted context lines
888         # as well as changes to other lines
889         test_write_lines +b " a" >patch
890 '
891
892 test_expect_success 'add -p works with pathological context lines' '
893         git reset &&
894         printf "%s\n" n y |
895         git add -p &&
896         git cat-file blob :a >actual &&
897         test_cmp expected-1 actual
898 '
899
900 test_expect_success 'add -p patch editing works with pathological context lines' '
901         git reset &&
902         # n q q below is in case edit fails
903         printf "%s\n" e y    n q q |
904         git add -p &&
905         git cat-file blob :a >actual &&
906         test_cmp expected-2 actual
907 '
908
909 test_expect_success 'checkout -p works with pathological context lines' '
910         test_write_lines a a a a a a >a &&
911         git add a &&
912         test_write_lines a b a b a b a b a b a >a &&
913         test_write_lines s n n y q | git checkout -p &&
914         test_write_lines a b a b a a b a b a >expect &&
915         test_cmp expect a
916 '
917
918 # This should be called from a subshell as it sets a temporary editor
919 setup_new_file() {
920         write_script new-file-editor.sh <<-\EOF &&
921         sed /^#/d "$1" >patch &&
922         sed /^+c/d patch >"$1"
923         EOF
924         test_set_editor "$(pwd)/new-file-editor.sh" &&
925         test_write_lines a b c d e f >new-file &&
926         test_write_lines a b d e f >new-file-expect &&
927         test_write_lines "@@ -0,0 +1,6 @@" +a +b +c +d +e +f >patch-expect
928 }
929
930 test_expect_success 'add -N followed by add -p patch editing' '
931         git reset --hard &&
932         (
933                 setup_new_file &&
934                 git add -N new-file &&
935                 test_write_lines e n q | git add -p &&
936                 git cat-file blob :new-file >actual &&
937                 test_cmp new-file-expect actual &&
938                 test_cmp patch-expect patch
939         )
940 '
941
942 test_expect_success 'checkout -p patch editing of added file' '
943         git reset --hard &&
944         (
945                 setup_new_file &&
946                 git add new-file &&
947                 git commit -m "add new file" &&
948                 git rm new-file &&
949                 git commit -m "remove new file" &&
950                 test_write_lines e n q | git checkout -p HEAD^ &&
951                 test_cmp new-file-expect new-file &&
952                 test_cmp patch-expect patch
953         )
954 '
955
956 test_expect_success 'show help from add--helper' '
957         git reset --hard &&
958         cat >expect <<-EOF &&
959
960         <BOLD>*** Commands ***<RESET>
961           1: <BOLD;BLUE>s<RESET>tatus     2: <BOLD;BLUE>u<RESET>pdate     3: <BOLD;BLUE>r<RESET>evert     4: <BOLD;BLUE>a<RESET>dd untracked
962           5: <BOLD;BLUE>p<RESET>atch      6: <BOLD;BLUE>d<RESET>iff       7: <BOLD;BLUE>q<RESET>uit       8: <BOLD;BLUE>h<RESET>elp
963         <BOLD;BLUE>What now<RESET>> <BOLD;RED>status        - show paths with changes<RESET>
964         <BOLD;RED>update        - add working tree state to the staged set of changes<RESET>
965         <BOLD;RED>revert        - revert staged set of changes back to the HEAD version<RESET>
966         <BOLD;RED>patch         - pick hunks and update selectively<RESET>
967         <BOLD;RED>diff          - view diff between HEAD and index<RESET>
968         <BOLD;RED>add untracked - add contents of untracked files to the staged set of changes<RESET>
969         <BOLD>*** Commands ***<RESET>
970           1: <BOLD;BLUE>s<RESET>tatus     2: <BOLD;BLUE>u<RESET>pdate     3: <BOLD;BLUE>r<RESET>evert     4: <BOLD;BLUE>a<RESET>dd untracked
971           5: <BOLD;BLUE>p<RESET>atch      6: <BOLD;BLUE>d<RESET>iff       7: <BOLD;BLUE>q<RESET>uit       8: <BOLD;BLUE>h<RESET>elp
972         <BOLD;BLUE>What now<RESET>>$SP
973         Bye.
974         EOF
975         test_write_lines h | force_color git add -i >actual.colored &&
976         test_decode_color <actual.colored >actual &&
977         test_cmp expect actual
978 '
979
980 test_done