summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <ps@pks.im>2019-06-14 08:06:05 +0200
committerPatrick Steinhardt <ps@pks.im>2019-06-15 09:34:21 +0200
commit05f9986a3555e3c5768e949743ed6dcdbd88252c (patch)
tree15b9a76b1cceda8691e36c12b594cf56ee43fd00
parent5811e3ba490ed57dd73714ff4cf26cfa355c19f4 (diff)
downloadlibgit2-05f9986a3555e3c5768e949743ed6dcdbd88252c.tar.gz
attr_file: convert to use `wildmatch`
Upstream git has converted to use `wildmatch` instead of `fnmatch`. Convert our gitattributes logic to use `wildmatch` as the last user of `fnmatch`. Please, don't expect I know what I'm doing here: the fnmatch parser is one of the most fun things to play around with as it has a sh*tload of weird cases. In all honesty, I'm simply relying on our tests that are by now rather comprehensive in that area. The conversion actually fixes compatibility with how git.git parser "**" patterns when the given path does not contain any directory separators. Previously, a pattern "**.foo" erroneously wouldn't match a file "x.foo", while git.git would match. Remove the new-unused LEADINGDIR/NOLEADINGDIR flags for `git_attr_fnmatch`.
-rw-r--r--src/attr_file.c27
-rw-r--r--src/attr_file.h5
-rw-r--r--src/ignore.c4
-rw-r--r--src/pathspec.c3
-rw-r--r--tests/ignore/path.c22
5 files changed, 30 insertions, 31 deletions
diff --git a/src/attr_file.c b/src/attr_file.c
index a2959aefd..edcfbdad5 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -15,7 +15,7 @@
#include "git2/tree.h"
#include "blob.h"
#include "index.h"
-#include "fnmatch.h"
+#include "wildmatch.h"
#include <ctype.h>
static void attr_file_free(git_attr_file *file)
@@ -402,18 +402,13 @@ bool git_attr_fnmatch__match(
}
if (match->flags & GIT_ATTR_FNMATCH_ICASE)
- flags |= FNM_CASEFOLD;
- if (match->flags & GIT_ATTR_FNMATCH_LEADINGDIR)
- flags |= FNM_LEADING_DIR;
+ flags |= WM_CASEFOLD;
if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) {
filename = relpath;
- flags |= FNM_PATHNAME;
+ flags |= WM_PATHNAME;
} else {
filename = path->basename;
-
- if (path->is_dir)
- flags |= FNM_LEADING_DIR;
}
if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir) {
@@ -428,8 +423,6 @@ bool git_attr_fnmatch__match(
path->basename == relpath)
return false;
- flags |= FNM_LEADING_DIR;
-
/* fail match if this is a file with same name as ignored folder */
samename = (match->flags & GIT_ATTR_FNMATCH_ICASE) ?
!strcasecmp(match->pattern, relpath) :
@@ -438,10 +431,10 @@ bool git_attr_fnmatch__match(
if (samename)
return false;
- return (p_fnmatch(match->pattern, relpath, flags) != FNM_NOMATCH);
+ return (wildmatch(match->pattern, relpath, flags) == WM_MATCH);
}
- return (p_fnmatch(match->pattern, filename, flags) != FNM_NOMATCH);
+ return (wildmatch(match->pattern, filename, flags) == WM_MATCH);
}
bool git_attr_rule__match(
@@ -659,8 +652,6 @@ int git_attr_fnmatch__parse(
if (*pattern == '!' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWNEG) != 0) {
spec->flags = spec->flags | GIT_ATTR_FNMATCH_NEGATIVE;
- if ((spec->flags & GIT_ATTR_FNMATCH_NOLEADINGDIR) == 0)
- spec->flags |= GIT_ATTR_FNMATCH_LEADINGDIR;
pattern++;
}
@@ -716,14 +707,6 @@ int git_attr_fnmatch__parse(
if (--slash_count <= 0)
spec->flags = spec->flags & ~GIT_ATTR_FNMATCH_FULLPATH;
}
- if ((spec->flags & GIT_ATTR_FNMATCH_NOLEADINGDIR) == 0 &&
- spec->length >= 2 &&
- pattern[spec->length - 1] == '*' &&
- pattern[spec->length - 2] == '/') {
- spec->length -= 2;
- spec->flags = spec->flags | GIT_ATTR_FNMATCH_LEADINGDIR;
- /* leave FULLPATH match on, however */
- }
if (context) {
char *slash = strrchr(context, '/');
diff --git a/src/attr_file.h b/src/attr_file.h
index fedf55af5..7a45516fb 100644
--- a/src/attr_file.h
+++ b/src/attr_file.h
@@ -32,12 +32,9 @@
#define GIT_ATTR_FNMATCH_MATCH_ALL (1U << 8)
#define GIT_ATTR_FNMATCH_ALLOWNEG (1U << 9)
#define GIT_ATTR_FNMATCH_ALLOWMACRO (1U << 10)
-#define GIT_ATTR_FNMATCH_LEADINGDIR (1U << 11)
-#define GIT_ATTR_FNMATCH_NOLEADINGDIR (1U << 12)
#define GIT_ATTR_FNMATCH__INCOMING \
- (GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG | \
- GIT_ATTR_FNMATCH_ALLOWMACRO | GIT_ATTR_FNMATCH_NOLEADINGDIR)
+ (GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO)
typedef enum {
GIT_ATTR_FILE__IN_MEMORY = 0,
diff --git a/src/ignore.c b/src/ignore.c
index 1d256c995..3f748b80f 100644
--- a/src/ignore.c
+++ b/src/ignore.c
@@ -193,9 +193,7 @@ static int parse_ignore_file(
}
match->flags =
- GIT_ATTR_FNMATCH_ALLOWSPACE |
- GIT_ATTR_FNMATCH_ALLOWNEG |
- GIT_ATTR_FNMATCH_NOLEADINGDIR;
+ GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
if (!(error = git_attr_fnmatch__parse(
match, &attrs->pool, context, &scan)))
diff --git a/src/pathspec.c b/src/pathspec.c
index 0cd3fc4cc..9eb952308 100644
--- a/src/pathspec.c
+++ b/src/pathspec.c
@@ -85,8 +85,7 @@ int git_pathspec__vinit(
if (!match)
return -1;
- match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE |
- GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_NOLEADINGDIR;
+ match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern);
if (ret == GIT_ENOTFOUND) {
diff --git a/tests/ignore/path.c b/tests/ignore/path.c
index bfed297c2..95269959c 100644
--- a/tests/ignore/path.c
+++ b/tests/ignore/path.c
@@ -230,6 +230,28 @@ void test_ignore_path__globs_and_path_delimiters(void)
assert_is_ignored(false, "_test/foo/bar/code/file");
}
+void test_ignore_path__globs_without_star(void)
+{
+ cl_git_rewritefile(
+ "attr/.gitignore",
+ "*.foo\n"
+ "**.bar\n"
+ );
+
+ assert_is_ignored(true, ".foo");
+ assert_is_ignored(true, "xyz.foo");
+ assert_is_ignored(true, ".bar");
+ assert_is_ignored(true, "x.bar");
+ assert_is_ignored(true, "xyz.bar");
+
+ assert_is_ignored(true, "test/.foo");
+ assert_is_ignored(true, "test/x.foo");
+ assert_is_ignored(true, "test/xyz.foo");
+ assert_is_ignored(true, "test/.bar");
+ assert_is_ignored(true, "test/x.bar");
+ assert_is_ignored(true, "test/xyz.bar");
+}
+
void test_ignore_path__skip_gitignore_directory(void)
{
cl_git_rewritefile("attr/.git/info/exclude", "/NewFolder\n/NewFolder/NewFolder");