test-lib: check Bash version for '-x' without using shell arrays
[git] / t / t3701-add-interactive.sh
1 #!/bin/sh
2
3 test_description='add -i basic tests'
4 . ./test-lib.sh
5 . "$TEST_DIRECTORY"/lib-terminal.sh
6
7 if ! test_have_prereq PERL
8 then
9         skip_all='skipping add -i tests, perl not available'
10         test_done
11 fi
12
13 diff_cmp () {
14         for x
15         do
16                 sed  -e '/^index/s/[0-9a-f]*[1-9a-f][0-9a-f]*\.\./1234567../' \
17                      -e '/^index/s/\.\.[0-9a-f]*[1-9a-f][0-9a-f]*/..9abcdef/' \
18                      -e '/^index/s/ 00*\.\./ 0000000../' \
19                      -e '/^index/s/\.\.00*$/..0000000/' \
20                      -e '/^index/s/\.\.00* /..0000000 /' \
21                      "$x" >"$x.filtered"
22         done
23         test_cmp "$1.filtered" "$2.filtered"
24 }
25
26 test_expect_success 'setup (initial)' '
27         echo content >file &&
28         git add file &&
29         echo more >>file &&
30         echo lines >>file
31 '
32 test_expect_success 'status works (initial)' '
33         git add -i </dev/null >output &&
34         grep "+1/-0 *+2/-0 file" output
35 '
36
37 test_expect_success 'setup expected' '
38         cat >expected <<-\EOF
39         new file mode 100644
40         index 0000000..d95f3ad
41         --- /dev/null
42         +++ b/file
43         @@ -0,0 +1 @@
44         +content
45         EOF
46 '
47
48 test_expect_success 'diff works (initial)' '
49         (echo d; echo 1) | git add -i >output &&
50         sed -ne "/new file/,/content/p" <output >diff &&
51         diff_cmp expected diff
52 '
53 test_expect_success 'revert works (initial)' '
54         git add file &&
55         (echo r; echo 1) | git add -i &&
56         git ls-files >output &&
57         ! grep . output
58 '
59
60 test_expect_success 'setup (commit)' '
61         echo baseline >file &&
62         git add file &&
63         git commit -m commit &&
64         echo content >>file &&
65         git add file &&
66         echo more >>file &&
67         echo lines >>file
68 '
69 test_expect_success 'status works (commit)' '
70         git add -i </dev/null >output &&
71         grep "+1/-0 *+2/-0 file" output
72 '
73
74 test_expect_success 'setup expected' '
75         cat >expected <<-\EOF
76         index 180b47c..b6f2c08 100644
77         --- a/file
78         +++ b/file
79         @@ -1 +1,2 @@
80          baseline
81         +content
82         EOF
83 '
84
85 test_expect_success 'diff works (commit)' '
86         (echo d; echo 1) | git add -i >output &&
87         sed -ne "/^index/,/content/p" <output >diff &&
88         diff_cmp expected diff
89 '
90 test_expect_success 'revert works (commit)' '
91         git add file &&
92         (echo r; echo 1) | git add -i &&
93         git add -i </dev/null >output &&
94         grep "unchanged *+3/-0 file" output
95 '
96
97
98 test_expect_success 'setup expected' '
99         cat >expected <<-\EOF
100         EOF
101 '
102
103 test_expect_success 'dummy edit works' '
104         test_set_editor : &&
105         (echo e; echo a) | git add -p &&
106         git diff > diff &&
107         diff_cmp expected diff
108 '
109
110 test_expect_success 'setup patch' '
111         cat >patch <<-\EOF
112         @@ -1,1 +1,4 @@
113          this
114         +patch
115         -does not
116          apply
117         EOF
118 '
119
120 test_expect_success 'setup fake editor' '
121         write_script "fake_editor.sh" <<-\EOF &&
122         mv -f "$1" oldpatch &&
123         mv -f patch "$1"
124         EOF
125         test_set_editor "$(pwd)/fake_editor.sh"
126 '
127
128 test_expect_success 'bad edit rejected' '
129         git reset &&
130         (echo e; echo n; echo d) | git add -p >output &&
131         grep "hunk does not apply" output
132 '
133
134 test_expect_success 'setup patch' '
135         cat >patch <<-\EOF
136         this patch
137         is garbage
138         EOF
139 '
140
141 test_expect_success 'garbage edit rejected' '
142         git reset &&
143         (echo e; echo n; echo d) | git add -p >output &&
144         grep "hunk does not apply" output
145 '
146
147 test_expect_success 'setup patch' '
148         cat >patch <<-\EOF
149         @@ -1,0 +1,0 @@
150          baseline
151         +content
152         +newcontent
153         +lines
154         EOF
155 '
156
157 test_expect_success 'setup expected' '
158         cat >expected <<-\EOF
159         diff --git a/file b/file
160         index b5dd6c9..f910ae9 100644
161         --- a/file
162         +++ b/file
163         @@ -1,4 +1,4 @@
164          baseline
165          content
166         -newcontent
167         +more
168          lines
169         EOF
170 '
171
172 test_expect_success 'real edit works' '
173         (echo e; echo n; echo d) | git add -p &&
174         git diff >output &&
175         diff_cmp expected output
176 '
177
178 test_expect_success 'skip files similarly as commit -a' '
179         git reset &&
180         echo file >.gitignore &&
181         echo changed >file &&
182         echo y | git add -p file &&
183         git diff >output &&
184         git reset &&
185         git commit -am commit &&
186         git diff >expected &&
187         diff_cmp expected output &&
188         git reset --hard HEAD^
189 '
190 rm -f .gitignore
191
192 test_expect_success FILEMODE 'patch does not affect mode' '
193         git reset --hard &&
194         echo content >>file &&
195         chmod +x file &&
196         printf "n\\ny\\n" | git add -p &&
197         git show :file | grep content &&
198         git diff file | grep "new mode"
199 '
200
201 test_expect_success FILEMODE 'stage mode but not hunk' '
202         git reset --hard &&
203         echo content >>file &&
204         chmod +x file &&
205         printf "y\\nn\\n" | git add -p &&
206         git diff --cached file | grep "new mode" &&
207         git diff          file | grep "+content"
208 '
209
210
211 test_expect_success FILEMODE 'stage mode and hunk' '
212         git reset --hard &&
213         echo content >>file &&
214         chmod +x file &&
215         printf "y\\ny\\n" | git add -p &&
216         git diff --cached file | grep "new mode" &&
217         git diff --cached file | grep "+content" &&
218         test -z "$(git diff file)"
219 '
220
221 # end of tests disabled when filemode is not usable
222
223 test_expect_success 'setup again' '
224         git reset --hard &&
225         test_chmod +x file &&
226         echo content >>file
227 '
228
229 # Write the patch file with a new line at the top and bottom
230 test_expect_success 'setup patch' '
231         cat >patch <<-\EOF
232         index 180b47c..b6f2c08 100644
233         --- a/file
234         +++ b/file
235         @@ -1,2 +1,4 @@
236         +firstline
237          baseline
238          content
239         +lastline
240         \ No newline at end of file
241         EOF
242 '
243
244 # Expected output, diff is similar to the patch but w/ diff at the top
245 test_expect_success 'setup expected' '
246         echo diff --git a/file b/file >expected &&
247         cat patch |sed "/^index/s/ 100644/ 100755/" >>expected &&
248         cat >expected-output <<-\EOF
249         --- a/file
250         +++ b/file
251         @@ -1,2 +1,4 @@
252         +firstline
253          baseline
254          content
255         +lastline
256         \ No newline at end of file
257         @@ -1,2 +1,3 @@
258         +firstline
259          baseline
260          content
261         @@ -1,2 +2,3 @@
262          baseline
263          content
264         +lastline
265         \ No newline at end of file
266         EOF
267 '
268
269 # Test splitting the first patch, then adding both
270 test_expect_success C_LOCALE_OUTPUT 'add first line works' '
271         git commit -am "clear local changes" &&
272         git apply patch &&
273         printf "%s\n" s y y | git add -p file 2>error |
274                 sed -n -e "s/^Stage this hunk[^@]*\(@@ .*\)/\1/" \
275                        -e "/^[-+@ \\\\]"/p  >output &&
276         test_must_be_empty error &&
277         git diff --cached >diff &&
278         diff_cmp expected diff &&
279         test_cmp expected-output output
280 '
281
282 test_expect_success 'setup expected' '
283         cat >expected <<-\EOF
284         diff --git a/non-empty b/non-empty
285         deleted file mode 100644
286         index d95f3ad..0000000
287         --- a/non-empty
288         +++ /dev/null
289         @@ -1 +0,0 @@
290         -content
291         EOF
292 '
293
294 test_expect_success 'deleting a non-empty file' '
295         git reset --hard &&
296         echo content >non-empty &&
297         git add non-empty &&
298         git commit -m non-empty &&
299         rm non-empty &&
300         echo y | git add -p non-empty &&
301         git diff --cached >diff &&
302         diff_cmp expected diff
303 '
304
305 test_expect_success 'setup expected' '
306         cat >expected <<-\EOF
307         diff --git a/empty b/empty
308         deleted file mode 100644
309         index e69de29..0000000
310         EOF
311 '
312
313 test_expect_success 'deleting an empty file' '
314         git reset --hard &&
315         > empty &&
316         git add empty &&
317         git commit -m empty &&
318         rm empty &&
319         echo y | git add -p empty &&
320         git diff --cached >diff &&
321         diff_cmp expected diff
322 '
323
324 test_expect_success 'split hunk setup' '
325         git reset --hard &&
326         test_write_lines 10 20 30 40 50 60 >test &&
327         git add test &&
328         test_tick &&
329         git commit -m test &&
330
331         test_write_lines 10 15 20 21 22 23 24 30 40 50 60 >test
332 '
333
334 test_expect_success 'split hunk "add -p (edit)"' '
335         # Split, say Edit and do nothing.  Then:
336         #
337         # 1. Broken version results in a patch that does not apply and
338         # only takes [y/n] (edit again) so the first q is discarded
339         # and then n attempts to discard the edit. Repeat q enough
340         # times to get out.
341         #
342         # 2. Correct version applies the (not)edited version, and asks
343         #    about the next hunk, against which we say q and program
344         #    exits.
345         printf "%s\n" s e     q n q q |
346         EDITOR=: git add -p &&
347         git diff >actual &&
348         ! grep "^+15" actual
349 '
350
351 test_expect_failure 'split hunk "add -p (no, yes, edit)"' '
352         test_write_lines 5 10 20 21 30 31 40 50 60 >test &&
353         git reset &&
354         # test sequence is s(plit), n(o), y(es), e(dit)
355         # q n q q is there to make sure we exit at the end.
356         printf "%s\n" s n y e   q n q q |
357         EDITOR=: git add -p 2>error &&
358         test_must_be_empty error &&
359         git diff >actual &&
360         ! grep "^+31" actual
361 '
362
363 test_expect_success 'patch mode ignores unmerged entries' '
364         git reset --hard &&
365         test_commit conflict &&
366         test_commit non-conflict &&
367         git checkout -b side &&
368         test_commit side conflict.t &&
369         git checkout master &&
370         test_commit master conflict.t &&
371         test_must_fail git merge side &&
372         echo changed >non-conflict.t &&
373         echo y | git add -p >output &&
374         ! grep a/conflict.t output &&
375         cat >expected <<-\EOF &&
376         * Unmerged path conflict.t
377         diff --git a/non-conflict.t b/non-conflict.t
378         index f766221..5ea2ed4 100644
379         --- a/non-conflict.t
380         +++ b/non-conflict.t
381         @@ -1 +1 @@
382         -non-conflict
383         +changed
384         EOF
385         git diff --cached >diff &&
386         diff_cmp expected diff
387 '
388
389 test_expect_success TTY 'diffs can be colorized' '
390         git reset --hard &&
391
392         echo content >test &&
393         printf y | test_terminal git add -p >output 2>&1 &&
394
395         # We do not want to depend on the exact coloring scheme
396         # git uses for diffs, so just check that we saw some kind of color.
397         grep "$(printf "\\033")" output
398 '
399
400 test_expect_success TTY 'diffFilter filters diff' '
401         git reset --hard &&
402
403         echo content >test &&
404         test_config interactive.diffFilter "sed s/^/foo:/" &&
405         printf y | test_terminal git add -p >output 2>&1 &&
406
407         # avoid depending on the exact coloring or content of the prompts,
408         # and just make sure we saw our diff prefixed
409         grep foo:.*content output
410 '
411
412 test_expect_success TTY 'detect bogus diffFilter output' '
413         git reset --hard &&
414
415         echo content >test &&
416         test_config interactive.diffFilter "echo too-short" &&
417         printf y | test_must_fail test_terminal git add -p
418 '
419
420 test_expect_success 'patch-mode via -i prompts for files' '
421         git reset --hard &&
422
423         echo one >file &&
424         echo two >test &&
425         git add -i <<-\EOF &&
426         patch
427         test
428
429         y
430         quit
431         EOF
432
433         echo test >expect &&
434         git diff --cached --name-only >actual &&
435         diff_cmp expect actual
436 '
437
438 test_expect_success 'add -p handles globs' '
439         git reset --hard &&
440
441         mkdir -p subdir &&
442         echo base >one.c &&
443         echo base >subdir/two.c &&
444         git add "*.c" &&
445         git commit -m base &&
446
447         echo change >one.c &&
448         echo change >subdir/two.c &&
449         git add -p "*.c" <<-\EOF &&
450         y
451         y
452         EOF
453
454         cat >expect <<-\EOF &&
455         one.c
456         subdir/two.c
457         EOF
458         git diff --cached --name-only >actual &&
459         test_cmp expect actual
460 '
461
462 test_expect_success 'add -p handles relative paths' '
463         git reset --hard &&
464
465         echo base >relpath.c &&
466         git add "*.c" &&
467         git commit -m relpath &&
468
469         echo change >relpath.c &&
470         mkdir -p subdir &&
471         git -C subdir add -p .. 2>error <<-\EOF &&
472         y
473         EOF
474
475         test_must_be_empty error &&
476
477         cat >expect <<-\EOF &&
478         relpath.c
479         EOF
480         git diff --cached --name-only >actual &&
481         test_cmp expect actual
482 '
483
484 test_expect_success 'add -p does not expand argument lists' '
485         git reset --hard &&
486
487         echo content >not-changed &&
488         git add not-changed &&
489         git commit -m "add not-changed file" &&
490
491         echo change >file &&
492         GIT_TRACE=$(pwd)/trace.out git add -p . <<-\EOF &&
493         y
494         EOF
495
496         # we know that "file" must be mentioned since we actually
497         # update it, but we want to be sure that our "." pathspec
498         # was not expanded into the argument list of any command.
499         # So look only for "not-changed".
500         ! grep not-changed trace.out
501 '
502
503 test_expect_success 'hunk-editing handles custom comment char' '
504         git reset --hard &&
505         echo change >>file &&
506         test_config core.commentChar "\$" &&
507         echo e | GIT_EDITOR=true git add -p &&
508         git diff --exit-code
509 '
510
511 test_expect_success 'add -p works even with color.ui=always' '
512         git reset --hard &&
513         echo change >>file &&
514         test_config color.ui always &&
515         echo y | git add -p &&
516         echo file >expect &&
517         git diff --cached --name-only >actual &&
518         test_cmp expect actual
519 '
520
521 test_expect_success 'setup different kinds of dirty submodules' '
522         test_create_repo for-submodules &&
523         (
524                 cd for-submodules &&
525                 test_commit initial &&
526                 test_create_repo dirty-head &&
527                 (
528                         cd dirty-head &&
529                         test_commit initial
530                 ) &&
531                 cp -R dirty-head dirty-otherwise &&
532                 cp -R dirty-head dirty-both-ways &&
533                 git add dirty-head &&
534                 git add dirty-otherwise dirty-both-ways &&
535                 git commit -m initial &&
536
537                 cd dirty-head &&
538                 test_commit updated &&
539                 cd ../dirty-both-ways &&
540                 test_commit updated &&
541                 echo dirty >>initial &&
542                 : >untracked &&
543                 cd ../dirty-otherwise &&
544                 echo dirty >>initial &&
545                 : >untracked
546         ) &&
547         git -C for-submodules diff-files --name-only >actual &&
548         cat >expected <<-\EOF &&
549         dirty-both-ways
550         dirty-head
551         dirty-otherwise
552         EOF
553         test_cmp expected actual &&
554         git -C for-submodules diff-files --name-only --ignore-submodules=dirty >actual &&
555         cat >expected <<-\EOF &&
556         dirty-both-ways
557         dirty-head
558         EOF
559         test_cmp expected actual
560 '
561
562 test_expect_success 'status ignores dirty submodules (except HEAD)' '
563         git -C for-submodules add -i </dev/null >output &&
564         grep dirty-head output &&
565         grep dirty-both-ways output &&
566         ! grep dirty-otherwise output
567 '
568
569 test_expect_success 'set up pathological context' '
570         git reset --hard &&
571         test_write_lines a a a a a a a a a a a >a &&
572         git add a &&
573         git commit -m a &&
574         test_write_lines c b a a a a a a a b a a a a >a &&
575         test_write_lines     a a a a a a a b a a a a >expected-1 &&
576         test_write_lines   b a a a a a a a b a a a a >expected-2 &&
577         # check editing can cope with missing header and deleted context lines
578         # as well as changes to other lines
579         test_write_lines +b " a" >patch
580 '
581
582 test_expect_success 'add -p works with pathological context lines' '
583         git reset &&
584         printf "%s\n" n y |
585         git add -p &&
586         git cat-file blob :a >actual &&
587         test_cmp expected-1 actual
588 '
589
590 test_expect_success 'add -p patch editing works with pathological context lines' '
591         git reset &&
592         # n q q below is in case edit fails
593         printf "%s\n" e y    n q q |
594         git add -p &&
595         git cat-file blob :a >actual &&
596         test_cmp expected-2 actual
597 '
598
599 test_done