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