Unignore subdirectories of ignored directories in Git

GitGitignore

Git Problem Overview


Let's say I have ignored a directory, but I want to unignore specific subdirectories therein. So I have the setup:

/uploads/
/uploads/rubbish/
/uploads/rubbish/stuff/KEEP_ME/
/uploads/foo/
/uploads/foo/bar/lose/

And I want to ignore everything but the KEEP_ME directory. I would hope the ignore would look something like:

/uploads/*
!/uploads/rubbish/stuff/KEEP_ME/

But that's not working, and neither are several permutations on the same theme.

One that does work is

/uploads/**/**/**/
!/uploads/rubbish/stuff/KEEP_ME/

But that feels a little restrictive and verbose?

Git Solutions


Solution 1 - Git

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/**/*

Solution 2 - Git

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

Solution 3 - Git

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.

Solution 4 - Git

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.

Solution 5 - Git

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/* 

Solution 6 - Git

git version 2.30.1 (Apple Git-130)

For people like me who need to unignore one type of files, the ! method also works. Also, like Art Shayderov mentioned, this method is still listed in git's official doc

So my case is that the dsym files are completely ignored from our iOS project. However, we need to check in some necessary dsym files that come with the xcframeworks. Here is part of my .gitignore

## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
!*.dSYM

After I added the last line and saved the file, those dsym immediately show up in my source control tool.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionWilView Question on Stackoverflow
Solution 1 - GitArt ShayderovView Answer on Stackoverflow
Solution 2 - GitKingCrunchView Answer on Stackoverflow
Solution 3 - Gituser295691View Answer on Stackoverflow
Solution 4 - GitJosiah YoderView Answer on Stackoverflow
Solution 5 - GitJohnView Answer on Stackoverflow
Solution 6 - Gitinfinity_coding7View Answer on Stackoverflow