Merge branch 'kb/full-history-compute-treesame-carefully-2'
[git] / t / t0008-ignores.sh
1 #!/bin/sh
2
3 test_description=check-ignore
4
5 . ./test-lib.sh
6
7 init_vars () {
8         global_excludes="$(pwd)/global-excludes"
9 }
10
11 enable_global_excludes () {
12         init_vars &&
13         git config core.excludesfile "$global_excludes"
14 }
15
16 expect_in () {
17         dest="$HOME/expected-$1" text="$2"
18         if test -z "$text"
19         then
20                 >"$dest" # avoid newline
21         else
22                 echo "$text" >"$dest"
23         fi
24 }
25
26 expect () {
27         expect_in stdout "$1"
28 }
29
30 expect_from_stdin () {
31         cat >"$HOME/expected-stdout"
32 }
33
34 test_stderr () {
35         expected="$1"
36         expect_in stderr "$1" &&
37         test_cmp "$HOME/expected-stderr" "$HOME/stderr"
38 }
39
40 stderr_contains () {
41         regexp="$1"
42         if grep "$regexp" "$HOME/stderr"
43         then
44                 return 0
45         else
46                 echo "didn't find /$regexp/ in $HOME/stderr"
47                 cat "$HOME/stderr"
48                 return 1
49         fi
50 }
51
52 stderr_empty_on_success () {
53         expect_code="$1"
54         if test $expect_code = 0
55         then
56                 test_stderr ""
57         else
58                 # If we expect failure then stderr might or might not be empty
59                 # due to --quiet - the caller can check its contents
60                 return 0
61         fi
62 }
63
64 test_check_ignore () {
65         args="$1" expect_code="${2:-0}" global_args="$3"
66
67         init_vars &&
68         rm -f "$HOME/stdout" "$HOME/stderr" "$HOME/cmd" &&
69         echo git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $args \
70                 >"$HOME/cmd" &&
71         echo "$expect_code" >"$HOME/expected-exit-code" &&
72         test_expect_code "$expect_code" \
73                 git $global_args check-ignore $quiet_opt $verbose_opt $non_matching_opt $args \
74                 >"$HOME/stdout" 2>"$HOME/stderr" &&
75         test_cmp "$HOME/expected-stdout" "$HOME/stdout" &&
76         stderr_empty_on_success "$expect_code"
77 }
78
79 # Runs the same code with 4 different levels of output verbosity:
80 #
81 #   1. with -q / --quiet
82 #   2. with default verbosity
83 #   3. with -v / --verbose
84 #   4. with -v / --verbose, *and* -n / --non-matching
85 #
86 # expecting success each time.  Takes advantage of the fact that
87 # check-ignore --verbose output is the same as normal output except
88 # for the extra first column.
89 #
90 # Arguments:
91 #   - (optional) prereqs for this test, e.g. 'SYMLINKS'
92 #   - test name
93 #   - output to expect from the fourth verbosity mode (the output
94 #     from the other verbosity modes is automatically inferred
95 #     from this value)
96 #   - code to run (should invoke test_check_ignore)
97 test_expect_success_multi () {
98         prereq=
99         if test $# -eq 4
100         then
101                 prereq=$1
102                 shift
103         fi
104         testname="$1" expect_all="$2" code="$3"
105
106         expect_verbose=$( echo "$expect_all" | grep -v '^::     ' )
107         expect=$( echo "$expect_verbose" | sed -e 's/.* //' )
108
109         test_expect_success $prereq "$testname" '
110                 expect "$expect" &&
111                 eval "$code"
112         '
113
114         # --quiet is only valid when a single pattern is passed
115         if test $( echo "$expect_all" | wc -l ) = 1
116         then
117                 for quiet_opt in '-q' '--quiet'
118                 do
119                         test_expect_success $prereq "$testname${quiet_opt:+ with $quiet_opt}" "
120                         expect '' &&
121                         $code
122                 "
123                 done
124                 quiet_opt=
125         fi
126
127         for verbose_opt in '-v' '--verbose'
128         do
129                 for non_matching_opt in '' ' -n' ' --non-matching'
130                 do
131                         if test -n "$non_matching_opt"
132                         then
133                                 my_expect="$expect_all"
134                         else
135                                 my_expect="$expect_verbose"
136                         fi
137
138                         test_code="
139                                 expect '$my_expect' &&
140                                 $code
141                         "
142                         opts="$verbose_opt$non_matching_opt"
143                         test_expect_success $prereq "$testname${opts:+ with $opts}" "$test_code"
144                 done
145         done
146         verbose_opt=
147         non_matching_opt=
148 }
149
150 test_expect_success 'setup' '
151         init_vars &&
152         mkdir -p a/b/ignored-dir a/submodule b &&
153         if test_have_prereq SYMLINKS
154         then
155                 ln -s b a/symlink
156         fi &&
157         (
158                 cd a/submodule &&
159                 git init &&
160                 echo a >a &&
161                 git add a &&
162                 git commit -m"commit in submodule"
163         ) &&
164         git add a/submodule &&
165         cat <<-\EOF >.gitignore &&
166                 one
167                 ignored-*
168                 top-level-dir/
169         EOF
170         for dir in . a
171         do
172                 : >$dir/not-ignored &&
173                 : >$dir/ignored-and-untracked &&
174                 : >$dir/ignored-but-in-index
175         done &&
176         git add -f ignored-but-in-index a/ignored-but-in-index &&
177         cat <<-\EOF >a/.gitignore &&
178                 two*
179                 *three
180         EOF
181         cat <<-\EOF >a/b/.gitignore &&
182                 four
183                 five
184                 # this comment should affect the line numbers
185                 six
186                 ignored-dir/
187                 # and so should this blank line:
188
189                 !on*
190                 !two
191         EOF
192         echo "seven" >a/b/ignored-dir/.gitignore &&
193         test -n "$HOME" &&
194         cat <<-\EOF >"$global_excludes" &&
195                 globalone
196                 !globaltwo
197                 globalthree
198         EOF
199         cat <<-\EOF >>.git/info/exclude
200                 per-repo
201         EOF
202 '
203
204 ############################################################################
205 #
206 # test invalid inputs
207
208 test_expect_success_multi '. corner-case' '::   .' '
209         test_check_ignore . 1
210 '
211
212 test_expect_success_multi 'empty command line' '' '
213         test_check_ignore "" 128 &&
214         stderr_contains "fatal: no path specified"
215 '
216
217 test_expect_success_multi '--stdin with empty STDIN' '' '
218         test_check_ignore "--stdin" 1 </dev/null &&
219         test_stderr ""
220 '
221
222 test_expect_success '-q with multiple args' '
223         expect "" &&
224         test_check_ignore "-q one two" 128 &&
225         stderr_contains "fatal: --quiet is only valid with a single pathname"
226 '
227
228 test_expect_success '--quiet with multiple args' '
229         expect "" &&
230         test_check_ignore "--quiet one two" 128 &&
231         stderr_contains "fatal: --quiet is only valid with a single pathname"
232 '
233
234 for verbose_opt in '-v' '--verbose'
235 do
236         for quiet_opt in '-q' '--quiet'
237         do
238                 test_expect_success "$quiet_opt $verbose_opt" "
239                         expect '' &&
240                         test_check_ignore '$quiet_opt $verbose_opt foo' 128 &&
241                         stderr_contains 'fatal: cannot have both --quiet and --verbose'
242                 "
243         done
244 done
245
246 test_expect_success '--quiet with multiple args' '
247         expect "" &&
248         test_check_ignore "--quiet one two" 128 &&
249         stderr_contains "fatal: --quiet is only valid with a single pathname"
250 '
251
252 test_expect_success_multi 'erroneous use of --' '' '
253         test_check_ignore "--" 128 &&
254         stderr_contains "fatal: no path specified"
255 '
256
257 test_expect_success_multi '--stdin with superfluous arg' '' '
258         test_check_ignore "--stdin foo" 128 &&
259         stderr_contains "fatal: cannot specify pathnames with --stdin"
260 '
261
262 test_expect_success_multi '--stdin -z with superfluous arg' '' '
263         test_check_ignore "--stdin -z foo" 128 &&
264         stderr_contains "fatal: cannot specify pathnames with --stdin"
265 '
266
267 test_expect_success_multi '-z without --stdin' '' '
268         test_check_ignore "-z" 128 &&
269         stderr_contains "fatal: -z only makes sense with --stdin"
270 '
271
272 test_expect_success_multi '-z without --stdin and superfluous arg' '' '
273         test_check_ignore "-z foo" 128 &&
274         stderr_contains "fatal: -z only makes sense with --stdin"
275 '
276
277 test_expect_success_multi 'needs work tree' '' '
278         (
279                 cd .git &&
280                 test_check_ignore "foo" 128
281         ) &&
282         stderr_contains "fatal: This operation must be run in a work tree"
283 '
284
285 ############################################################################
286 #
287 # test standard ignores
288
289 # First make sure that the presence of a file in the working tree
290 # does not impact results, but that the presence of a file in the
291 # index does.
292
293 for subdir in '' 'a/'
294 do
295         if test -z "$subdir"
296         then
297                 where="at top-level"
298         else
299                 where="in subdir $subdir"
300         fi
301
302         test_expect_success_multi "non-existent file $where not ignored" \
303                 "::     ${subdir}non-existent" \
304                 "test_check_ignore '${subdir}non-existent' 1"
305
306         test_expect_success_multi "non-existent file $where ignored" \
307                 ".gitignore:1:one       ${subdir}one" \
308                 "test_check_ignore '${subdir}one'"
309
310         test_expect_success_multi "existing untracked file $where not ignored" \
311                 "::     ${subdir}not-ignored" \
312                 "test_check_ignore '${subdir}not-ignored' 1"
313
314         test_expect_success_multi "existing tracked file $where not ignored" \
315                 "::     ${subdir}ignored-but-in-index" \
316                 "test_check_ignore '${subdir}ignored-but-in-index' 1"
317
318         test_expect_success_multi "existing untracked file $where ignored" \
319                 ".gitignore:2:ignored-* ${subdir}ignored-and-untracked" \
320                 "test_check_ignore '${subdir}ignored-and-untracked'"
321
322         test_expect_success_multi "mix of file types $where" \
323 "::     ${subdir}non-existent
324 .gitignore:1:one        ${subdir}one
325 ::      ${subdir}not-ignored
326 ::      ${subdir}ignored-but-in-index
327 .gitignore:2:ignored-*  ${subdir}ignored-and-untracked" \
328                 "test_check_ignore '
329                         ${subdir}non-existent
330                         ${subdir}one
331                         ${subdir}not-ignored
332                         ${subdir}ignored-but-in-index
333                         ${subdir}ignored-and-untracked'
334                 "
335 done
336
337 # Having established the above, from now on we mostly test against
338 # files which do not exist in the working tree or index.
339
340 test_expect_success 'sub-directory local ignore' '
341         expect "a/3-three" &&
342         test_check_ignore "a/3-three a/three-not-this-one"
343 '
344
345 test_expect_success 'sub-directory local ignore with --verbose'  '
346         expect "a/.gitignore:2:*three   a/3-three" &&
347         test_check_ignore "--verbose a/3-three a/three-not-this-one"
348 '
349
350 test_expect_success 'local ignore inside a sub-directory' '
351         expect "3-three" &&
352         (
353                 cd a &&
354                 test_check_ignore "3-three three-not-this-one"
355         )
356 '
357 test_expect_success 'local ignore inside a sub-directory with --verbose' '
358         expect "a/.gitignore:2:*three   3-three" &&
359         (
360                 cd a &&
361                 test_check_ignore "--verbose 3-three three-not-this-one"
362         )
363 '
364
365 test_expect_success_multi 'nested include' \
366         'a/b/.gitignore:8:!on*  a/b/one' '
367         test_check_ignore "a/b/one"
368 '
369
370 ############################################################################
371 #
372 # test ignored sub-directories
373
374 test_expect_success_multi 'ignored sub-directory' \
375         'a/b/.gitignore:5:ignored-dir/  a/b/ignored-dir' '
376         test_check_ignore "a/b/ignored-dir"
377 '
378
379 test_expect_success 'multiple files inside ignored sub-directory' '
380         expect_from_stdin <<-\EOF &&
381                 a/b/ignored-dir/foo
382                 a/b/ignored-dir/twoooo
383                 a/b/ignored-dir/seven
384         EOF
385         test_check_ignore "a/b/ignored-dir/foo a/b/ignored-dir/twoooo a/b/ignored-dir/seven"
386 '
387
388 test_expect_success 'multiple files inside ignored sub-directory with -v' '
389         expect_from_stdin <<-\EOF &&
390                 a/b/.gitignore:5:ignored-dir/   a/b/ignored-dir/foo
391                 a/b/.gitignore:5:ignored-dir/   a/b/ignored-dir/twoooo
392                 a/b/.gitignore:5:ignored-dir/   a/b/ignored-dir/seven
393         EOF
394         test_check_ignore "-v a/b/ignored-dir/foo a/b/ignored-dir/twoooo a/b/ignored-dir/seven"
395 '
396
397 test_expect_success 'cd to ignored sub-directory' '
398         expect_from_stdin <<-\EOF &&
399                 foo
400                 twoooo
401                 ../one
402                 seven
403                 ../../one
404         EOF
405         (
406                 cd a/b/ignored-dir &&
407                 test_check_ignore "foo twoooo ../one seven ../../one"
408         )
409 '
410
411 test_expect_success 'cd to ignored sub-directory with -v' '
412         expect_from_stdin <<-\EOF &&
413                 a/b/.gitignore:5:ignored-dir/   foo
414                 a/b/.gitignore:5:ignored-dir/   twoooo
415                 a/b/.gitignore:8:!on*   ../one
416                 a/b/.gitignore:5:ignored-dir/   seven
417                 .gitignore:1:one        ../../one
418         EOF
419         (
420                 cd a/b/ignored-dir &&
421                 test_check_ignore "-v foo twoooo ../one seven ../../one"
422         )
423 '
424
425 ############################################################################
426 #
427 # test handling of symlinks
428
429 test_expect_success_multi SYMLINKS 'symlink' '::        a/symlink' '
430         test_check_ignore "a/symlink" 1
431 '
432
433 test_expect_success_multi SYMLINKS 'beyond a symlink' '' '
434         test_check_ignore "a/symlink/foo" 128 &&
435         test_stderr "fatal: '\''a/symlink/foo'\'' is beyond a symbolic link"
436 '
437
438 test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' '
439         (
440                 cd a &&
441                 test_check_ignore "symlink/foo" 128
442         ) &&
443         test_stderr "fatal: '\''symlink/foo'\'' is beyond a symbolic link"
444 '
445
446 ############################################################################
447 #
448 # test handling of submodules
449
450 test_expect_success_multi 'submodule' '' '
451         test_check_ignore "a/submodule/one" 128 &&
452         test_stderr "fatal: Path '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''"
453 '
454
455 test_expect_success_multi 'submodule from subdirectory' '' '
456         (
457                 cd a &&
458                 test_check_ignore "submodule/one" 128
459         ) &&
460         test_stderr "fatal: Path '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''"
461 '
462
463 ############################################################################
464 #
465 # test handling of global ignore files
466
467 test_expect_success 'global ignore not yet enabled' '
468         expect_from_stdin <<-\EOF &&
469                 .git/info/exclude:7:per-repo    per-repo
470                 a/.gitignore:2:*three   a/globalthree
471                 .git/info/exclude:7:per-repo    a/per-repo
472         EOF
473         test_check_ignore "-v globalone per-repo a/globalthree a/per-repo not-ignored a/globaltwo"
474 '
475
476 test_expect_success 'global ignore' '
477         enable_global_excludes &&
478         expect_from_stdin <<-\EOF &&
479                 globalone
480                 per-repo
481                 globalthree
482                 a/globalthree
483                 a/per-repo
484                 globaltwo
485         EOF
486         test_check_ignore "globalone per-repo globalthree a/globalthree a/per-repo not-ignored globaltwo"
487 '
488
489 test_expect_success 'global ignore with -v' '
490         enable_global_excludes &&
491         expect_from_stdin <<-EOF &&
492                 $global_excludes:1:globalone    globalone
493                 .git/info/exclude:7:per-repo    per-repo
494                 $global_excludes:3:globalthree  globalthree
495                 a/.gitignore:2:*three   a/globalthree
496                 .git/info/exclude:7:per-repo    a/per-repo
497                 $global_excludes:2:!globaltwo   globaltwo
498         EOF
499         test_check_ignore "-v globalone per-repo globalthree a/globalthree a/per-repo not-ignored globaltwo"
500 '
501
502 ############################################################################
503 #
504 # test --stdin
505
506 cat <<-\EOF >stdin
507         one
508         not-ignored
509         a/one
510         a/not-ignored
511         a/b/on
512         a/b/one
513         a/b/one one
514         "a/b/one two"
515         "a/b/one\"three"
516         a/b/not-ignored
517         a/b/two
518         a/b/twooo
519         globaltwo
520         a/globaltwo
521         a/b/globaltwo
522         b/globaltwo
523 EOF
524 cat <<-\EOF >expected-default
525         one
526         a/one
527         a/b/on
528         a/b/one
529         a/b/one one
530         a/b/one two
531         "a/b/one\"three"
532         a/b/two
533         a/b/twooo
534         globaltwo
535         a/globaltwo
536         a/b/globaltwo
537         b/globaltwo
538 EOF
539 cat <<-EOF >expected-verbose
540         .gitignore:1:one        one
541         .gitignore:1:one        a/one
542         a/b/.gitignore:8:!on*   a/b/on
543         a/b/.gitignore:8:!on*   a/b/one
544         a/b/.gitignore:8:!on*   a/b/one one
545         a/b/.gitignore:8:!on*   a/b/one two
546         a/b/.gitignore:8:!on*   "a/b/one\"three"
547         a/b/.gitignore:9:!two   a/b/two
548         a/.gitignore:1:two*     a/b/twooo
549         $global_excludes:2:!globaltwo   globaltwo
550         $global_excludes:2:!globaltwo   a/globaltwo
551         $global_excludes:2:!globaltwo   a/b/globaltwo
552         $global_excludes:2:!globaltwo   b/globaltwo
553 EOF
554
555 sed -e 's/^"//' -e 's/\\//' -e 's/"$//' stdin | \
556         tr "\n" "\0" >stdin0
557 sed -e 's/^"//' -e 's/\\//' -e 's/"$//' expected-default | \
558         tr "\n" "\0" >expected-default0
559 sed -e 's/      "/      /' -e 's/\\//' -e 's/"$//' expected-verbose | \
560         tr ":\t\n" "\0" >expected-verbose0
561
562 test_expect_success '--stdin' '
563         expect_from_stdin <expected-default &&
564         test_check_ignore "--stdin" <stdin
565 '
566
567 test_expect_success '--stdin -q' '
568         expect "" &&
569         test_check_ignore "-q --stdin" <stdin
570 '
571
572 test_expect_success '--stdin -v' '
573         expect_from_stdin <expected-verbose &&
574         test_check_ignore "-v --stdin" <stdin
575 '
576
577 for opts in '--stdin -z' '-z --stdin'
578 do
579         test_expect_success "$opts" "
580                 expect_from_stdin <expected-default0 &&
581                 test_check_ignore '$opts' <stdin0
582         "
583
584         test_expect_success "$opts -q" "
585                 expect "" &&
586                 test_check_ignore '-q $opts' <stdin0
587         "
588
589         test_expect_success "$opts -v" "
590                 expect_from_stdin <expected-verbose0 &&
591                 test_check_ignore '-v $opts' <stdin0
592         "
593 done
594
595 cat <<-\EOF >stdin
596         ../one
597         ../not-ignored
598         one
599         not-ignored
600         b/on
601         b/one
602         b/one one
603         "b/one two"
604         "b/one\"three"
605         b/two
606         b/not-ignored
607         b/twooo
608         ../globaltwo
609         globaltwo
610         b/globaltwo
611         ../b/globaltwo
612         c/not-ignored
613 EOF
614 # N.B. we deliberately end STDIN with a non-matching pattern in order
615 # to test that the exit code indicates that one or more of the
616 # provided paths is ignored - in other words, that it represents an
617 # aggregation of all the results, not just the final result.
618
619 cat <<-EOF >expected-all
620         .gitignore:1:one        ../one
621         ::      ../not-ignored
622         .gitignore:1:one        one
623         ::      not-ignored
624         a/b/.gitignore:8:!on*   b/on
625         a/b/.gitignore:8:!on*   b/one
626         a/b/.gitignore:8:!on*   b/one one
627         a/b/.gitignore:8:!on*   b/one two
628         a/b/.gitignore:8:!on*   "b/one\"three"
629         a/b/.gitignore:9:!two   b/two
630         ::      b/not-ignored
631         a/.gitignore:1:two*     b/twooo
632         $global_excludes:2:!globaltwo   ../globaltwo
633         $global_excludes:2:!globaltwo   globaltwo
634         $global_excludes:2:!globaltwo   b/globaltwo
635         $global_excludes:2:!globaltwo   ../b/globaltwo
636         ::      c/not-ignored
637 EOF
638 grep -v '^::    ' expected-all >expected-verbose
639 sed -e 's/.*    //' expected-verbose >expected-default
640
641 sed -e 's/^"//' -e 's/\\//' -e 's/"$//' stdin | \
642         tr "\n" "\0" >stdin0
643 sed -e 's/^"//' -e 's/\\//' -e 's/"$//' expected-default | \
644         tr "\n" "\0" >expected-default0
645 sed -e 's/      "/      /' -e 's/\\//' -e 's/"$//' expected-verbose | \
646         tr ":\t\n" "\0" >expected-verbose0
647
648 test_expect_success '--stdin from subdirectory' '
649         expect_from_stdin <expected-default &&
650         (
651                 cd a &&
652                 test_check_ignore "--stdin" <../stdin
653         )
654 '
655
656 test_expect_success '--stdin from subdirectory with -v' '
657         expect_from_stdin <expected-verbose &&
658         (
659                 cd a &&
660                 test_check_ignore "--stdin -v" <../stdin
661         )
662 '
663
664 test_expect_success '--stdin from subdirectory with -v -n' '
665         expect_from_stdin <expected-all &&
666         (
667                 cd a &&
668                 test_check_ignore "--stdin -v -n" <../stdin
669         )
670 '
671
672 for opts in '--stdin -z' '-z --stdin'
673 do
674         test_expect_success "$opts from subdirectory" '
675                 expect_from_stdin <expected-default0 &&
676                 (
677                         cd a &&
678                         test_check_ignore "'"$opts"'" <../stdin0
679                 )
680         '
681
682         test_expect_success "$opts from subdirectory with -v" '
683                 expect_from_stdin <expected-verbose0 &&
684                 (
685                         cd a &&
686                         test_check_ignore "'"$opts"' -v" <../stdin0
687                 )
688         '
689 done
690
691 test_expect_success PIPE 'streaming support for --stdin' '
692         mkfifo in out &&
693         (git check-ignore -n -v --stdin <in >out &) &&
694
695         # We cannot just "echo >in" because check-ignore would get EOF
696         # after echo exited; instead we open the descriptor in our
697         # shell, and then echo to the fd. We make sure to close it at
698         # the end, so that the subprocess does get EOF and dies
699         # properly.
700         exec 9>in &&
701         test_when_finished "exec 9>&-" &&
702         echo >&9 one &&
703         read response <out &&
704         echo "$response" | grep "^\.gitignore:1:one     one" &&
705         echo >&9 two &&
706         read response <out &&
707         echo "$response" | grep "^::    two"
708 '
709
710 test_done