There are many questions on this topic, but none involve ~/.gitignore
.
I have a strange project with a top-level project directory—call it project/
—full of garbage, but with at least one important subdirectory—say project/feedme/
. I'd like to .gitignore
everything in project/
, but un.gitignore
project/feedme/
and its contents. This is easy with a negated pattern:
project$ cat .gitignore
/*
!/feedme/
I have a file project/feedme/.exrc
which I would like to ignore. Being a vi
user, I use .exrc
files for local editor settings often, and so I have an entry in my ~/.gitconfig
(specified by the core.excludesFile
Git setting):
project$ cat ~/.gitignore
.exrc
Unfortunately git
seems not to want to reignore .exrc
:
project$ git status -s
?? feedme/
project$ git add feedme/
project$ git status -s
A feedme/.exrc
Why does Git not ignore project/feedme/.exrc
?
Edit: This section is wrong, as per prosoitos' answer. See my edits at the end of this question, as it has not been completely answered.
Why do I think this should work? The manual page gitignore(5) says so:
Each line in a gitignore file specifies a pattern. When deciding whether to ignore a path, Git normally checks gitignore patterns from multiple sources, with the following order of precedence, from highest to lowest (within one level of precedence, the last matching pattern decides the outcome):
[…]
Patterns read from a .gitignore file in the same directory as the path, or in any parent directory, with patterns in the higher level files (up to the toplevel of the work tree) being overridden by those in lower level files down to the directory containing the
file. […][…]
Patterns read from the file specified by the configuration variable core.excludesFile.
Since patterns from ~/.gitignore
are read last, I would have thought that the .exrc
would be ignored in the end regardless of local .gitgnore
s.
A possible explanation is that this (again from the gitignore(5) manual page, emphasis mine)
It is not possible to re-include a file if a parent directory of that file is excluded.
also applies in its dual form (not in the manual page)
It is not possible to exclude a file if a parent directory of that file is re-included.
I would consider this a bug however if this is the case; at least it should be stated clearly in the documentation.
P.S.: My ~/.gitignore
works under normal circumstances, so the problem is not there.
Edits
Prosoitos is right, the global ~/.gitignore
is read first, so the project/feedme/
subdirectory is re-included after .exrc
s are globally ignored. However I still face another oddity.
As I understand the documentation now, putting .exrc
on the last line of my ~/.gitignore
is equivalent (if project/.git/info/excludes
is empty) to putting .exrc
on the first line of project/.gitignore
. Yet if I do this, project/feedme/.exrc
is ignored this time!
project$ cat ~/.gitignore
project$ cat .gitignore
.exrc
/*
!/feedme/
project$ git ls-files --other --ignored --exclude-standard
.gitignore
feedme/.exrc
What is happening here?
No. This is where you got it wrong. The section of the Git manual that you quote:
means that the global
.gitignore
gets overridden by your local one, not the other way around.So
!/feedme/
in your local.gitignore
overrides.exrc
in your global one. Consequently,feedme/.exrc
is not excluded.An easy solution would be to add
.exrc
in your local.gitignore
file and to make sure that it is below the line!/feedme/
since:Note:
Admittedly, the expression "order of precedence" can be confusing.
Here, "higher precedence" means that it is "more important than" (so it overrides).
You seem to have understood it in the sense of operator precedence, where "higher precedence" would mean "executed before", which would lead to the opposite result. Hence the confusion.
Edit after your edit to the question:
Now that you have made corrections to your situation, I think that you are correct.
I reproduced your situation, then ran:
and I get:
showing that the exclusion of
.exrc
did not get negated by!/feedme/
.I tried to google this and I am also unable to find any documentation on it.