diff options
author | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2016-02-15 16:03:39 +0700 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2016-02-15 15:32:33 -0800 |
commit | d589a67eceacd1cc171bbe94906ca7c9a0edd8c5 (patch) | |
tree | fa380845f615c5f0742f6ff61bd20befbf06db77 /dir.c | |
parent | c62a91736a6dcd909167cf9fbb751d65c85482dd (diff) | |
download | git-d589a67eceacd1cc171bbe94906ca7c9a0edd8c5.tar.gz |
dir.c: don't exclude whole dir prematurely
If there is a pattern "!foo/bar", this patch makes it not exclude
"foo" right away. This gives us a chance to examine "foo" and
re-include "foo/bar".
Helped-by: brian m. carlson <sandals@crustytoothpaste.net>
Helped-by: Micha Wiedenmann <mw-u2@gmx.de>
Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Diffstat (limited to 'dir.c')
-rw-r--r-- | dir.c | 109 |
1 files changed, 108 insertions, 1 deletions
@@ -930,6 +930,75 @@ static int match_sticky(struct exclude *exc, const char *pathname, int pathlen, return 0; } +static inline int different_decisions(const struct exclude *a, + const struct exclude *b) +{ + return (a->flags & EXC_FLAG_NEGATIVE) != (b->flags & EXC_FLAG_NEGATIVE); +} + +/* + * Return non-zero if pathname is a directory and an ancestor of the + * literal path in a pattern. + */ +static int match_directory_part(const char *pathname, int pathlen, + int *dtype, struct exclude *x) +{ + const char *base = x->base; + int baselen = x->baselen ? x->baselen - 1 : 0; + const char *pattern = x->pattern; + int prefix = x->nowildcardlen; + int patternlen = x->patternlen; + + if (*dtype == DT_UNKNOWN) + *dtype = get_dtype(NULL, pathname, pathlen); + if (*dtype != DT_DIR) + return 0; + + if (*pattern == '/') { + pattern++; + patternlen--; + prefix--; + } + + if (baselen) { + if (((pathlen < baselen && base[pathlen] == '/') || + pathlen == baselen) && + !strncmp_icase(pathname, base, pathlen)) + return 1; + pathname += baselen + 1; + pathlen -= baselen + 1; + } + + + if (prefix && + (((pathlen < prefix && pattern[pathlen] == '/') || + pathlen == prefix) && + !strncmp_icase(pathname, pattern, pathlen))) + return 1; + + return 0; +} + +static struct exclude *should_descend(const char *pathname, int pathlen, + int *dtype, struct exclude_list *el, + struct exclude *exc) +{ + int i; + + for (i = el->nr - 1; 0 <= i; i--) { + struct exclude *x = el->excludes[i]; + + if (x == exc) + break; + + if (!(x->flags & EXC_FLAG_NODIR) && + different_decisions(x, exc) && + match_directory_part(pathname, pathlen, dtype, x)) + return x; + } + return NULL; +} + /* * Scan the given exclude list in reverse to see whether pathname * should be ignored. The first match (i.e. the last on the list), if @@ -943,7 +1012,7 @@ static struct exclude *last_exclude_matching_from_list(const char *pathname, struct exclude_list *el) { struct exclude *exc = NULL; /* undecided */ - int i; + int i, maybe_descend = 0; if (!el->nr) return NULL; /* undefined */ @@ -955,6 +1024,10 @@ static struct exclude *last_exclude_matching_from_list(const char *pathname, const char *exclude = x->pattern; int prefix = x->nowildcardlen; + if (!maybe_descend && i < el->nr - 1 && + different_decisions(x, el->excludes[i+1])) + maybe_descend = 1; + if (x->sticky_paths.nr) { if (*dtype == DT_UNKNOWN) *dtype = get_dtype(NULL, pathname, pathlen); @@ -998,6 +1071,34 @@ static struct exclude *last_exclude_matching_from_list(const char *pathname, return NULL; } + /* + * We have found a matching pattern "exc" that may exclude whole + * directory. We also found that there may be a pattern that matches + * something inside the directory and reincludes stuff. + * + * Go through the patterns again, find that pattern and double check. + * If it's true, return "undecided" and keep descending in. "exc" is + * marked sticky so that it continues to match inside the directory. + */ + if (!(exc->flags & EXC_FLAG_NEGATIVE) && maybe_descend) { + struct exclude *x; + + if (*dtype == DT_UNKNOWN) + *dtype = get_dtype(NULL, pathname, pathlen); + + if (*dtype == DT_DIR && + (x = should_descend(pathname, pathlen, dtype, el, exc))) { + add_sticky(exc, pathname, pathlen); + trace_printf_key(&trace_exclude, + "exclude: %.*s vs %s at line %d => %s," + " forced open by %s at line %d => n/a\n", + pathlen, pathname, exc->pattern, exc->srcpos, + exc->flags & EXC_FLAG_NEGATIVE ? "no" : "yes", + x->pattern, x->srcpos); + return NULL; + } + } + trace_printf_key(&trace_exclude, "exclude: %.*s vs %s at line %d => %s%s\n", pathlen, pathname, exc->pattern, exc->srcpos, exc->flags & EXC_FLAG_NEGATIVE ? "no" : "yes", @@ -2097,6 +2198,12 @@ int read_directory(struct dir_struct *dir, const char *path, int len, const stru return dir->nr; /* + * Stay on the safe side. if read_directory() has run once on + * "dir", some sticky flag may have been left. Clear them all. + */ + clear_sticky(dir); + + /* * exclude patterns are treated like positive ones in * create_simplify. Usually exclude patterns should be a * subset of positive ones, which has no impacts on |