1421

I have a folder application/, which I add to the .gitignore.
Inside the application/-folder there is a folder application/language/gr.

How can I include this folder?

I've tried this:

application/
!application/language/gr/
Mike
  • 14,010
  • 29
  • 101
  • 161
chchrist
  • 18,854
  • 11
  • 48
  • 82
  • 1
    Hopefully, the "`.gitignore` pattern format" documentation just got clearer (December 2013). See [my answer below](http://stackoverflow.com/a/20652768/6309) – VonC Dec 18 '13 at 08:11
  • 1
    My favorite question and answer, added to favorites as well as to browser bookmarks. – codekiddy Nov 17 '15 at 07:02
  • 2
    See also [How do I tell Git to ignore everything except a subdirectory?](http://stackoverflow.com/questions/1248570/how-do-i-tell-git-to-ignore-everything-except-a-subdirectory) – 030 Mar 30 '17 at 07:03

20 Answers20

2268

If you exclude application/, then everything under it will always be excluded (even if some later negative exclusion pattern (“unignore”) might match something under application/).

To do what you want, you have to “unignore” every parent directory of anything that you want to “unignore”. Usually you end up writing rules for this situation in pairs: ignore everything in a directory, but not some certain subdirectory.

# you can skip this first one if it is not already excluded by prior patterns
!application/

application/*
!application/language/

application/language/*
!application/language/gr/

Note
The trailing /* is significant:

  • The pattern dir/ excludes a directory named dir and (implicitly) everything under it.
    With dir/, Git will never look at anything under dir, and thus will never apply any of the “un-exclude” patterns to anything under dir.
  • The pattern dir/* says nothing about dir itself; it just excludes everything under dir. With dir/*, Git will process the direct contents of dir, giving other patterns a chance to “un-exclude” some bit of the content (!dir/sub/).
fawaad
  • 341
  • 6
  • 12
Chris Johnsen
  • 214,407
  • 26
  • 209
  • 186
  • 17
    Are the trailing asterisks significant? If so, what's the difference in meaning? Per the algorithm described in the *gitignore* documentation, ending with a trailing slash matches a directory and paths beneath that directory. Ending with an asterisk would then fall to treatment as a glob pattern. Experimenting shows the asterisk variant to work, but not the one ending in just a trailing slash. I'd like to understand why that's so. – seh May 11 '11 at 13:21
  • 151
    @seh: Yes, the trailing `/*` is significant. If a directory is excluded, Git will never look at the contents of that directory. The pattern `dir/` excludes a directory named `dir` and (implicitly) everything under it. The pattern `dir/*` says nothing about `dir` itself; it just excludes everything under `dir`. With `dir/`, Git will never look at anything under `dir`, and thus will never apply any of the “un-exclude” patterns to anything under `dir`. With `dir/*`, Git will process the direct contents of `dir`, giving other patterns a chance to “un-exclude” some bit of the content (`!dir/sub/`). – Chris Johnsen May 12 '11 at 02:58
  • 12
    Ah, that explains it. No matter how many times I've read the *gitignore* documentation, I never understood when the reverted patterns don't work. With your explanation, it's now clear. The *gitignore* documentation needs a "recipe" section to explain how to do this. – seh May 13 '11 at 00:32
  • Why on earth doesn't git just assume the trailing asterisk if the line points to a directory? Just adding that asterisk fixed the problem I was having and made git behave as expected! – Jez Mar 26 '13 at 12:02
  • Also note that until .gitignore is STAGED the changes will not show on your working copy. I sometimes forget that! – Greg Robson May 01 '14 at 10:58
  • @Jez If the directory implied an asterisk then the first line would exempt all files in that directory from being ignored, even ones that should be ignored by other rules. – Jason Goemaat May 29 '14 at 21:27
  • For me, skipping the first line works all the time, no need for the `!application/`. `!application/language/` overrules `application/*`. It does not overrule `application/`. – smets.kevin Jul 17 '14 at 11:47
  • 10
    I couldn't quite get this to work (crazy .gitignore file!), so instead I just force-added the files after cd'ing to the directory I wanted. `git add -f .` – K0D4 Jul 21 '15 at 21:42
  • 1
    @Koda: force-adding works to a point -- the danger is that if you later add extra files to a directory that you force-added, they will not be included by default (you will have to explicitly force-add the new files as well, and it's easy to forget to do this). Getting the ignore exclusions correct avoids this problem. – Miral Jul 12 '16 at 03:03
  • 2
    Note that you can't rely on the output of `git status`, which will just tell you that the top level directory is going to be added. Instead, do a `git add` of the top level directory and then `git status` will (hopefully) list the subset of files that have been matched by the pattern. – Matthew Strawbridge Sep 02 '16 at 10:00
  • 2
    Does not work for git version ```2.10.2.windows.1```. – Sebastian Nov 28 '16 at 14:21
  • I also found that the `git status` output can be miss-leading. I tried various ways to add the `/.config/ranger` folder but the `git status` can only show `/.config`. If I run `git add` then the `/.config/ranger` folder is actually added. – Jason Dec 18 '17 at 06:37
  • So the non-asterisk is recursive but doesn't allow exclusions, while the asterisk version is non-recursive but does allow exclusions? – endolith May 17 '18 at 18:41
  • Quite convoluted, but functional – Post Self Oct 20 '18 at 13:28
  • 1
    Adding `/public/img/*` and `!/public/img/*/` worked perfectly for me. Ignoring the contents in the `img` directory, but not any of its subdirectories. – Cloud Jul 18 '19 at 19:20
  • @ChrisJohnsen I don't seem this working when I need to ignore application/* from all root folder/sub-folders but unignore same directory inside particular path i.e. thirdparty. I am using following .gitignore application/* !thirdparty/**/application/ – OmGanesh Nov 06 '19 at 21:54
  • can't understand why this nonsense. regex would make gitignore much easier – santaclos May 21 '23 at 14:53
182

Commit 59856de from Karsten Blees (kblees) for Git 1.9/2.0 (Q1 2014) clarifies that case:

gitignore.txt: clarify recursive nature of excluded directories

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. (*)
(*: unless certain conditions are met in git 2.8+, see below)
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".

Example to exclude everything except a specific directory foo/bar (note the /* - without the slash, the wildcard would also exclude everything within foo/bar):

 --------------------------------------------------------------
     $ cat .gitignore
     # exclude everything except directory foo/bar
     /*
     !/foo
     /foo/*
     !/foo/bar
 --------------------------------------------------------------

In your case:

application/*
!application/**/
application/language/*
!application/language/**/
!application/language/gr/**

You must white-list folders first, before being able to white-list files within a given folder.


Update Feb/March 2016:

Note that with git 2.9.x/2.10 (mid 2016?), it might be possible to re-include a file if a parent directory of that file is excluded if there is no wildcard in the path re-included.

Nguyễn Thái Ngọc Duy (pclouds) is trying to add this feature:

So with git 2.9+, this could have actually worked, but was ultimately reverted:

application/
!application/language/gr/
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • I tried to use the updated re-include syntax posted at the end of your answer on git for windows `v2.8.1.windows.1` but it does not appear to work :( – David Hancock Apr 12 '16 at 17:58
  • 1
    @DavidHancock Sorry, I have edited the answer: this is not available yet. – VonC Apr 12 '16 at 18:24
  • Thanks for clearing that up. I have a monstrous .gitignore at the minute. I hope this makes it into 2.9. – David Hancock Apr 12 '16 at 18:48
  • 1
    @DavidHancock me too: that is more than 13 Stack Overflow answers that I had to edit multiple times over! – VonC Apr 12 '16 at 18:49
  • 5
    Git 2.9 was released yesterday. Confirming the `application/` + `!application/language/gr/` pattern mentioned in the answer is working as expected. – Ray Shan Jun 15 '16 at 00:52
  • 2
    @RayShan Strange: I have see the revert commit cancelling that feature, but I haven't see (and the release note https://github.com/git/git/blob/master/Documentation/RelNotes/2.9.0.txt does not mention) any commit improving the .gitignore rules. – VonC Jun 15 '16 at 07:13
  • Does not work for git version ```2.10.2.windows.1```. – Sebastian Nov 28 '16 at 14:20
  • @Sebastian Yes, it still does not work, and I have edited the answer to make it clearer. – VonC Nov 28 '16 at 19:40
77

@Chris Johnsen's answer is great, but with a newer versions of Git (1.8.2 or later), there is a double asterisk pattern you can leverage for a bit more shorthand solution:

# assuming the root folder you want to ignore is 'application'
application/**/*

# the subfolder(s) you want to track:
!application/language/gr/

This way you don't have to "unignore" parent directory of the subfolder you want to track.


With Git 2.17.0 (Not sure how early before this version. Possibly back to 1.8.2), using the ** pattern combined with excludes for each subdirectory leading up to your file(s) works. For example:

# assuming the root folder you want to ignore is 'application'
application/**

# Explicitly track certain content nested in the 'application' folder:
!application/language/
!application/language/gr/
!application/language/gr/** # Example adding all files & folder in the 'gr' folder
!application/language/gr/SomeFile.txt # Example adding specific file in the 'gr' folder
Ben Kane
  • 9,331
  • 6
  • 36
  • 58
rpyzh
  • 917
  • 6
  • 9
  • 9
    Alas, this fails (Git 1.8.4.msysgit.0) because the pattern `**` can match zero subfolders, and `*` will match `language` and exclude it, preventing inclusion of `gr`. The full chain of parents @Chris Johnson recommends seems necessary still. – Sean Gugler Feb 14 '14 at 00:26
  • 3
    Sounds perfect but it doesn't work for me on git 2.3.7 ... `/www/**/* !/www/config.xml !/www/res` config.xml and the res directory are still ignored. – Rob Aug 11 '15 at 11:18
  • @Rob you need to also add `!/www/res/`. You can use the `folder/**/*` pattern, but you still need to add excludes for each subdirectory you want added. It's still shorter and more readable than the ignore/exclude combo. – Ben Kane Apr 12 '18 at 16:14
  • I know that's an old comment, but in case you are curious. I've added an edit to the answer documenting this approach. – Ben Kane Apr 12 '18 at 16:26
49

I've found only this actually works.

**/node_modules/*
!**/node_modules/keep-dir
Steve Kling
  • 491
  • 4
  • 3
31

There are a bunch of similar questions about this, so I'll post what I wrote before:

The only way I got this to work on my machine was to do it this way:

# Ignore all directories, and all sub-directories, and it's contents:
*/*

#Now ignore all files in the current directory 
#(This fails to ignore files without a ".", for example 
#'file.txt' works, but 
#'file' doesn't):
*.*

#Only Include these specific directories and subdirectories:
!wordpress/
!wordpress/*/
!wordpress/*/wp-content/
!wordpress/*/wp-content/themes/
!wordpress/*/wp-content/themes/*
!wordpress/*/wp-content/themes/*/*
!wordpress/*/wp-content/themes/*/*/*
!wordpress/*/wp-content/themes/*/*/*/*
!wordpress/*/wp-content/themes/*/*/*/*/*

Notice how you have to explicitly allow content for each level you want to include. So if I have subdirectories 5 deep under themes, I still need to spell that out.

This is from @Yarin's comment here: https://stackoverflow.com/a/5250314/1696153

These were useful topics:

I also tried

*
*/*
**/**

and **/wp-content/themes/**

or /wp-content/themes/**/*

None of that worked for me, either. Lots of trial and error!

Katie
  • 45,622
  • 19
  • 93
  • 125
  • 3
    Note: Remember that order does matter in a .gitignore file too, so make sure you put your `!` rules at the bottom. – starbeamrainbowlabs Aug 12 '19 at 11:15
  • 3
    this is more embarrassing than javascript as a language itself. why on earth are there so many different and absurd answers for such a simple problem? (I'm being critical of git, not your solution). And no, git is not bad, but it has some bad parts. – Joel M Mar 01 '21 at 05:04
15

The simplest and probably best way is to try adding the files manually (generally this takes precedence over .gitignore-style rules):

git add /path/to/module

You might need -f if the file is already ignored. You may even want the -N intent to add flag, to suggest you will add them, but not immediately. I often do this for new files I’m not ready to stage yet.


This a copy of an answer posted on what could easily be a duplicate QA. I am reposting it here for increased visibility—I find it easier not to have a mess of gitignore rules.

D. Ben Knoble
  • 4,273
  • 1
  • 20
  • 38
14

I wanted to track Nagios configuration files located in /etc/nagios/ together with the plugins in /usr/lib64/nagios/plugins/. For this I have initialized a git repo in / and used the following exclusion list:

/*
!etc
etc/*
!etc/nagios
!usr
usr/*
!usr/lib64
usr/lib64/*
!usr/lib64/nagios
usr/lib64/nagios/*
!usr/lib64/nagios/plugins

Git walks down the list like that:

/*             exclude everything under / ...
!etc           but include /etc back
etc/*             exclude everything under /etc/...
!etc/nagios       but include /etc/nagios back
!usr           but include /usr back
usr/*             exclude everything under /usr/...
                  and so on...
BMC
  • 184
  • 2
  • 9
13

I have found a similar case here, where in laravel by default, .gitignore ignores all using asterix, then overrides the public directory. ( This is also the same solution as the main answer @Chris Johnsen, just a bit thinner and more concise maybe.)

*
!public
!.gitignore

This is not sufficient if you run into the OP scenario.

If you want to commit a specific subfolders of public, say for e.g. in your public/products directory you want to include files that are one subfolder deep e.g. to include public/products/a/b.jpg they wont be detected correctly, even if you add them specifically like this !/public/products, !public/products/*, etc..

The solution is to make sure you add an entry for every path level like this to override them all.

*
!.gitignore
!public/
!public/*/
!public/products/
!public/products/*
!public/products/*/
!public/products/*/
!public/products/*/*
blamb
  • 4,220
  • 4
  • 32
  • 50
13

add a file named .gitignore to subfolder, then fill with

!/Bin/

this works for me!

jiayi797
  • 151
  • 1
  • 4
11

My JetBrains IntelliJ IDEA .gitignore-configuration, where I need to exclude whole .idea folder except .idea/runConfigurations:

.idea
!.idea/
.idea/*
!.idea/runConfigurations/

Further details:
https://github.com/daggerok/gitignore-idea-runConfigurations#exclude-idea-except-idearunconfigurations

Mike
  • 14,010
  • 29
  • 101
  • 161
Maksim Kostromin
  • 3,273
  • 1
  • 32
  • 30
  • 1
    BTW, for me it was enough to use `.idea/*` and `!.idea/runConfigurations/`. No need to to write `.idea` and `!.idea/`. – Mike Jun 12 '23 at 15:36
7

So , since many programmers uses node . the use case which meets this question is to exclude node_modules except one module module-a for example:

!node_modules/

node_modules/*
!node_modules/module-a/
Abdennour TOUMI
  • 87,526
  • 38
  • 249
  • 254
7

Add an additional answer:

!/.vs/              <== include this folder to source control, folder only, nothing else
/.vs/*              <== but ignore all files and sub-folder inside this folder
!/.vs/ProjectSettings.json <== but include this file to source control
!/.vs/config/       <== then include this folder to source control, folder only, nothing else
!/.vs/config/*      <== then include all files inside the folder

here is result:

enter image description here

Dongdong
  • 2,208
  • 19
  • 28
7

gitignore - Specifies intentionally untracked files to ignore.

Example to exclude everything except a specific directory foo/bar (note the /* - without the slash, the wildcard would also exclude everything within foo/bar):

$ cat .gitignore
# exclude everything except directory foo/bar
/*
!/foo
/foo/*
!/foo/bar

Another example for WordPress:

!/wp-content
wp-content/*
!/wp-content/plugins
wp-content/plugins/*
!wp-content/plugins/my-awesome-plugin

More informations in here: https://git-scm.com/docs/gitignore

Ali Han
  • 509
  • 7
  • 10
6

This worked for me:

**/.idea/**
!**/.idea/copyright/
!.idea/copyright/profiles_settings.xml
!.idea/copyright/Copyright.xml
garbo
  • 139
  • 2
  • 2
4

Especially for the older Git versions, most of the suggestions won't work that well. If that's the case, I'd put a separate .gitignore in the directory where I want the content to be included regardless of other settings and allow there what is needed.

For example: /.gitignore

# ignore all .dll files
*.dll

/dependency_files/.gitignore

# include everything
!*

So everything in /dependency_files (even .dll files) are included just fine.

Breit
  • 399
  • 5
  • 9
4

In WordPress, this helped me:

wp-admin/
wp-includes/
/wp-content/*
!wp-content/plugins/
/wp-content/plugins/*
!/wp-content/plugins/plugin-name/
!/wp-content/plugins/plugin-name/*.*
!/wp-content/plugins/plugin-name/**
T.Todua
  • 53,146
  • 19
  • 236
  • 237
3

Just another example of walking down the directory structure to get exactly what you want. Note: I didn't exclude Library/ but Library/**/*

# .gitignore file
Library/**/*
!Library/Application Support/
!Library/Application Support/Sublime Text 3/
!Library/Application Support/Sublime Text 3/Packages/
!Library/Application Support/Sublime Text 3/Packages/User/
!Library/Application Support/Sublime Text 3/Packages/User/*macro
!Library/Application Support/Sublime Text 3/Packages/User/*snippet
!Library/Application Support/Sublime Text 3/Packages/User/*settings
!Library/Application Support/Sublime Text 3/Packages/User/*keymap
!Library/Application Support/Sublime Text 3/Packages/User/*theme
!Library/Application Support/Sublime Text 3/Packages/User/**/
!Library/Application Support/Sublime Text 3/Packages/User/**/*macro
!Library/Application Support/Sublime Text 3/Packages/User/**/*snippet
!Library/Application Support/Sublime Text 3/Packages/User/**/*settings
!Library/Application Support/Sublime Text 3/Packages/User/**/*keymap
!Library/Application Support/Sublime Text 3/Packages/User/**/*theme

> git add Library

> git status

On branch master
Your branch is up-to-date with 'origin/master'.
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   Library/Application Support/Sublime Text 3/Packages/User/Default (OSX).sublime-keymap
    new file:   Library/Application Support/Sublime Text 3/Packages/User/ElixirSublime.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/Package Control.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/Preferences.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/RESTer.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/SublimeLinter/Monokai (SL).tmTheme
    new file:   Library/Application Support/Sublime Text 3/Packages/User/TextPastryHistory.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/ZenTabs.sublime-settings
    new file:   Library/Application Support/Sublime Text 3/Packages/User/adrian-comment.sublime-macro
    new file:   Library/Application Support/Sublime Text 3/Packages/User/json-pretty-generate.sublime-snippet
    new file:   Library/Application Support/Sublime Text 3/Packages/User/raise-exception.sublime-snippet
    new file:   Library/Application Support/Sublime Text 3/Packages/User/trailing_spaces.sublime-settings
Adrian Dunston
  • 2,950
  • 4
  • 24
  • 23
3

Similar to this comment, none of the solutions and patterns worked for me; forcing git to add the files and folders that should be excluded, worked:

git add -f .

nightblade9
  • 354
  • 2
  • 15
2

I wanted to track jquery production js files and this worked:

node_modules/*
!node_modules/jquery
node_modules/jquery/*
!node_modules/jquery/dist/*
2

I often use this workaround in CLI where instead of configuring my .gitignore, I create a separate .include file where I define the (sub)directories I want included in spite of directories directly or recursively ignored by .gitignore.

Thus, I additionally use

git add `cat .include`

during staging, before committing.

To the OP, I suggest using a .include which has these lines:

<parent_folder_path>/application/language/gr/*

NOTE: Using cat does not allow usage of aliases (within .include) for specifying $HOME (or any other specific directory). This is because the line homedir/app1/* when passed to git add using the above command appears as git add 'homedir/app1/*', and enclosing characters in single quotes ('') preserves the literal value of each character within the quotes, thus preventing aliases (such as homedir) from functioning (see Bash Single Quotes).

Here is an example of a .include file I use in my repo here.

/home/abhirup/token.txt
/home/abhirup/.include
/home/abhirup/.vim/*
/home/abhirup/.viminfo
/home/abhirup/.bashrc
/home/abhirup/.vimrc
/home/abhirup/.condarc
Abhirup Das
  • 723
  • 5
  • 8