Skip to content

Commit 368aa52

Browse files
aspiersgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 1794e6e commit 368aa52

File tree

11 files changed

+905
-3
lines changed

11 files changed

+905
-3
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
/git-bundle
2020
/git-cat-file
2121
/git-check-attr
22+
/git-check-ignore
2223
/git-check-ref-format
2324
/git-checkout
2425
/git-checkout-index

Documentation/git-check-ignore.txt

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,10 @@ The second .gitignore prevents git from ignoring
153153

154154
SEE ALSO
155155
--------
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]
158160

159161
GIT
160162
---

Documentation/technical/api-directory-listing.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,6 @@ marked. If you to exclude files, make sure you have loaded index first.
8181

8282
* Use `dir.entries[]`.
8383

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.
8585

8686
(JC)

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,7 @@ BUILTIN_OBJS += builtin/branch.o
822822
BUILTIN_OBJS += builtin/bundle.o
823823
BUILTIN_OBJS += builtin/cat-file.o
824824
BUILTIN_OBJS += builtin/check-attr.o
825+
BUILTIN_OBJS += builtin/check-ignore.o
825826
BUILTIN_OBJS += builtin/check-ref-format.o
826827
BUILTIN_OBJS += builtin/checkout-index.o
827828
BUILTIN_OBJS += builtin/checkout.o

builtin.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
5858
extern int cmd_checkout(int argc, const char **argv, const char *prefix);
5959
extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
6060
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);
6162
extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
6263
extern int cmd_cherry(int argc, const char **argv, const char *prefix);
6364
extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);

builtin/check-ignore.c

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ git-branch mainporcelain common
1212
git-bundle mainporcelain
1313
git-cat-file plumbinginterrogators
1414
git-check-attr purehelpers
15+
git-check-ignore purehelpers
1516
git-checkout mainporcelain common
1617
git-checkout-index plumbingmanipulators
1718
git-check-ref-format purehelpers

contrib/completion/git-completion.bash

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -842,6 +842,7 @@ __git_list_porcelain_commands ()
842842
archimport) : import;;
843843
cat-file) : plumbing;;
844844
check-attr) : plumbing;;
845+
check-ignore) : plumbing;;
845846
check-ref-format) : plumbing;;
846847
checkout-index) : plumbing;;
847848
commit-tree) : plumbing;;

git.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,7 @@ static void handle_internal_command(int argc, const char **argv)
340340
{ "bundle", cmd_bundle, RUN_SETUP_GENTLY },
341341
{ "cat-file", cmd_cat_file, RUN_SETUP },
342342
{ "check-attr", cmd_check_attr, RUN_SETUP },
343+
{ "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
343344
{ "check-ref-format", cmd_check_ref_format },
344345
{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
345346
{ "checkout-index", cmd_checkout_index,

0 commit comments

Comments
 (0)