diff --git a/src/fnmatch.c b/src/fnmatch.c index a2945b8db..33c8a2512 100644 --- a/src/fnmatch.c +++ b/src/fnmatch.c @@ -93,11 +93,24 @@ p_fnmatchx(const char *pattern, const char *string, int flags, size_t recurs) * It will be restored if/when we recurse below. */ if (c == '*') { - flags &= ~FNM_PATHNAME; - while (c == '*') - c = *++pattern; - if (c == '/') - c = *++pattern; + c = *++pattern; + /* star-star-slash is at the end, match by default */ + if (c == EOS) + return 0; + /* Double-star must be at end or between slashes */ + if (c != '/') + return (FNM_NOMATCH); + + c = *++pattern; + do { + int e = p_fnmatchx(pattern, string, recurs_flags, recurs); + if (e != FNM_NOMATCH) + return e; + string = strchr(string, '/'); + } while (string++); + + /* If we get here, we didn't find a match */ + return FNM_NOMATCH; } if (*string == '.' && (flags & FNM_PERIOD) && diff --git a/tests/attr/ignore.c b/tests/attr/ignore.c index 91bf984a1..f1fe1c71f 100644 --- a/tests/attr/ignore.c +++ b/tests/attr/ignore.c @@ -132,6 +132,32 @@ void test_attr_ignore__leading_stars(void) assert_is_ignored(false, "dir1/kid2/file"); } +void test_attr_ignore__globs_and_path_delimiters(void) +{ + cl_git_rewritefile("attr/.gitignore", "foo/bar/**"); + assert_is_ignored(true, "foo/bar/baz"); + assert_is_ignored(true, "foo/bar/baz/quux"); + + cl_git_rewritefile("attr/.gitignore", "_*/"); + assert_is_ignored(true, "sub/_test/a/file"); + assert_is_ignored(false, "test_folder/file"); + assert_is_ignored(true, "_test/file"); + assert_is_ignored(true, "_test/a/file"); + + cl_git_rewritefile("attr/.gitignore", "**/_*/"); + assert_is_ignored(true, "sub/_test/a/file"); + assert_is_ignored(false, "test_folder/file"); + assert_is_ignored(true, "_test/file"); + assert_is_ignored(true, "_test/a/file"); + + cl_git_rewritefile("attr/.gitignore", "**/_*/foo/bar/*ux"); + + assert_is_ignored(true, "sub/_test/foo/bar/qux/file"); + assert_is_ignored(true, "_test/foo/bar/qux/file"); + assert_is_ignored(true, "_test/foo/bar/crux/file"); + assert_is_ignored(false, "_test/foo/bar/code/file"); +} + void test_attr_ignore__skip_gitignore_directory(void) { cl_git_rewritefile("attr/.git/info/exclude", "/NewFolder\n/NewFolder/NewFolder");