add git-check-ignore sub-command

This works in a similar manner to git-check-attr. Thanks to Jeff King and Junio C Hamano for the idea: http://thread.gmane.org/gmane.comp.version-control.git/108671/focus=108815 Signed-off-by: Adam Spiers <git@adamspiers.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Adam Spiers committed Jan 6, 2013 at 16:58 UTC 368aa52952deb631b38a89837c4abbb00c5261c1
11 files changed +905 -3
.gitignore
+1
@@ -19,6 +19,7 @@
19 /git-bundle
20 /git-cat-file
21 /git-check-attr
22 +/git-check-ignore
23 /git-check-ref-format
24 /git-checkout
25 /git-checkout-index
Documentation/git-check-ignore.txt new
+89
@@ -0,0 +1,89 @@
1 +git-check-ignore(1)
2 +===================
3 +
4 +NAME
5 +----
6 +git-check-ignore - Debug gitignore / exclude files
7 +
8 +
9 +SYNOPSIS
10 +--------
11 +[verse]
12 +'git check-ignore' [options] pathname...
13 +'git check-ignore' [options] --stdin < <list-of-paths>
14 +
15 +DESCRIPTION
16 +-----------
17 +
18 +For each pathname given via the command-line or from a file via
19 +`--stdin`, show the pattern from .gitignore (or other input files to
20 +the exclude mechanism) that decides if the pathname is excluded or
21 +included. Later patterns within a file take precedence over earlier
22 +ones.
23 +
24 +OPTIONS
25 +-------
26 +-q, --quiet::
27 + Don't output anything, just set exit status. This is only
28 + valid with a single pathname.
29 +
30 +-v, --verbose::
31 + Also output details about the matching pattern (if any)
32 + for each given pathname.
33 +
34 +--stdin::
35 + Read file names from stdin instead of from the command-line.
36 +
37 +-z::
38 + The output format is modified to be machine-parseable (see
39 + below). If `--stdin` is also given, input paths are separated
40 + with a NUL character instead of a linefeed character.
41 +
42 +OUTPUT
43 +------
44 +
45 +By default, any of the given pathnames which match an ignore pattern
46 +will be output, one per line. If no pattern matches a given path,
47 +nothing will be output for that path; this means that path will not be
48 +ignored.
49 +
50 +If `--verbose` is specified, the output is a series of lines of the form:
51 +
52 +<source> <COLON> <linenum> <COLON> <pattern> <HT> <pathname>
53 +
54 +<pathname> is the path of a file being queried, <pattern> is the
55 +matching pattern, <source> is the pattern's source file, and <linenum>
56 +is the line number of the pattern within that source. If the pattern
57 +contained a `!` prefix or `/` suffix, it will be preserved in the
58 +output. <source> will be an absolute path when referring to the file
59 +configured by `core.excludesfile`, or relative to the repository root
60 +when referring to `.git/info/exclude` or a per-directory exclude file.
61 +
62 +If `-z` is specified, the pathnames in the output are delimited by the
63 +null character; if `--verbose` is also specified then null characters
64 +are also used instead of colons and hard tabs:
65 +
66 +<source> <NULL> <linenum> <NULL> <pattern> <NULL> <pathname> <NULL>
67 +
68 +
69 +EXIT STATUS
70 +-----------
71 +
72 +0::
73 + One or more of the provided paths is ignored.
74 +
75 +1::
76 + None of the provided paths are ignored.
77 +
78 +128::
79 + A fatal error was encountered.
80 +
81 +SEE ALSO
82 +--------
83 +linkgit:gitignore[5]
84 +linkgit:gitconfig[5]
85 +linkgit:git-ls-files[5]
86 +
87 +GIT
88 +---
89 +Part of the linkgit:git[1] suite
Documentation/gitignore.txt
+4 -2
@@ -153,8 +153,10 @@ The second .gitignore prevents git from ignoring
153
154 SEE ALSO
155 --------
156 -linkgit:git-rm[1], linkgit:git-update-index[1],
157 -linkgit:gitrepository-layout[5]
156 +linkgit:git-rm[1],
157 +linkgit:git-update-index[1],
158 +linkgit:gitrepository-layout[5],
159 +linkgit:git-check-ignore[1]
160
161 GIT
162 ---
Documentation/technical/api-directory-listing.txt
+1 -1
@@ -81,6 +81,6 @@ marked. If you to exclude files, make sure you have loaded index first.
81
82 * Use `dir.entries[]`.
83
84 -* Call `free_directory()` when none of the contained elements are no longer in use.
84 +* Call `clear_directory()` when none of the contained elements are no longer in use.
85
86 (JC)
Makefile
+1
@@ -822,6 +822,7 @@ BUILTIN_OBJS += builtin/branch.o
822 BUILTIN_OBJS += builtin/bundle.o
823 BUILTIN_OBJS += builtin/cat-file.o
824 BUILTIN_OBJS += builtin/check-attr.o
825 +BUILTIN_OBJS += builtin/check-ignore.o
826 BUILTIN_OBJS += builtin/check-ref-format.o
827 BUILTIN_OBJS += builtin/checkout-index.o
828 BUILTIN_OBJS += builtin/checkout.o
builtin.h
+1
@@ -58,6 +58,7 @@ extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
58 extern int cmd_checkout(int argc, const char **argv, const char *prefix);
59 extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
60 extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
61 +extern int cmd_check_ignore(int argc, const char **argv, const char *prefix);
62 extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
63 extern int cmd_cherry(int argc, const char **argv, const char *prefix);
64 extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
builtin/check-ignore.c new
+173
@@ -0,0 +1,173 @@
1 +#include "builtin.h"
2 +#include "cache.h"
3 +#include "dir.h"
4 +#include "quote.h"
5 +#include "pathspec.h"
6 +#include "parse-options.h"
7 +
8 +static int quiet, verbose, stdin_paths;
9 +static const char * const check_ignore_usage[] = {
10 +"git check-ignore [options] pathname...",
11 +"git check-ignore [options] --stdin < <list-of-paths>",
12 +NULL
13 +};
14 +
15 +static int null_term_line;
16 +
17 +static const struct option check_ignore_options[] = {
18 + OPT__QUIET(&quiet, N_("suppress progress reporting")),
19 + OPT__VERBOSE(&verbose, N_("be verbose")),
20 + OPT_GROUP(""),
21 + OPT_BOOLEAN(0, "stdin", &stdin_paths,
22 + N_("read file names from stdin")),
23 + OPT_BOOLEAN('z', NULL, &null_term_line,
24 + N_("input paths are terminated by a null character")),
25 + OPT_END()
26 +};
27 +
28 +static void output_exclude(const char *path, struct exclude *exclude)
29 +{
30 + char *bang = exclude->flags & EXC_FLAG_NEGATIVE ? "!" : "";
31 + char *slash = exclude->flags & EXC_FLAG_MUSTBEDIR ? "/" : "";
32 + if (!null_term_line) {
33 + if (!verbose) {
34 + write_name_quoted(path, stdout, '\n');
35 + } else {
36 + quote_c_style(exclude->el->src, NULL, stdout, 0);
37 + printf(":%d:%s%s%s\t",
38 + exclude->srcpos,
39 + bang, exclude->pattern, slash);
40 + quote_c_style(path, NULL, stdout, 0);
41 + fputc('\n', stdout);
42 + }
43 + } else {
44 + if (!verbose) {
45 + printf("%s%c", path, '\0');
46 + } else {
47 + printf("%s%c%d%c%s%s%s%c%s%c",
48 + exclude->el->src, '\0',
49 + exclude->srcpos, '\0',
50 + bang, exclude->pattern, slash, '\0',
51 + path, '\0');
52 + }
53 + }
54 +}
55 +
56 +static int check_ignore(const char *prefix, const char **pathspec)
57 +{
58 + struct dir_struct dir;
59 + const char *path, *full_path;
60 + char *seen;
61 + int num_ignored = 0, dtype = DT_UNKNOWN, i;
62 + struct path_exclude_check check;
63 + struct exclude *exclude;
64 +
65 + /* read_cache() is only necessary so we can watch out for submodules. */
66 + if (read_cache() < 0)
67 + die(_("index file corrupt"));
68 +
69 + memset(&dir, 0, sizeof(dir));
70 + dir.flags |= DIR_COLLECT_IGNORED;
71 + setup_standard_excludes(&dir);
72 +
73 + if (!pathspec || !*pathspec) {
74 + if (!quiet)
75 + fprintf(stderr, "no pathspec given.\n");
76 + return 0;
77 + }
78 +
79 + path_exclude_check_init(&check, &dir);
80 + /*
81 + * look for pathspecs matching entries in the index, since these
82 + * should not be ignored, in order to be consistent with
83 + * 'git status', 'git add' etc.
84 + */
85 + seen = find_pathspecs_matching_against_index(pathspec);
86 + for (i = 0; pathspec[i]; i++) {
87 + path = pathspec[i];
88 + full_path = prefix_path(prefix, prefix
89 + ? strlen(prefix) : 0, path);
90 + full_path = check_path_for_gitlink(full_path);
91 + die_if_path_beyond_symlink(full_path, prefix);
92 + if (!seen[i] && path[0]) {
93 + exclude = last_exclude_matching_path(&check, full_path,
94 + -1, &dtype);
95 + if (exclude) {
96 + if (!quiet)
97 + output_exclude(path, exclude);
98 + num_ignored++;
99 + }
100 + }
101 + }
102 + free(seen);
103 + clear_directory(&dir);
104 + path_exclude_check_clear(&check);
105 +
106 + return num_ignored;
107 +}
108 +
109 +static int check_ignore_stdin_paths(const char *prefix)
110 +{
111 + struct strbuf buf, nbuf;
112 + char **pathspec = NULL;
113 + size_t nr = 0, alloc = 0;
114 + int line_termination = null_term_line ? 0 : '\n';
115 + int num_ignored;
116 +
117 + strbuf_init(&buf, 0);
118 + strbuf_init(&nbuf, 0);
119 + while (strbuf_getline(&buf, stdin, line_termination) != EOF) {
120 + if (line_termination && buf.buf[0] == '"') {
121 + strbuf_reset(&nbuf);
122 + if (unquote_c_style(&nbuf, buf.buf, NULL))
123 + die("line is badly quoted");
124 + strbuf_swap(&buf, &nbuf);
125 + }
126 + ALLOC_GROW(pathspec, nr + 1, alloc);
127 + pathspec[nr] = xcalloc(strlen(buf.buf) + 1, sizeof(*buf.buf));
128 + strcpy(pathspec[nr++], buf.buf);
129 + }
130 + ALLOC_GROW(pathspec, nr + 1, alloc);
131 + pathspec[nr] = NULL;
132 + num_ignored = check_ignore(prefix, (const char **)pathspec);
133 + maybe_flush_or_die(stdout, "attribute to stdout");
134 + strbuf_release(&buf);
135 + strbuf_release(&nbuf);
136 + free(pathspec);
137 + return num_ignored;
138 +}
139 +
140 +int cmd_check_ignore(int argc, const char **argv, const char *prefix)
141 +{
142 + int num_ignored;
143 +
144 + git_config(git_default_config, NULL);
145 +
146 + argc = parse_options(argc, argv, prefix, check_ignore_options,
147 + check_ignore_usage, 0);
148 +
149 + if (stdin_paths) {
150 + if (argc > 0)
151 + die(_("cannot specify pathnames with --stdin"));
152 + } else {
153 + if (null_term_line)
154 + die(_("-z only makes sense with --stdin"));
155 + if (argc == 0)
156 + die(_("no path specified"));
157 + }
158 + if (quiet) {
159 + if (argc > 1)
160 + die(_("--quiet is only valid with a single pathname"));
161 + if (verbose)
162 + die(_("cannot have both --quiet and --verbose"));
163 + }
164 +
165 + if (stdin_paths) {
166 + num_ignored = check_ignore_stdin_paths(prefix);
167 + } else {
168 + num_ignored = check_ignore(prefix, argv);
169 + maybe_flush_or_die(stdout, "ignore to stdout");
170 + }
171 +
172 + return !num_ignored;
173 +}
command-list.txt
+1
@@ -12,6 +12,7 @@ git-branch mainporcelain common
12 git-bundle mainporcelain
13 git-cat-file plumbinginterrogators
14 git-check-attr purehelpers
15 +git-check-ignore purehelpers
16 git-checkout mainporcelain common
17 git-checkout-index plumbingmanipulators
18 git-check-ref-format purehelpers
contrib/completion/git-completion.bash
+1
@@ -842,6 +842,7 @@ __git_list_porcelain_commands ()
842 archimport) : import;;
843 cat-file) : plumbing;;
844 check-attr) : plumbing;;
845 + check-ignore) : plumbing;;
846 check-ref-format) : plumbing;;
847 checkout-index) : plumbing;;
848 commit-tree) : plumbing;;
git.c
+1
@@ -340,6 +340,7 @@ static void handle_internal_command(int argc, const char **argv)
340 { "bundle", cmd_bundle, RUN_SETUP_GENTLY },
341 { "cat-file", cmd_cat_file, RUN_SETUP },
342 { "check-attr", cmd_check_attr, RUN_SETUP },
343 + { "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
344 { "check-ref-format", cmd_check_ref_format },
345 { "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
346 { "checkout-index", cmd_checkout_index,
t/t0008-ignores.sh new
+632
@@ -0,0 +1,632 @@
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 $args \
70 + >"$HOME/cmd" &&
71 + test_expect_code "$expect_code" \
72 + git $global_args check-ignore $quiet_opt $verbose_opt $args \
73 + >"$HOME/stdout" 2>"$HOME/stderr" &&
74 + test_cmp "$HOME/expected-stdout" "$HOME/stdout" &&
75 + stderr_empty_on_success "$expect_code"
76 +}
77 +
78 +test_expect_success_multi () {
79 + prereq=
80 + if test $# -eq 4
81 + then
82 + prereq=$1
83 + shift
84 + fi
85 + testname="$1" expect_verbose="$2" code="$3"
86 +
87 + expect=$( echo "$expect_verbose" | sed -e 's/.* //' )
88 +
89 + test_expect_success $prereq "$testname" '
90 + expect "$expect" &&
91 + eval "$code"
92 + '
93 +
94 + for quiet_opt in '-q' '--quiet'
95 + do
96 + test_expect_success $prereq "$testname${quiet_opt:+ with $quiet_opt}" "
97 + expect '' &&
98 + $code
99 + "
100 + done
101 + quiet_opt=
102 +
103 + for verbose_opt in '-v' '--verbose'
104 + do
105 + test_expect_success $prereq "$testname${verbose_opt:+ with $verbose_opt}" "
106 + expect '$expect_verbose' &&
107 + $code
108 + "
109 + done
110 + verbose_opt=
111 +}
112 +
113 +test_expect_success 'setup' '
114 + init_vars &&
115 + mkdir -p a/b/ignored-dir a/submodule b &&
116 + if test_have_prereq SYMLINKS
117 + then
118 + ln -s b a/symlink
119 + fi &&
120 + (
121 + cd a/submodule &&
122 + git init &&
123 + echo a >a &&
124 + git add a &&
125 + git commit -m"commit in submodule"
126 + ) &&
127 + git add a/submodule &&
128 + cat <<-\EOF >.gitignore &&
129 + one
130 + ignored-*
131 + EOF
132 + touch {,a/}{not-ignored,ignored-{and-untracked,but-in-index}} &&
133 + git add -f {,a/}ignored-but-in-index
134 + cat <<-\EOF >a/.gitignore &&
135 + two*
136 + *three
137 + EOF
138 + cat <<-\EOF >a/b/.gitignore &&
139 + four
140 + five
141 + # this comment should affect the line numbers
142 + six
143 + ignored-dir/
144 + # and so should this blank line:
145 +
146 + !on*
147 + !two
148 + EOF
149 + echo "seven" >a/b/ignored-dir/.gitignore &&
150 + test -n "$HOME" &&
151 + cat <<-\EOF >"$global_excludes" &&
152 + globalone
153 + !globaltwo
154 + globalthree
155 + EOF
156 + cat <<-\EOF >>.git/info/exclude
157 + per-repo
158 + EOF
159 +'
160 +
161 +############################################################################
162 +#
163 +# test invalid inputs
164 +
165 +test_expect_success_multi 'empty command line' '' '
166 + test_check_ignore "" 128 &&
167 + stderr_contains "fatal: no path specified"
168 +'
169 +
170 +test_expect_success_multi '--stdin with empty STDIN' '' '
171 + test_check_ignore "--stdin" 1 </dev/null &&
172 + if test -n "$quiet_opt"; then
173 + test_stderr ""
174 + else
175 + test_stderr "no pathspec given."
176 + fi
177 +'
178 +
179 +test_expect_success '-q with multiple args' '
180 + expect "" &&
181 + test_check_ignore "-q one two" 128 &&
182 + stderr_contains "fatal: --quiet is only valid with a single pathname"
183 +'
184 +
185 +test_expect_success '--quiet with multiple args' '
186 + expect "" &&
187 + test_check_ignore "--quiet one two" 128 &&
188 + stderr_contains "fatal: --quiet is only valid with a single pathname"
189 +'
190 +
191 +for verbose_opt in '-v' '--verbose'
192 +do
193 + for quiet_opt in '-q' '--quiet'
194 + do
195 + test_expect_success "$quiet_opt $verbose_opt" "
196 + expect '' &&
197 + test_check_ignore '$quiet_opt $verbose_opt foo' 128 &&
198 + stderr_contains 'fatal: cannot have both --quiet and --verbose'
199 + "
200 + done
201 +done
202 +
203 +test_expect_success '--quiet with multiple args' '
204 + expect "" &&
205 + test_check_ignore "--quiet one two" 128 &&
206 + stderr_contains "fatal: --quiet is only valid with a single pathname"
207 +'
208 +
209 +test_expect_success_multi 'erroneous use of --' '' '
210 + test_check_ignore "--" 128 &&
211 + stderr_contains "fatal: no path specified"
212 +'
213 +
214 +test_expect_success_multi '--stdin with superfluous arg' '' '
215 + test_check_ignore "--stdin foo" 128 &&
216 + stderr_contains "fatal: cannot specify pathnames with --stdin"
217 +'
218 +
219 +test_expect_success_multi '--stdin -z with superfluous arg' '' '
220 + test_check_ignore "--stdin -z foo" 128 &&
221 + stderr_contains "fatal: cannot specify pathnames with --stdin"
222 +'
223 +
224 +test_expect_success_multi '-z without --stdin' '' '
225 + test_check_ignore "-z" 128 &&
226 + stderr_contains "fatal: -z only makes sense with --stdin"
227 +'
228 +
229 +test_expect_success_multi '-z without --stdin and superfluous arg' '' '
230 + test_check_ignore "-z foo" 128 &&
231 + stderr_contains "fatal: -z only makes sense with --stdin"
232 +'
233 +
234 +test_expect_success_multi 'needs work tree' '' '
235 + (
236 + cd .git &&
237 + test_check_ignore "foo" 128
238 + ) &&
239 + stderr_contains "fatal: This operation must be run in a work tree"
240 +'
241 +
242 +############################################################################
243 +#
244 +# test standard ignores
245 +
246 +# First make sure that the presence of a file in the working tree
247 +# does not impact results, but that the presence of a file in the
248 +# index does.
249 +
250 +for subdir in '' 'a/'
251 +do
252 + if test -z "$subdir"
253 + then
254 + where="at top-level"
255 + else
256 + where="in subdir $subdir"
257 + fi
258 +
259 + test_expect_success_multi "non-existent file $where not ignored" '' "
260 + test_check_ignore '${subdir}non-existent' 1
261 + "
262 +
263 + test_expect_success_multi "non-existent file $where ignored" \
264 + ".gitignore:1:one ${subdir}one" "
265 + test_check_ignore '${subdir}one'
266 + "
267 +
268 + test_expect_success_multi "existing untracked file $where not ignored" '' "
269 + test_check_ignore '${subdir}not-ignored' 1
270 + "
271 +
272 + test_expect_success_multi "existing tracked file $where not ignored" '' "
273 + test_check_ignore '${subdir}ignored-but-in-index' 1
274 + "
275 +
276 + test_expect_success_multi "existing untracked file $where ignored" \
277 + ".gitignore:2:ignored-* ${subdir}ignored-and-untracked" "
278 + test_check_ignore '${subdir}ignored-and-untracked'
279 + "
280 +done
281 +
282 +# Having established the above, from now on we mostly test against
283 +# files which do not exist in the working tree or index.
284 +
285 +test_expect_success 'sub-directory local ignore' '
286 + expect "a/3-three" &&
287 + test_check_ignore "a/3-three a/three-not-this-one"
288 +'
289 +
290 +test_expect_success 'sub-directory local ignore with --verbose' '
291 + expect "a/.gitignore:2:*three a/3-three" &&
292 + test_check_ignore "--verbose a/3-three a/three-not-this-one"
293 +'
294 +
295 +test_expect_success 'local ignore inside a sub-directory' '
296 + expect "3-three" &&
297 + (
298 + cd a &&
299 + test_check_ignore "3-three three-not-this-one"
300 + )
301 +'
302 +test_expect_success 'local ignore inside a sub-directory with --verbose' '
303 + expect "a/.gitignore:2:*three 3-three" &&
304 + (
305 + cd a &&
306 + test_check_ignore "--verbose 3-three three-not-this-one"
307 + )
308 +'
309 +
310 +test_expect_success_multi 'nested include' \
311 + 'a/b/.gitignore:8:!on* a/b/one' '
312 + test_check_ignore "a/b/one"
313 +'
314 +
315 +############################################################################
316 +#
317 +# test ignored sub-directories
318 +
319 +test_expect_success_multi 'ignored sub-directory' \
320 + 'a/b/.gitignore:5:ignored-dir/ a/b/ignored-dir' '
321 + test_check_ignore "a/b/ignored-dir"
322 +'
323 +
324 +test_expect_success 'multiple files inside ignored sub-directory' '
325 + expect_from_stdin <<-\EOF &&
326 + a/b/ignored-dir/foo
327 + a/b/ignored-dir/twoooo
328 + a/b/ignored-dir/seven
329 + EOF
330 + test_check_ignore "a/b/ignored-dir/foo a/b/ignored-dir/twoooo a/b/ignored-dir/seven"
331 +'
332 +
333 +test_expect_success 'multiple files inside ignored sub-directory with -v' '
334 + expect_from_stdin <<-\EOF &&
335 + a/b/.gitignore:5:ignored-dir/ a/b/ignored-dir/foo
336 + a/b/.gitignore:5:ignored-dir/ a/b/ignored-dir/twoooo
337 + a/b/.gitignore:5:ignored-dir/ a/b/ignored-dir/seven
338 + EOF
339 + test_check_ignore "-v a/b/ignored-dir/foo a/b/ignored-dir/twoooo a/b/ignored-dir/seven"
340 +'
341 +
342 +test_expect_success 'cd to ignored sub-directory' '
343 + expect_from_stdin <<-\EOF &&
344 + foo
345 + twoooo
346 + ../one
347 + seven
348 + ../../one
349 + EOF
350 + (
351 + cd a/b/ignored-dir &&
352 + test_check_ignore "foo twoooo ../one seven ../../one"
353 + )
354 +'
355 +
356 +test_expect_success 'cd to ignored sub-directory with -v' '
357 + expect_from_stdin <<-\EOF &&
358 + a/b/.gitignore:5:ignored-dir/ foo
359 + a/b/.gitignore:5:ignored-dir/ twoooo
360 + a/b/.gitignore:8:!on* ../one
361 + a/b/.gitignore:5:ignored-dir/ seven
362 + .gitignore:1:one ../../one
363 + EOF
364 + (
365 + cd a/b/ignored-dir &&
366 + test_check_ignore "-v foo twoooo ../one seven ../../one"
367 + )
368 +'
369 +
370 +############################################################################
371 +#
372 +# test handling of symlinks
373 +
374 +test_expect_success_multi SYMLINKS 'symlink' '' '
375 + test_check_ignore "a/symlink" 1
376 +'
377 +
378 +test_expect_success_multi SYMLINKS 'beyond a symlink' '' '
379 + test_check_ignore "a/symlink/foo" 128 &&
380 + test_stderr "fatal: '\''a/symlink/foo'\'' is beyond a symbolic link"
381 +'
382 +
383 +test_expect_success_multi SYMLINKS 'beyond a symlink from subdirectory' '' '
384 + (
385 + cd a &&
386 + test_check_ignore "symlink/foo" 128
387 + ) &&
388 + test_stderr "fatal: '\''symlink/foo'\'' is beyond a symbolic link"
389 +'
390 +
391 +############################################################################
392 +#
393 +# test handling of submodules
394 +
395 +test_expect_success_multi 'submodule' '' '
396 + test_check_ignore "a/submodule/one" 128 &&
397 + test_stderr "fatal: Path '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''"
398 +'
399 +
400 +test_expect_success_multi 'submodule from subdirectory' '' '
401 + (
402 + cd a &&
403 + test_check_ignore "submodule/one" 128
404 + ) &&
405 + test_stderr "fatal: Path '\''a/submodule/one'\'' is in submodule '\''a/submodule'\''"
406 +'
407 +
408 +############################################################################
409 +#
410 +# test handling of global ignore files
411 +
412 +test_expect_success 'global ignore not yet enabled' '
413 + expect_from_stdin <<-\EOF &&
414 + .git/info/exclude:7:per-repo per-repo
415 + a/.gitignore:2:*three a/globalthree
416 + .git/info/exclude:7:per-repo a/per-repo
417 + EOF
418 + test_check_ignore "-v globalone per-repo a/globalthree a/per-repo not-ignored a/globaltwo"
419 +'
420 +
421 +test_expect_success 'global ignore' '
422 + enable_global_excludes &&
423 + expect_from_stdin <<-\EOF &&
424 + globalone
425 + per-repo
426 + globalthree
427 + a/globalthree
428 + a/per-repo
429 + globaltwo
430 + EOF
431 + test_check_ignore "globalone per-repo globalthree a/globalthree a/per-repo not-ignored globaltwo"
432 +'
433 +
434 +test_expect_success 'global ignore with -v' '
435 + enable_global_excludes &&
436 + expect_from_stdin <<-EOF &&
437 + $global_excludes:1:globalone globalone
438 + .git/info/exclude:7:per-repo per-repo
439 + $global_excludes:3:globalthree globalthree
440 + a/.gitignore:2:*three a/globalthree
441 + .git/info/exclude:7:per-repo a/per-repo
442 + $global_excludes:2:!globaltwo globaltwo
443 + EOF
444 + test_check_ignore "-v globalone per-repo globalthree a/globalthree a/per-repo not-ignored globaltwo"
445 +'
446 +
447 +############################################################################
448 +#
449 +# test --stdin
450 +
451 +cat <<-\EOF >stdin
452 + one
453 + not-ignored
454 + a/one
455 + a/not-ignored
456 + a/b/on
457 + a/b/one
458 + a/b/one one
459 + "a/b/one two"
460 + "a/b/one\"three"
461 + a/b/not-ignored
462 + a/b/two
463 + a/b/twooo
464 + globaltwo
465 + a/globaltwo
466 + a/b/globaltwo
467 + b/globaltwo
468 +EOF
469 +cat <<-\EOF >expected-default
470 + one
471 + a/one
472 + a/b/on
473 + a/b/one
474 + a/b/one one
475 + a/b/one two
476 + "a/b/one\"three"
477 + a/b/two
478 + a/b/twooo
479 + globaltwo
480 + a/globaltwo
481 + a/b/globaltwo
482 + b/globaltwo
483 +EOF
484 +cat <<-EOF >expected-verbose
485 + .gitignore:1:one one
486 + .gitignore:1:one a/one
487 + a/b/.gitignore:8:!on* a/b/on
488 + a/b/.gitignore:8:!on* a/b/one
489 + a/b/.gitignore:8:!on* a/b/one one
490 + a/b/.gitignore:8:!on* a/b/one two
491 + a/b/.gitignore:8:!on* "a/b/one\"three"
492 + a/b/.gitignore:9:!two a/b/two
493 + a/.gitignore:1:two* a/b/twooo
494 + $global_excludes:2:!globaltwo globaltwo
495 + $global_excludes:2:!globaltwo a/globaltwo
496 + $global_excludes:2:!globaltwo a/b/globaltwo
497 + $global_excludes:2:!globaltwo b/globaltwo
498 +EOF
499 +
500 +sed -e 's/^"//' -e 's/\\//' -e 's/"$//' stdin | \
501 + tr "\n" "\0" >stdin0
502 +sed -e 's/^"//' -e 's/\\//' -e 's/"$//' expected-default | \
503 + tr "\n" "\0" >expected-default0
504 +sed -e 's/ "/ /' -e 's/\\//' -e 's/"$//' expected-verbose | \
505 + tr ":\t\n" "\0" >expected-verbose0
506 +
507 +test_expect_success '--stdin' '
508 + expect_from_stdin <expected-default &&
509 + test_check_ignore "--stdin" <stdin
510 +'
511 +
512 +test_expect_success '--stdin -q' '
513 + expect "" &&
514 + test_check_ignore "-q --stdin" <stdin
515 +'
516 +
517 +test_expect_success '--stdin -v' '
518 + expect_from_stdin <expected-verbose &&
519 + test_check_ignore "-v --stdin" <stdin
520 +'
521 +
522 +for opts in '--stdin -z' '-z --stdin'
523 +do
524 + test_expect_success "$opts" "
525 + expect_from_stdin <expected-default0 &&
526 + test_check_ignore '$opts' <stdin0
527 + "
528 +
529 + test_expect_success "$opts -q" "
530 + expect "" &&
531 + test_check_ignore '-q $opts' <stdin0
532 + "
533 +
534 + test_expect_success "$opts -v" "
535 + expect_from_stdin <expected-verbose0 &&
536 + test_check_ignore '-v $opts' <stdin0
537 + "
538 +done
539 +
540 +cat <<-\EOF >stdin
541 + ../one
542 + ../not-ignored
543 + one
544 + not-ignored
545 + b/on
546 + b/one
547 + b/one one
548 + "b/one two"
549 + "b/one\"three"
550 + b/two
551 + b/not-ignored
552 + b/twooo
553 + ../globaltwo
554 + globaltwo
555 + b/globaltwo
556 + ../b/globaltwo
557 +EOF
558 +cat <<-\EOF >expected-default
559 + ../one
560 + one
561 + b/on
562 + b/one
563 + b/one one
564 + b/one two
565 + "b/one\"three"
566 + b/two
567 + b/twooo
568 + ../globaltwo
569 + globaltwo
570 + b/globaltwo
571 + ../b/globaltwo
572 +EOF
573 +cat <<-EOF >expected-verbose
574 + .gitignore:1:one ../one
575 + .gitignore:1:one one
576 + a/b/.gitignore:8:!on* b/on
577 + a/b/.gitignore:8:!on* b/one
578 + a/b/.gitignore:8:!on* b/one one
579 + a/b/.gitignore:8:!on* b/one two
580 + a/b/.gitignore:8:!on* "b/one\"three"
581 + a/b/.gitignore:9:!two b/two
582 + a/.gitignore:1:two* b/twooo
583 + $global_excludes:2:!globaltwo ../globaltwo
584 + $global_excludes:2:!globaltwo globaltwo
585 + $global_excludes:2:!globaltwo b/globaltwo
586 + $global_excludes:2:!globaltwo ../b/globaltwo
587 +EOF
588 +
589 +sed -e 's/^"//' -e 's/\\//' -e 's/"$//' stdin | \
590 + tr "\n" "\0" >stdin0
591 +sed -e 's/^"//' -e 's/\\//' -e 's/"$//' expected-default | \
592 + tr "\n" "\0" >expected-default0
593 +sed -e 's/ "/ /' -e 's/\\//' -e 's/"$//' expected-verbose | \
594 + tr ":\t\n" "\0" >expected-verbose0
595 +
596 +test_expect_success '--stdin from subdirectory' '
597 + expect_from_stdin <expected-default &&
598 + (
599 + cd a &&
600 + test_check_ignore "--stdin" <../stdin
601 + )
602 +'
603 +
604 +test_expect_success '--stdin from subdirectory with -v' '
605 + expect_from_stdin <expected-verbose &&
606 + (
607 + cd a &&
608 + test_check_ignore "--stdin -v" <../stdin
609 + )
610 +'
611 +
612 +for opts in '--stdin -z' '-z --stdin'
613 +do
614 + test_expect_success "$opts from subdirectory" '
615 + expect_from_stdin <expected-default0 &&
616 + (
617 + cd a &&
618 + test_check_ignore "'"$opts"'" <../stdin0
619 + )
620 + '
621 +
622 + test_expect_success "$opts from subdirectory with -v" '
623 + expect_from_stdin <expected-verbose0 &&
624 + (
625 + cd a &&
626 + test_check_ignore "'"$opts"' -v" <../stdin0
627 + )
628 + '
629 +done
630 +
631 +
632 +test_done