From 97660b2cfedeee452db8e852d6d476e333ac971d Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Tue, 4 Nov 2025 14:11:20 -0800 Subject: [PATCH] dir.c: do not be fooled by :(exclude) pathspec elements When exclude_matches_pathspec() tries to determine if an otherwise excluded item matches the pathspec given, it goes through each pathspec element and declares a hit, without checking if the element is a negative ":(exclude)" element. Fix it be applying the usual "a path matches if it matches any one of positive pathspec element, and if it matches none of negative pathspec elements" rule in the function. Signed-off-by: Junio C Hamano --- dir.c | 14 ++++++++++++-- t/t2204-add-ignored.sh | 17 +++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/dir.c b/dir.c index 5b2181e589..5b1258a09d 100644 --- a/dir.c +++ b/dir.c @@ -2219,6 +2219,8 @@ static int exclude_matches_pathspec(const char *path, int pathlen, const struct pathspec *pathspec) { int i; + int matches_exclude_magic = 0; + int matches_pathspec_elem = 0; if (!pathspec || !pathspec->nr) return 0; @@ -2235,15 +2237,23 @@ static int exclude_matches_pathspec(const char *path, int pathlen, for (i = 0; i < pathspec->nr; i++) { const struct pathspec_item *item = &pathspec->items[i]; int len = item->nowildcard_len; + int *matches; + + if (item->magic & PATHSPEC_EXCLUDE) + matches = &matches_exclude_magic; + else + matches = &matches_pathspec_elem; if (len == pathlen && !ps_strncmp(item, item->match, path, pathlen)) - return 1; + *matches = 1; if (len > pathlen && item->match[pathlen] == '/' && !ps_strncmp(item, item->match, path, pathlen)) - return 1; + *matches = 1; } + if (matches_pathspec_elem && !matches_exclude_magic) + return 1; return 0; } diff --git a/t/t2204-add-ignored.sh b/t/t2204-add-ignored.sh index 31eb233df5..aa55b219ab 100755 --- a/t/t2204-add-ignored.sh +++ b/t/t2204-add-ignored.sh @@ -89,4 +89,21 @@ do ' done +test_expect_success "exclude magic would not interfere with .gitignore" ' + test_write_lines dir file sub ign err out "*.o" >.gitignore && + >foo.o && + >foo.c && + test_must_fail git add foo.o 2>err && + test_grep "are ignored by one" err && + test_grep "hint: Use -f" err && + + git add ":(exclude)foo.o" && + git ls-files >actual && + cat >expect <<-\EOF && + .gitignore + foo.c + EOF + test_cmp expect actual +' + test_done -- 2.47.3