1#!/bin/sh
2
3test_description=check-ignore
4
5. ./test-lib.sh
6
7init_vars () {
8 global_excludes="$(pwd)/global-excludes"
9}
10
11enable_global_excludes () {
12 init_vars &&
13 git config core.excludesfile "$global_excludes"
14}
15
16expect_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
26expect () {
27 expect_in stdout "$1"
28}
29
30expect_from_stdin () {
31 cat >"$HOME/expected-stdout"
32}
33
34test_stderr () {
35 expected="$1"
36 expect_in stderr "$1" &&
37 test_cmp "$HOME/expected-stderr" "$HOME/stderr"
38}
39
40stderr_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
52stderr_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
64test_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)
97test_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
150test_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
208test_expect_success_multi '. corner-case' ':: .' '
209 test_check_ignore . 1
210'
211
212test_expect_success_multi 'empty command line' '' '
213 test_check_ignore "" 128 &&
214 stderr_contains "fatal: no path specified"
215'
216
217test_expect_success_multi '--stdin with empty STDIN' '' '
218 test_check_ignore "--stdin" 1 </dev/null &&
219 test_stderr ""
220'
221
222test_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
228test_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
234for verbose_opt in '-v' '--verbose'
235do
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
244done
245
246test_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
252test_expect_success_multi 'erroneous use of --' '' '
253 test_check_ignore "--" 128 &&
254 stderr_contains "fatal: no path specified"
255'
256
257test_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
262test_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
267test_expect_success_multi '-z without --stdin' '' '
268 test_check_ignore "-z" 128 &&
269 stderr_contains "fatal: -z only makes sense with --stdin"
270'
271
272test_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
277test_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
293for subdir in '' 'a/'
294do
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 "
335done
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
340test_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
345test_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
350test_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'
357test_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
365test_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
374test_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
379test_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
388test_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
397test_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
411test_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
429test_expect_success_multi SYMLINKS 'symlink' ':: a/symlink' '
430 test_check_ignore "a/symlink" 1
431'
432
433test_expect_success_multi SYMLINKS 'beyond a symlink' '' '
434 test_check_ignore "a/symlink/foo" 128 &&
435 test_stderr "fatal: pathspec '\''a/symlink/foo'\'' is beyond a symbolic link"
436'
437
438test_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: pathspec '\''symlink/foo'\'' is beyond a symbolic link"
444'
445
446############################################################################
447#
448# test handling of submodules
449
450test_expect_success_multi 'submodule' '' '
451 test_check_ignore "a/submodule/one" 128 &&
452 test_stderr "fatal: Pathspec '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''"
453'
454
455test_expect_success_multi 'submodule from subdirectory' '' '
456 (
457 cd a &&
458 test_check_ignore "submodule/one" 128
459 ) &&
460 test_stderr "fatal: Pathspec '\''submodule/one'\'' is in submodule '\''a/submodule'\''"
461'
462
463############################################################################
464#
465# test handling of global ignore files
466
467test_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
476test_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
489test_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
506cat <<-\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
523EOF
524cat <<-\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
538EOF
539cat <<-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
553EOF
554
555sed -e 's/^"//' -e 's/\\//' -e 's/"$//' stdin | \
556 tr "\n" "\0" >stdin0
557sed -e 's/^"//' -e 's/\\//' -e 's/"$//' expected-default | \
558 tr "\n" "\0" >expected-default0
559sed -e 's/ "/ /' -e 's/\\//' -e 's/"$//' expected-verbose | \
560 tr ":\t\n" "\0" >expected-verbose0
561
562test_expect_success '--stdin' '
563 expect_from_stdin <expected-default &&
564 test_check_ignore "--stdin" <stdin
565'
566
567test_expect_success '--stdin -q' '
568 expect "" &&
569 test_check_ignore "-q --stdin" <stdin
570'
571
572test_expect_success '--stdin -v' '
573 expect_from_stdin <expected-verbose &&
574 test_check_ignore "-v --stdin" <stdin
575'
576
577for opts in '--stdin -z' '-z --stdin'
578do
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 "
593done
594
595cat <<-\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
613EOF
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
619cat <<-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
637EOF
638grep -v '^:: ' expected-all >expected-verbose
639sed -e 's/.* //' expected-verbose >expected-default
640
641sed -e 's/^"//' -e 's/\\//' -e 's/"$//' stdin | \
642 tr "\n" "\0" >stdin0
643sed -e 's/^"//' -e 's/\\//' -e 's/"$//' expected-default | \
644 tr "\n" "\0" >expected-default0
645sed -e 's/ "/ /' -e 's/\\//' -e 's/"$//' expected-verbose | \
646 tr ":\t\n" "\0" >expected-verbose0
647
648test_expect_success '--stdin from subdirectory' '
649 expect_from_stdin <expected-default &&
650 (
651 cd a &&
652 test_check_ignore "--stdin" <../stdin
653 )
654'
655
656test_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
664test_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
672for opts in '--stdin -z' '-z --stdin'
673do
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 '
689done
690
691test_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 #
701 # Similarly, we must keep "out" open so that check-ignore does
702 # not ever get SIGPIPE trying to write to us. Not only would that
703 # produce incorrect results, but then there would be no writer on the
704 # other end of the pipe, and we would potentially block forever trying
705 # to open it.
706 exec 9>in &&
707 exec 8<out &&
708 test_when_finished "exec 9>&-" &&
709 test_when_finished "exec 8<&-" &&
710 echo >&9 one &&
711 read response <&8 &&
712 echo "$response" | grep "^\.gitignore:1:one one" &&
713 echo >&9 two &&
714 read response <&8 &&
715 echo "$response" | grep "^:: two"
716'
717
718test_done