mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-08 06:15:02 +00:00
ignore: fix negative ignores without wildcards.
This commit is contained in:
parent
2a0f67f04c
commit
4f3586034b
50
src/ignore.c
50
src/ignore.c
@ -10,6 +10,41 @@
|
||||
|
||||
#define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n"
|
||||
|
||||
/**
|
||||
* A negative ignore pattern can match a positive one without
|
||||
* wildcards if its pattern equals the tail of the positive
|
||||
* pattern. Thus
|
||||
*
|
||||
* foo/bar
|
||||
* !bar
|
||||
*
|
||||
* would result in foo/bar being unignored again.
|
||||
*/
|
||||
static int does_negate_pattern(git_attr_fnmatch *rule, git_attr_fnmatch *neg)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if ((rule->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0
|
||||
&& (neg->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0) {
|
||||
/*
|
||||
* no chance of matching if rule is shorter than
|
||||
* the negated one
|
||||
*/
|
||||
if (rule->length < neg->length)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* shift pattern so its tail aligns with the
|
||||
* negated pattern
|
||||
*/
|
||||
p = rule->pattern + rule->length - neg->length;
|
||||
if (strcmp(p, neg->pattern) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A negative ignore can only unignore a file which is given explicitly before, thus
|
||||
*
|
||||
@ -31,6 +66,8 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
|
||||
char *path;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
|
||||
*out = 0;
|
||||
|
||||
/* path of the file relative to the workdir, so we match the rules in subdirs */
|
||||
if (match->containing_dir) {
|
||||
git_buf_puts(&buf, match->containing_dir);
|
||||
@ -41,9 +78,14 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
|
||||
path = git_buf_detach(&buf);
|
||||
|
||||
git_vector_foreach(rules, i, rule) {
|
||||
/* no chance of matching w/o a wilcard */
|
||||
if (!(rule->flags & GIT_ATTR_FNMATCH_HASWILD))
|
||||
continue;
|
||||
if (!(rule->flags & GIT_ATTR_FNMATCH_HASWILD)) {
|
||||
if (does_negate_pattern(rule, match)) {
|
||||
*out = 1;
|
||||
goto out;
|
||||
}
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're dealing with a directory (which we know via the
|
||||
@ -62,7 +104,6 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
|
||||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
|
||||
if ((error = p_fnmatch(git_buf_cstr(&buf), path, FNM_PATHNAME)) < 0) {
|
||||
giterr_set(GITERR_INVALID, "error matching pattern");
|
||||
goto out;
|
||||
@ -76,7 +117,6 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
|
||||
}
|
||||
}
|
||||
|
||||
*out = 0;
|
||||
error = 0;
|
||||
|
||||
out:
|
||||
|
@ -892,6 +892,59 @@ void test_status_ignore__negative_ignores_without_trailing_slash_inside_ignores(
|
||||
cl_assert(found_parent_child2_file);
|
||||
}
|
||||
|
||||
void test_status_ignore__negative_directory_ignores(void)
|
||||
{
|
||||
static const char *test_files[] = {
|
||||
"empty_standard_repo/parent/child1/bar.txt",
|
||||
"empty_standard_repo/parent/child2/bar.txt",
|
||||
"empty_standard_repo/parent/child3/foo.txt",
|
||||
"empty_standard_repo/parent/child4/bar.txt",
|
||||
"empty_standard_repo/parent/nested/child5/bar.txt",
|
||||
"empty_standard_repo/parent/nested/child6/bar.txt",
|
||||
"empty_standard_repo/parent/nested/child7/bar.txt",
|
||||
"empty_standard_repo/padded_parent/child8/bar.txt",
|
||||
NULL
|
||||
};
|
||||
|
||||
make_test_data("empty_standard_repo", test_files);
|
||||
cl_git_mkfile(
|
||||
"empty_standard_repo/.gitignore",
|
||||
"foo.txt\n"
|
||||
"parent/child1\n"
|
||||
"parent/child2\n"
|
||||
"parent/child4\n"
|
||||
"parent/nested/child5\n"
|
||||
"nested/child6\n"
|
||||
"nested/child7\n"
|
||||
"padded_parent/child8\n"
|
||||
/* test simple exact match */
|
||||
"!parent/child1\n"
|
||||
/* test negating file without negating dir */
|
||||
"!parent/child2/bar.txt\n"
|
||||
/* test negative pattern on dir with its content
|
||||
* being ignored */
|
||||
"!parent/child3\n"
|
||||
/* test with partial match at end */
|
||||
"!child4\n"
|
||||
/* test with partial match with '/' at end */
|
||||
"!nested/child5\n"
|
||||
/* test with complete match */
|
||||
"!nested/child6\n"
|
||||
/* test with trailing '/' */
|
||||
"!child7/\n"
|
||||
/* test with partial dir match */
|
||||
"!_parent/child8\n");
|
||||
|
||||
refute_is_ignored("parent/child1/bar.txt");
|
||||
assert_is_ignored("parent/child2/bar.txt");
|
||||
assert_is_ignored("parent/child3/foo.txt");
|
||||
refute_is_ignored("parent/child4/bar.txt");
|
||||
assert_is_ignored("parent/nested/child5/bar.txt");
|
||||
refute_is_ignored("parent/nested/child6/bar.txt");
|
||||
refute_is_ignored("parent/nested/child7/bar.txt");
|
||||
assert_is_ignored("padded_parent/child8/bar.txt");
|
||||
}
|
||||
|
||||
void test_status_ignore__filename_with_cr(void)
|
||||
{
|
||||
int ignored;
|
||||
|
Loading…
Reference in New Issue
Block a user