Reputation: 634
I have a lot of files in directory which i wouldn't like them to be tracked by git. So i want to ignore all files except .bashrc
, .gitignore
and .config/terminator/config
for example.
I'm trying to do something like this in .gitignore:
*
!.gitignore
!.bashrc
!.config/terminator/config
.bashrc
and .gitignore
files are unignored now but still cannot get .config/terminator/config
unignored. How can i do this in git?
I have one possible solution but is there more elegant one without specifying subdirectories?
*
!.gitignore
!.bashrc
!.gitconfig
!.config/
!.config/terminator/
!.config/terminator/config
Upvotes: 1
Views: 594
Reputation: 489638
The short version of the answer is that once Git has ignored some directory, it proceeds not to bother to look inside that directory unless something else already forces it to do so.1 To stop this, you must explicitly list the directory or directories that you don't want ignored, with !
entries:
*
!/.bashrc
!/.config
!.config/terminator
!.config/terminator/config
!/.gitignore
That is:
*
tells Git: ignore everything unless overridden. Then the eventual:
!.bashrc
overrides the ignore rule for .bashrc
, and so on. But what Git does is that it starts by reading .
to find all entries. Suppose it finds them in alphabetical order: .bashrc
, .config
, .editorthing
, .gitignore
, .history
, and so on; and suppose we have your original .gitignore
file:
.bashrc
, let's check on it... ah, line 1 says ignore but 3 says don't, so don't ignore. Result: not ignored..config
, let's check on that. Line 1 says ignore, line 2 is about .gitignore
, line 3 is about .bashrc
, and line 4 says to keep .config/terminator/config
. It's not any of those, so line 1 applies! Result: ignored!Arguably, the action in step 2 is idiotic: it's clearly impossible to apply the long rule if the shorter .config
is ignored, so the presence of .config/<something>
as un-ignored ought to trigger Git to un-ignore .config
. And in fact, the newest versions of Git try to be more clever about this, but older ones don't: you must explicitly un-ignore .config
(or /.config
, which is better here for reasons I won't get into, but that's why I wrote it that way above2).
There is a more elegant trick but it causes Git to be much slower, so you may not want to use it. The trick is to explicitly un-ignore all directories:
*
!*/
!/.bashrc
and so on. But now if you have a directory a
with millions of sub-directories and files within a
, Git will scan all a
and all its subdirectories, whereas before, it would skip it.
1Having a file within .config
in the index forces Git to scan .config
. However, you'd then run into the same issue with .config/terminator
unless the index contained a file within .config/terminator
as well.
2OK, I will get into it a little bit. It does not apply in this particular case, but assuming you are un-ignoring .bashrc
, if Git were for some reason to scan a/
, and a/
contained a file named .bashrc
, Git would not ignore a/.bashrc
. We do not need the leading slash on .config/terminator
, though, because that contains an embedded slash (one not at the end). This makes the match on that name rooted, while matches on .bashrc
are non-rooted and hence match a/.bashrc
.
Upvotes: 4