Skip to content

Commit a39b15b

Browse files
committed
Merge branch 'as/check-ignore'
Add a new command "git check-ignore" for debugging .gitignore files. The variable names may want to get cleaned up but that can be done in-tree. * as/check-ignore: clean.c, ls-files.c: respect encapsulation of exclude_list_groups t0008: avoid brace expansion add git-check-ignore sub-command setup.c: document get_pathspec() add.c: extract new die_if_path_beyond_symlink() for reuse add.c: extract check_path_for_gitlink() from treat_gitlinks() for reuse pathspec.c: rename newly public functions for clarity add.c: move pathspec matchers into new pathspec.c for reuse add.c: remove unused argument from validate_pathspec() dir.c: improve docs for match_pathspec() and match_pathspec_depth() dir.c: provide clear_directory() for reclaiming dir_struct memory dir.c: keep track of where patterns came from dir.c: use a single struct exclude_list per source of excludes Conflicts: builtin/ls-files.c dir.c
2 parents f12e49a + 72aeb18 commit a39b15b

20 files changed

+1251
-119
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
/git-bundle
2323
/git-cat-file
2424
/git-check-attr
25+
/git-check-ignore
2526
/git-check-ref-format
2627
/git-checkout
2728
/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
@@ -184,8 +184,10 @@ The second .gitignore prevents git from ignoring
184184

185185
SEE ALSO
186186
--------
187-
linkgit:git-rm[1], linkgit:git-update-index[1],
188-
linkgit:gitrepository-layout[5]
187+
linkgit:git-rm[1],
188+
linkgit:git-update-index[1],
189+
linkgit:gitrepository-layout[5],
190+
linkgit:git-check-ignore[1]
189191

190192
GIT
191193
---

Documentation/technical/api-directory-listing.txt

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,20 @@ marked. If you to exclude files, make sure you have loaded index first.
6767
* Prepare `struct dir_struct dir` and clear it with `memset(&dir, 0,
6868
sizeof(dir))`.
6969

70-
* Call `add_exclude()` to add single exclude pattern,
71-
`add_excludes_from_file()` to add patterns from a file
72-
(e.g. `.git/info/exclude`), and/or set `dir.exclude_per_dir`. A
73-
short-hand function `setup_standard_excludes()` can be used to set up
74-
the standard set of exclude settings.
70+
* To add single exclude pattern, call `add_exclude_list()` and then
71+
`add_exclude()`.
72+
73+
* To add patterns from a file (e.g. `.git/info/exclude`), call
74+
`add_excludes_from_file()` , and/or set `dir.exclude_per_dir`. A
75+
short-hand function `setup_standard_excludes()` can be used to set
76+
up the standard set of exclude settings.
7577

7678
* Set options described in the Data Structure section above.
7779

7880
* Call `read_directory()`.
7981

8082
* Use `dir.entries[]`.
8183

84+
* Call `clear_directory()` when none of the contained elements are no longer in use.
85+
8286
(JC)

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,7 @@ LIB_H += pack-revindex.h
666666
LIB_H += pack.h
667667
LIB_H += parse-options.h
668668
LIB_H += patch-ids.h
669+
LIB_H += pathspec.h
669670
LIB_H += pkt-line.h
670671
LIB_H += progress.h
671672
LIB_H += prompt.h
@@ -789,6 +790,7 @@ LIB_OBJS += parse-options-cb.o
789790
LIB_OBJS += patch-delta.o
790791
LIB_OBJS += patch-ids.o
791792
LIB_OBJS += path.o
793+
LIB_OBJS += pathspec.o
792794
LIB_OBJS += pkt-line.o
793795
LIB_OBJS += preload-index.o
794796
LIB_OBJS += pretty.o
@@ -854,6 +856,7 @@ BUILTIN_OBJS += builtin/branch.o
854856
BUILTIN_OBJS += builtin/bundle.o
855857
BUILTIN_OBJS += builtin/cat-file.o
856858
BUILTIN_OBJS += builtin/check-attr.o
859+
BUILTIN_OBJS += builtin/check-ignore.o
857860
BUILTIN_OBJS += builtin/check-ref-format.o
858861
BUILTIN_OBJS += builtin/checkout-index.o
859862
BUILTIN_OBJS += builtin/checkout.o

builtin.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ extern int cmd_cat_file(int argc, const char **argv, const char *prefix);
5252
extern int cmd_checkout(int argc, const char **argv, const char *prefix);
5353
extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
5454
extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
55+
extern int cmd_check_ignore(int argc, const char **argv, const char *prefix);
5556
extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
5657
extern int cmd_cherry(int argc, const char **argv, const char *prefix);
5758
extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);

builtin/add.c

Lines changed: 18 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "cache.h"
77
#include "builtin.h"
88
#include "dir.h"
9+
#include "pathspec.h"
910
#include "exec_cmd.h"
1011
#include "cache-tree.h"
1112
#include "run-command.h"
@@ -97,39 +98,6 @@ int add_files_to_cache(const char *prefix, const char **pathspec, int flags)
9798
return !!data.add_errors;
9899
}
99100

