Fix negative gitignore rules with leading directories #4670
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
When computing whether a file is ignored, we simply search for the first
matching rule and return whether it is a positive ignore rule (the file
is really ignored) or whether it is a negative ignore rule (the file is
being unignored). Each rule has a set of flags which are being passed to
fnmatch
, depending on what kind of rule it is. E.g. in case it is anegative ignore we add a flag
GIT_ATTR_FNMATCH_NEGATIVE
, in case itcontains a glob we set the
GIT_ATTR_FNMATCH_HASGLOB
flag.One of these flags is the
GIT_ATTR_FNMATCH_LEADINGDIR
flag, which isalways set in case the pattern has a trailing "/*" or in case the
pattern is negative. The flag causes the
fnmatch
function to return amatch in case a string is a leading directory of another, e.g. "dir/"
matches "dir/foo/bar.c". In case of negative patterns, this is wrong in
certain cases.
Take the following simple example of a gitignore:
The
LEADINGDIR
flag causes "!dir/" to match "dir/foo/bar.c", and wecorrectly unignore the directory. But take this example:
We expect everything in "dir/" to be unignored, but e.g. a file in a
subdirectory of dir should be ignored, as the "" does not cross
directory hierarchies. With
LEADINGDIR
, though, we would just see that"dir/" matches and return that the file is unignored, even if it is
contained in a subdirectory. Instead, we want to ignore leading
directories here and check ".test". Afterwards, we have to iterate up
to the parent directory and do the same checks.
To fix the issue, disallow matching against leading directories in
gitignore files. This can be trivially done by just adding the
GIT_ATTR_FNMATCH_NOLEADINGDIR
to the spec passed togit_attr_fnmatch__parse
. Due to a bug in that function, though, thisflag is being ignored for negative patterns, which is fixed in this
commit, as well. As a last fix, we need to ignore rules that are
supposed to match a directory when our path itself is a file.
All together, these changes fix the described error case.
After two days of despair and constantly pulling my hair out I finally got a fix for both #4623 and #4624.