The story goes back to 94bc671 (Add directory pattern matching to attributes - 2012-12-08). Before this commit, directories are passed to path_matches without the trailing slash. This is fine for matching pattern "subdir" with "foo/subdir". Patterns like "subdir/" (i.e. match _directory_ subdir) won't work though. So paths are now passed to path_matches with the trailing slash (i.e. "subdir/"). The trailing slash is used as the directory indicator (similar to dtype in exclude case). This makes pattern "subdir/" match directory "subdir/". Pattern "subdir" no longer match subdir, which is now "subdir/". As the trailing slash in pathname is the directory indicator, we do not need to keep it in the pathname for matching. The trailing slash should be turned to dtype "DT_DIR" and stripped out of pathname. This keeps the code pattern similar to exclude. The same applies for the pattern "subdir/". The trailing slash is converted to flag EXC_FLAG_MUSTBEDIR and should not remain in the pattern, as noted in parse_exclude_pattern(). prepare_attr_stack() breaks this and keeps the trailing slash anyway. To sum up, both patterns and pathnames should never have the trailing slash when it comes to match_basename. Reported-and-analyzed-by: Jeff King <peff@xxxxxxxx> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx> --- attr.c | 11 +++++++++-- t/t5002-archive-attr-pattern.sh | 6 ++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/attr.c b/attr.c index 1818ba5..a95c837 100644 --- a/attr.c +++ b/attr.c @@ -256,7 +256,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src, &res->u.pat.flags, &res->u.pat.nowildcardlen); if (res->u.pat.flags & EXC_FLAG_MUSTBEDIR) - res->u.pat.patternlen++; + p[res->u.pat.patternlen] = '\0'; if (res->u.pat.flags & EXC_FLAG_NEGATIVE) { warning(_("Negative patterns are ignored in git attributes\n" "Use '\\!' for literal leading exclamation.")); @@ -665,9 +665,16 @@ static int path_matches(const char *pathname, int pathlen, { const char *pattern = pat->pattern; int prefix = pat->nowildcardlen; + int dtype; + + if (pathlen && pathname[pathlen-1] == '/') { + dtype = DT_DIR; + pathlen--; + } else + dtype = DT_REG; if ((pat->flags & EXC_FLAG_MUSTBEDIR) && - ((!pathlen) || (pathname[pathlen-1] != '/'))) + dtype != DT_DIR) return 0; if (pat->flags & EXC_FLAG_NODIR) { diff --git a/t/t5002-archive-attr-pattern.sh b/t/t5002-archive-attr-pattern.sh index 0c847fb..98ccc3c 100755 --- a/t/t5002-archive-attr-pattern.sh +++ b/t/t5002-archive-attr-pattern.sh @@ -27,6 +27,10 @@ test_expect_success 'setup' ' echo ignored-only-if-dir/ export-ignore >>.git/info/attributes && git add ignored-only-if-dir && + mkdir -p ignored-without-slash && + echo ignored without slash >ignored-without-slash/foo && + git add ignored-without-slash/foo && + echo ignored-without-slash export-ignore >>.git/info/attributes && mkdir -p one-level-lower/two-levels-lower/ignored-only-if-dir && echo ignored by ignored dir >one-level-lower/two-levels-lower/ignored-only-if-dir/ignored-by-ignored-dir && @@ -49,6 +53,8 @@ test_expect_exists archive/not-ignored-dir/ignored-only-if-dir test_expect_exists archive/not-ignored-dir/ test_expect_missing archive/ignored-only-if-dir/ test_expect_missing archive/ignored-ony-if-dir/ignored-by-ignored-dir +test_expect_missing archive/ignored-without-slash/ && +test_expect_missing archive/ignored-without-slash/foo && test_expect_exists archive/one-level-lower/ test_expect_missing archive/one-level-lower/two-levels-lower/ignored-only-if-dir/ test_expect_missing archive/one-level-lower/two-levels-lower/ignored-ony-if-dir/ignored-by-ignored-dir -- 1.8.2.82.gc24b958 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html