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