Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unignore subdirectories of ignored directories in Git

Tags:

git

gitignore

According to pattern format section of the gitignore documentation:

An optional prefix "!" which negates the pattern; any matching file excluded by a previous pattern will become included again. It is not possible to re-include a file if a parent directory of that file is excluded. Git doesn’t list excluded directories for performance reasons, so any patterns on contained files have no effect, no matter where they are defined. Put a backslash ("\") in front of the first "!" for patterns that begin with a literal "!", for example, "!important!.txt".

Therefore the previously-excluded parent directory /uploads/rubbish/stuff/keep/ pattern must be exclusively negated before negating its content:

#ignore everything within /uploads/ 
/uploads/*

#include everything within /uploads/rubbish/stuff/keep
!/uploads/rubbish/stuff/keep/  
!/uploads/rubbish/stuff/keep/*

To include subdirectories inside /uploads/rubbish/stuff/keep/ add the third line:

!/uploads/rubbish/stuff/keep/**/*

Even if you add something to .gitignore, you can force git to add it to the index

git add --force uploads/rubbish/stuff/KEEP_ME/

However, "KEEP_ME" seems to be a directory and git usually doesnt like empty folder, so you should can add a "placeholder"-holder file instead, if the folder is empty

git add --force uploads/rubbish/stuff/KEEP_ME/.keep_me

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.


Was trying to figure out how to include a specific folder when excluding all folders with the generic exclusion

**/build

if you add the /* to the end of your generic exclude you continue to exclude all build files **/build/*

Then you add another line to correct for the path that you want to be included to look like

!**/folder/build/* 

leaving us a gitignore that reads

**/build/* 
!**/folder/build/* 

Buo-Ren Lin and John's answers are quite helpful, but I needed to combine both.

When wanting to exclude other sub-folders as well as files within uploads, I found it necessary to explicitly specify arbitrary directories before the given folder both while excluding the folder and while including the sub-folder

**/uploads/*
!**/uploads/rubbish/
!**/uploads/rubbish/*

I also found it necessary to explicitly re-include both the sub-folder and its contents to show both items within the folder and sub-folders.