Commit 4c02d393 by Edward Thomson

Merge pull request #3016 from pks-t/ignore-exclude-fix

ignore: fix negative ignores without wildcards.
parents a0e652d2 4f358603
...@@ -11,6 +11,41 @@ ...@@ -11,6 +11,41 @@
#define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n" #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 * A negative ignore can only unignore a file which is given explicitly before, thus
* *
* foo * foo
...@@ -31,6 +66,8 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match ...@@ -31,6 +66,8 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
char *path; char *path;
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
*out = 0;
/* path of the file relative to the workdir, so we match the rules in subdirs */ /* path of the file relative to the workdir, so we match the rules in subdirs */
if (match->containing_dir) { if (match->containing_dir) {
git_buf_puts(&buf, 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 ...@@ -41,9 +78,14 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
path = git_buf_detach(&buf); path = git_buf_detach(&buf);
git_vector_foreach(rules, i, rule) { git_vector_foreach(rules, i, rule) {
/* no chance of matching w/o a wilcard */ if (!(rule->flags & GIT_ATTR_FNMATCH_HASWILD)) {
if (!(rule->flags & GIT_ATTR_FNMATCH_HASWILD)) if (does_negate_pattern(rule, match)) {
*out = 1;
goto out;
}
else
continue; continue;
}
/* /*
* If we're dealing with a directory (which we know via the * 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 ...@@ -62,7 +104,6 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
if (error < 0) if (error < 0)
goto out; goto out;
if ((error = p_fnmatch(git_buf_cstr(&buf), path, FNM_PATHNAME)) < 0) { if ((error = p_fnmatch(git_buf_cstr(&buf), path, FNM_PATHNAME)) < 0) {
giterr_set(GITERR_INVALID, "error matching pattern"); giterr_set(GITERR_INVALID, "error matching pattern");
goto out; goto out;
...@@ -76,7 +117,6 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match ...@@ -76,7 +117,6 @@ static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match
} }
} }
*out = 0;
error = 0; error = 0;
out: out:
......
...@@ -892,6 +892,59 @@ void test_status_ignore__negative_ignores_without_trailing_slash_inside_ignores( ...@@ -892,6 +892,59 @@ void test_status_ignore__negative_ignores_without_trailing_slash_inside_ignores(
cl_assert(found_parent_child2_file); 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) void test_status_ignore__filename_with_cr(void)
{ {
int ignored; int ignored;
......
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