Merge pull request #3745 from libgit2/cmn/ignore-starstar Improve star-star matching
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
diff --git a/src/fnmatch.c b/src/fnmatch.c
index a2945b8..33c8a25 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 91bf984..f1fe1c7 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");