100-
static void fill_pathspec_matches(const char **pathspec, char *seen, int specs)
101-
{
102-
int num_unmatched = 0, i;
103-
104-
/*
105-
* Since we are walking the index as if we were walking the directory,
106-
* we have to mark the matched pathspec as seen; otherwise we will
107-
* mistakenly think that the user gave a pathspec that did not match
108-
* anything.
109-
*/
110-
for (i = 0; i < specs; i++)
111-
if (!seen[i])
112-
num_unmatched++;
113-
if (!num_unmatched)
114-
return;
115-
for (i = 0; i < active_nr; i++) {
116-
struct cache_entry *ce = active_cache[i];
117-
match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
118-
}
119-
}
120-
121-
static char *find_used_pathspec(const char **pathspec)
122-
{
123-
char *seen;
124-
int i;
125-
126-
for (i = 0; pathspec[i]; i++)
127-
; /* just counting */
128-
seen = xcalloc(i, 1);
129-
fill_pathspec_matches(pathspec, seen, i);
130-
return seen;
131-
}
132-
133101
static char *prune_directory(struct dir_struct *dir, const char **pathspec, int prefix)
134102
{
135103
char *seen;
@@ -149,35 +117,23 @@ static char *prune_directory(struct dir_struct *dir, const char **pathspec, int
149117
*dst++ = entry;
150118
}
151119
dir->nr = dst - dir->entries;
152-
fill_pathspec_matches(pathspec, seen, specs);
120+
add_pathspec_matches_against_index(pathspec, seen, specs);
153121
return seen;
154122
}
155123

124+
/*
125+
* Checks the index to see whether any path in pathspec refers to
126+
* something inside a submodule. If so, dies with an error message.
127+
*/
156128
static void treat_gitlinks(const char **pathspec)
157129
{
158130
int i;
159131

160132
if (!pathspec || !*pathspec)
161133
return;
162134

163-
for (i = 0; i < active_nr; i++) {
164-
struct cache_entry *ce = active_cache[i];
165-
if (S_ISGITLINK(ce->ce_mode)) {
166-
int len = ce_namelen(ce), j;
167-
for (j = 0; pathspec[j]; j++) {
168-
int len2 = strlen(pathspec[j]);
169-
if (len2 <= len || pathspec[j][len] != '/' ||
170-
memcmp(ce->name, pathspec[j], len))
171-
continue;
172-
if (len2 == len + 1)
173-
/* strip trailing slash */
174-
pathspec[j] = xstrndup(ce->name, len);
175-
else
176-
die (_("Path '%s' is in submodule '%.*s'"),
177-
pathspec[j], len, ce->name);
178-
}
179-
}
180-
}
135+
for (i = 0; pathspec[i]; i++)
136+
pathspec[i] = check_path_for_gitlink(pathspec[i]);
181137
}
182138

183139
static void refresh(int verbose, const char **pathspec)
@@ -197,17 +153,19 @@ static void refresh(int verbose, const char **pathspec)
197153
free(seen);
198154
}
199155

200-
static const char **validate_pathspec(int argc, const char **argv, const char *prefix)
156+
/*
157+
* Normalizes argv relative to prefix, via get_pathspec(), and then
158+
* runs die_if_path_beyond_symlink() on each path in the normalized
159+
* list.
160+
*/
161+
static const char **validate_pathspec(const char **argv, const char *prefix)
201162
{
202163
const char **pathspec = get_pathspec(prefix, argv);
203164

204165
if (pathspec) {
205166
const char **p;
206167
for (p = pathspec; *p; p++) {
207-
if (has_symlink_leading_path(*p, strlen(*p))) {
208-
int len = prefix ? strlen(prefix) : 0;
209-
die(_("'%s' is beyond a symbolic link"), *p + len);
210-
}
168+
die_if_path_beyond_symlink(*p, prefix);
211169
}
212170
}
213171

@@ -248,7 +206,7 @@ int interactive_add(int argc, const char **argv, const char *prefix, int patch)
248206
const char **pathspec = NULL;
249207

250208
if (argc) {
251-
pathspec = validate_pathspec(argc, argv, prefix);
209+
pathspec = validate_pathspec(argv, prefix);
252210
if (!pathspec)
253211
return -1;
254212
}
@@ -415,7 +373,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
415373
fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
416374
return 0;
417375
}
418-
pathspec = validate_pathspec(argc, argv, prefix);
376+
pathspec = validate_pathspec(argv, prefix);
419377

420378
if (read_cache() < 0)
421379
die(_("index file corrupt"));
@@ -448,7 +406,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
448406

449407
path_exclude_check_init(&check, &dir);
450408
if (!seen)
451-
seen = find_used_pathspec(pathspec);
409+
seen = find_pathspecs_matching_against_index(pathspec);
452410
for (i = 0; pathspec[i]; i++) {
453411
if (!seen[i] && pathspec[i][0]
454412
&& !file_exists(pathspec[i])) {

0 commit comments

Comments
 (0)