Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

pks-t
Copy link
Member

@pks-t pks-t commented Jun 5, 2018

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 a
negative ignore we add a flag GIT_ATTR_FNMATCH_NEGATIVE, in case it
contains a glob we set the GIT_ATTR_FNMATCH_HASGLOB flag.

One of these flags is the GIT_ATTR_FNMATCH_LEADINGDIR flag, which is
always set in case the pattern has a trailing "/*" or in case the
pattern is negative. The flag causes the fnmatch function to return a
match 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:

dir/
!dir/

The LEADINGDIR flag causes "!dir/" to match "dir/foo/bar.c", and we
correctly unignore the directory. But take this example:

*.test
!dir/*

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 to
git_attr_fnmatch__parse. Due to a bug in that function, though, this
flag 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.

@pks-t
Copy link
Member Author

pks-t commented Jun 5, 2018

As usual, I recommend reading this PR in commit-order together with their respective explanations in the commit messages.

pks-t added 3 commits June 6, 2018 08:33
…files

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 a
negative ignore we add a flag `GIT_ATTR_FNMATCH_NEGATIVE`, in case it
contains a glob we set the `GIT_ATTR_FNMATCH_HASGLOB` flag.

One of these flags is the `GIT_ATTR_FNMATCH_LEADINGDIR` flag, which is
always set in case the pattern has a trailing "/*" or in case the
pattern is negative. The flag causes the `fnmatch` function to return a
match 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:

    dir/
    !dir/

The `LEADINGDIR` flag causes "!dir/" to match "dir/foo/bar.c", and we
correctly unignore the directory. But take this example:

    *.test
    !dir/*

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 to
`git_attr_fnmatch__parse`. Due to a bug in that function, though, this
flag 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.
When checking whether a rule negates another rule, we were checking
whether a rule had the `GIT_ATTR_FNMATCH_LEADINGDIR` flag set and, if
so, added a "/*" to its end before passing it to `fnmatch`. Our code now
sets `GIT_ATTR_FNMATCH_NOLEADINGDIR`, thus the `LEADINGDIR` flag shall
never be set. Furthermore, due to the `NOLEADINGDIR` flag, trailing
globs do not get consumed by our ignore parser anymore.

Clean up code by just dropping this now useless logic.
@pks-t pks-t force-pushed the pks/ignore-leadingdir branch from 3e88a69 to d22fd81 Compare June 6, 2018 06:33
@ethomson
Copy link
Member

ethomson commented Jun 9, 2018

I'm glad you tackled this problem, for I could not have solved it as I no longer have hair to pull out in frustration. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants