Skip to content

Commit 1940a02

Browse files
peffgitster
authored andcommitted
match_pathname(): give fnmatch one char of prefix context
In match_pathname(), which we use for matching .gitignore and .gitattribute patterns, we are comparing paths with fnmatch patterns (actually our extended wildmatch, which will be important). There's an extra optimization there: we pre-compute the number of non-wildcard characters at the beginning of the pattern and do an fspathncmp() on that prefix. That lets us avoid fnmatch entirely on patterns without wildcards, and shrinks the amount of work we hand off to fnmatch. For a pattern like "foo*.txt" and a path "foobar.txt", we'd cut away the matching "foo" prefix and just pass "*.txt" and "bar.txt" to fnmatch(). But this misses a subtle corner case. In fnmatch(), we'll think "bar.txt" is the start of the path, but it's not. This doesn't matter for the pattern above, but consider the wildmatch pattern "foo**/bar" and the path "foobar". These two should not match, because there is no file named "bar", and the "**" applies only to the containing directory name. But after removing the "foo" prefix, fnmatch will get "**/bar" and "bar", which it does consider a match, because "**/" can match zero directories. We can solve this by giving fnmatch a bit more context. As long as it has one byte of the matched prefix, then it will know that "bar" is not the start of the path. In this example it would get "o**/bar" and "obar", and realize that they cannot match. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 9d6c580 commit 1940a02

File tree

2 files changed

+17
-0
lines changed

2 files changed

+17
-0
lines changed

dir.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1369,6 +1369,12 @@ int match_pathname(const char *pathname, int pathlen,
13691369
if (patternlen == prefix && namelen == prefix)
13701370
return 1;
13711371

1372+
/*
1373+
* Retain one character of the prefix to
1374+
* pass to fnmatch, which lets it distinguish
1375+
* the start of a directory component correctly.
1376+
*/
1377+
prefix--;
13721378
pattern += prefix;
13731379
patternlen -= prefix;
13741380
name += prefix;

t/t0008-ignores.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,17 @@ test_expect_success 'directories and ** matches' '
847847
test_cmp expect actual
848848
'
849849

850+
test_expect_success '** not confused by matching leading prefix' '
851+
cat >.gitignore <<-\EOF &&
852+
foo**/bar
853+
EOF
854+
git check-ignore foobar foo/bar >actual &&
855+
cat >expect <<-\EOF &&
856+
foo/bar
857+
EOF
858+
test_cmp expect actual
859+
'
860+
850861
############################################################################
851862
#
852863
# test whitespace handling

0 commit comments

Comments
 (0)