The rule is simple:
It is not possible to re-include a file if a parent directory of that file is excluded.
That is why you need to
-
ignore files and folders recursively:
/foo/**
(if you only ignore /foo/, that is the folder, then no amount of ‘!‘ exclusion rule will work, since the foo/ folder itself is ignored: Git will stop there)
-
Then exclude folders from the ignore rules:
!/foo/**/ -
Before whitelisting files like the
.gitkeep!/foo/**/.gitkeep
That works because the .gitkeep parent folders are excluded from the ignore rules.
As opposed to Bernardo original proposal (before his edit):
/foo/**
!**/.gitkeep
If does not exclude folders from the /** ignore rule, so the exclusion rule for files is inoperative.
You can check that with:
git check-ignore -v -- /path/to/.gitkeep
As mentioned in scm .gitignore:
- using ‘
*‘ is the same as ‘**‘ - using
!.gitkeep(without any / anchor) would exclude that file recursively.
In both cases, ‘recursively‘ is the key term which explains why exclusion rules can apply: if you ignore a folder (like . or /foo/), you won’t be able to exclude anything inside that folder.
But if you ignore elements (files and folder) recursively (* or **), and exclude folders from gitignore rules (again through recursive rule !*), then you can exclude (white-list) files.