The solution presented as the most-upvoted answer is incorrect, and easily demonstrable as such.
Start with ignoring everything in uploads/*:
mkdir -p uploads/rubbish/stuff/KEEP_ME
touch uploads/a uploads/rubbish/a uploads/rubbish/stuff/a uploads/rubbish/stuff/KEEP_ME/a
echo '/uploads/*' >> .gitignore
git init
git add .
git commit -m "Initial commit"
Now unignore the parent directory of the ignored stuff as above:
echo 'uploads/rubbish/stuff/KEEP_ME/' >> .gitignore
echo 'uploads/rubbish/stuff/KEEP_ME/*' >> .gitignore
git status -u
Shows no untracked files.
In order to get it to work, you need to ignore all files under the uploads/
tree (uploads/**/*
, not just the top level, uploads/*
) and then add all parent directories of the tree you want to keep
echo '/uploads/**/*' > .gitignore
echo '!/uploads/rubbish/' >> .gitignore
echo '!/uploads/rubbish/stuff' >> .gitignore
echo '!/uploads/rubbish/stuff/KEEP_ME' >> .gitignore
echo '!/uploads/rubbish/stuff/KEEP_ME/*' >> .gitignore
git status -u
Which gives:
On branch master
...
Untracked files:
(use "git add <file>..." to include in what will be committed)
uploads/rubbish/stuff/KEEP_ME/a
If we had used uploads/*
in the .gitignore
above, then all the intermediate files would have been included as well, so for example uploads/rubbish/a
would show up in the status command above.