From e6efecc46a34a984535e6a90e45a9db45af4eff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 21 Sep 2015 16:56:14 +0700 Subject: dir.c: make last_exclude_matching_from_list() run til the end MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The next patch adds some post processing to the result value before it's returned to the caller. Keep all branches reach the end of the function, so we can do it all in one place. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- dir.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'dir.c') diff --git a/dir.c b/dir.c index 0943a81964..4893181a0c 100644 --- a/dir.c +++ b/dir.c @@ -752,6 +752,7 @@ static struct exclude *last_exclude_matching_from_list(const char *pathname, int *dtype, struct exclude_list *el) { + struct exclude *exc = NULL; /* undecided */ int i; if (!el->nr) @@ -773,18 +774,22 @@ static struct exclude *last_exclude_matching_from_list(const char *pathname, if (match_basename(basename, pathlen - (basename - pathname), exclude, prefix, x->patternlen, - x->flags)) - return x; + x->flags)) { + exc = x; + break; + } continue; } assert(x->baselen == 0 || x->base[x->baselen - 1] == '/'); if (match_pathname(pathname, pathlen, x->base, x->baselen ? x->baselen - 1 : 0, - exclude, prefix, x->patternlen, x->flags)) - return x; + exclude, prefix, x->patternlen, x->flags)) { + exc = x; + break; + } } - return NULL; /* undecided */ + return exc; } /* -- cgit v1.2.3 From 57534ee77d22e725d971ee89c77dc6aad61c573f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Th=C3=A1i=20Ng=E1=BB=8Dc=20Duy?= Date: Mon, 21 Sep 2015 16:56:15 +0700 Subject: dir.c: don't exclude whole dir prematurely if neg pattern may match MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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". In order for it to detect that the directory under examination should not be excluded right away, in other words it is a parent directory of a negative pattern, the "directory path" of the negative pattern must be literal. Patterns like "!f?o/bar" can't stop "foo" from being excluded. Basename matching (i.e. "no slashes in the pattern") or must-be-dir matching (i.e. "trailing slash in the pattern") does not work well with this. For example, if we descend in "foo" and are examining "foo/abc", current code for "foo/" pattern will check if path "foo/abc", not "foo", is a directory. The same problem with basename matching. These may need big code reorg to make it work. Signed-off-by: Nguyễn Thái Ngọc Duy Signed-off-by: Junio C Hamano --- dir.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) (limited to 'dir.c') diff --git a/dir.c b/dir.c index 4893181a0c..894027c0cd 100644 --- a/dir.c +++ b/dir.c @@ -733,6 +733,25 @@ int match_pathname(const char *pathname, int pathlen, */ if (!patternlen && !namelen) return 1; + /* + * This can happen when we ignore some exclude rules + * on directories in other to see if negative rules + * may match. E.g. + * + * /abc + * !/abc/def/ghi + * + * The pattern of interest is "/abc". On the first + * try, we should match path "abc" with this pattern + * in the "if" statement right above, but the caller + * ignores it. + * + * On the second try with paths within "abc", + * e.g. "abc/xyz", we come here and try to match it + * with "/abc". + */ + if (!patternlen && namelen && *name == '/') + return 1; } return fnmatch_icase_mem(pattern, patternlen, @@ -740,6 +759,48 @@ int match_pathname(const char *pathname, int pathlen, WM_PATHNAME) == 0; } +/* + * Return non-zero if pathname is a directory and an ancestor of the + * literal path in a (negative) pattern. This is used to keep + * descending in "foo" and "foo/bar" when the pattern is + * "!foo/bar/.gitignore". "foo/notbar" will not be descended however. + */ +static int match_neg_path(const char *pathname, int pathlen, int *dtype, + const char *base, int baselen, + const char *pattern, int prefix, int patternlen, + int flags) +{ + assert((flags & EXC_FLAG_NEGATIVE) && !(flags & EXC_FLAG_NODIR)); + + 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] == '/') && + !strncmp_icase(pathname, pattern, pathlen))) + return 1; + + return 0; +} + /* * 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 @@ -753,7 +814,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, matched_negative_path = 0; if (!el->nr) return NULL; /* undefined */ @@ -788,7 +849,18 @@ static struct exclude *last_exclude_matching_from_list(const char *pathname, exc = x; break; } + + if ((x->flags & EXC_FLAG_NEGATIVE) && !matched_negative_path && + match_neg_path(pathname, pathlen, dtype, x->base, + x->baselen ? x->baselen - 1 : 0, + exclude, prefix, x->patternlen, x->flags)) + matched_negative_path = 1; } + if (exc && + !(exc->flags & EXC_FLAG_NEGATIVE) && + !(exc->flags & EXC_FLAG_NODIR) && + matched_negative_path) + exc = NULL; return exc; } -- cgit v1.2.3