Commit 05f9986a by Patrick Steinhardt

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`.
parent 5811e3ba
......@@ -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, '/');
......
......@@ -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,
......
......@@ -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)))
......
......@@ -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) {
......
......@@ -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");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment