summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@github.com>2016-04-19 15:24:14 -0400
committerEdward Thomson <ethomson@github.com>2016-04-19 15:24:14 -0400
commit95fbc81dafd64400d51637a27ecd49de5ea63145 (patch)
treec2c66e18fbfef1fd323906138516e96f99ba5a73
parent029c93464f3124556286ccc46164c1d4181edfcc (diff)
parentd45928cc0d969f255337e11edd59b4da6dc4926d (diff)
downloadlibgit2-95fbc81dafd64400d51637a27ecd49de5ea63145.tar.gz
Merge pull request #3745 from libgit2/cmn/ignore-starstar
Improve star-star matching
-rw-r--r--src/fnmatch.c23
-rw-r--r--tests/attr/ignore.c26
2 files changed, 44 insertions, 5 deletions
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");