Skip to content

Commit b8922fc

Browse files
committed
ignore: keep negative rules containing wildcards
Ignore rules allow for reverting a previously ignored rule by prefixing it with an exclamation mark. As such, a negative rule can only override previously ignored files. While computing all ignore patterns, we try to use this fact to optimize away some negative rules which do not override any previous patterns, as they won't change the outcome anyway. In some cases, though, this optimization causes us to get the actual ignores wrong for some files. This may happen whenever the pattern contains a wildcard, as we are unable to reason about whether a pattern overrides a previous pattern in a sane way. This happens for example in the case where a gitignore file contains "*.c" and "!src/*.c", where we wouldn't un-ignore files inside of the "src/" subdirectory. In this case, the first solution coming to mind may be to just strip the "src/" prefix and simply compare the basenames. While that would work here, it would stop working as soon as the basename pattern itself is different, like for example with "*x.c" and "!src/*.c. As such, we settle for the easier fix of just not optimizing away rules that contain a wildcard.
1 parent 4467543 commit b8922fc

File tree

2 files changed

+17
-2
lines changed

2 files changed

+17
-2
lines changed

src/ignore.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,14 @@ static int parse_ignore_file(
205205

206206
scan = git__next_line(scan);
207207

208-
/* if a negative match doesn't actually do anything, throw it away */
209-
if (match->flags & GIT_ATTR_FNMATCH_NEGATIVE)
208+
/*
209+
* If a negative match doesn't actually do anything,
210+
* throw it away. As we cannot always verify whether a
211+
* rule containing wildcards negates another rule, we
212+
* do not optimize away these rules, though.
213+
* */
214+
if (match->flags & GIT_ATTR_FNMATCH_NEGATIVE
215+
&& !(match->flags & GIT_ATTR_FNMATCH_HASWILD))
210216
error = does_negate_rule(&valid_rule, &attrs->rules, match);
211217

212218
if (!error && valid_rule)

tests/attr/ignore.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,3 +303,12 @@ void test_attr_ignore__test(void)
303303
assert_is_ignored(true, "dist/foo.o");
304304
assert_is_ignored(true, "bin/foo");
305305
}
306+
307+
void test_attr_ignore__unignore_dir_succeeds(void)
308+
{
309+
cl_git_rewritefile("attr/.gitignore",
310+
"*.c\n"
311+
"!src/*.c\n");
312+
assert_is_ignored(false, "src/foo.c");
313+
assert_is_ignored(true, "src/foo/foo.c");
314+
}

0 commit comments

Comments
 (0)