According to the gitignore documentation, an asterisk *
matches anything except a slash. It’s not clear to me how that actually works in practice.
Consider the following structure for example:
foo/
foo/test_dir1/test_file1
foo/test_dir1/test_dir2/test_file2
foo/test_dir1/test_dir2/test_dir3/test_file3
All three files are ignored with the gitignore directive: /foo/test_dir1/*
So clearly the two slashes in test_dir1/test_dir2/test_dir3
are ignored.
It makes sense if you consider that /foo/test_dir1/*
matches with /foo/test_dir1/test_dir2
, and therefore anything in this directory will be ignored. However I am struggling to understand in what case a slash would indeed be ignored and what would be the practicality of that.
I would have expected test_file2
and test_file3
to not be ignored with the example above.
Bertrand Bastien is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
14
TL;DR: in order to have insight on why a file or directory is ignored in your repo, run:
git check-ignore -v path/to/file_or_directory
Your main issue seems to reconcile the pattern matching rules described by the documentation with the fact that some content gets ignored, even though the documentation states that they don’t match a pattern.
Here is the gotcha:
- the documenation describes how an individual file or directory can be matched,
- however, it does not describe very precisely what are the implications of being a match
One very important effect of being matched:
- if, after applying all patterns (both inclusive and exclusive patterns), a directory is matched, then all of its content will be ignored —
git
will not even stat the directory’s content for new files
Going back to the example you quote (link, and by re-reading it I find it is misleading):
- The pattern
foo/*
, matchesfoo/test.json
(a regular file),foo/bar
(a directory), but it does not matchfoo/bar/hello.c
(a regular file), as the asterisk in the pattern does not matchbar/hello.c
which has a slash in it.
the misleading part: it doesn’t say that, unless other patterns are added to explicitly include foo/bar
, since foo/bar
is a match, all of foo/bar/
content will be ignored anyways.
Let’s look at another illustration of the difference between “being matched” and “being ignored”:
# .gitignore:
foo/
!foo/bar
You could expect that the above gitignore file would allow you to ignore all of foo/
‘s content, except for bar
.
If you try it however, you will see that bar
is actually ignored:
$ cat .gitignore
foo/
!foo/bar
$ git check-ignore -v foo/bar
.gitignore:1:foo/ foo/bar
Since pattern foo/
matches directory foo/
, git
will not even run ls
inside foo/
to check if some files should be included after all.
Here is a variation that allows to have “ignore everything in foo/
except bar
“:
$ cat .gitignore
foo/*
!foo/bar
$ git check-ignore -v foo/bar
.gitignore:2:!foo/bar foo/bar
If you read carefully how files or directories are matched, you will see that foo/
is not a match for foo/*
, that everything inside foo/
is a match, but this time, the !foo/bar
pattern will also trigger.
The documentation actually explains that (or tries to ?) with its very last example (same link, scroll to bottom):
Example to exclude everything except a specific directory foo/bar (note the /* – without the slash, the wildcard would also exclude everything within foo/bar):
$ cat .gitignore # exclude everything except directory foo/bar /* !/foo /foo/* !/foo/bar
1
It applies pattern matching based on the string you are providing. So if you look for foo/*/bar
you will find any folders in a structure that start with foo, and end with bar. The slashes are just part of the pattern it looks for and is ignored when it comes as a symbol on it’s own. The documentation refers to that.
1