Search code examples
gitwildcardgitignore

Ignore file extension except in a specific directory in `.gitignore`


In my repository, I have scripts that generate loads of .json files, that I want to ignore, so I have *.json in .gitignore. But I have one directory config containing .json files and I want those to be in version control. I know I can run git add -f config/*.json, but is there any solution using only .gitignore to override the previous rules, so that git sees these files automatically without me needing to manually run git add -f ...?

I have tried these, but none worked:

*.json

!configs
!configs/
!configs/*
!configs/*.json

EDIT: to clarify, both .gitignore and the configs directory are both at the the top level of the repository. The structure looks something like this:

repo/
├─ .gitignore
├─ config/
│  ├─ keep_this_1.json
│  ├─ keep_this_2.json
├─ scripts/
│  ├─ results/
│  │  ├─ d1/
│  │  │  ├─ ignore_this.json
│  │  ├─ d2/
│  │  │  ├─ ignore_this_too.json

Solution

  • Update

    After seeing the update to your question post, it seems like that your exclusions, and the ones suggested in the answers, did not work due to a typo.

    In fact, the original exclusion patterns refer to a configs folder, while in your directory structure and screenshot, the interested folder appears to be actually called config (without a trailing s).

    Since both the .gitignore and the config folder are on the same level, you can make the rules work with just the two following patterns:

    *.json
    !config/*.json
    

    Original Answer

    The exclusion !configs/*.json would work only if the .gitignore and the configs folder are at the same directory level. In fact, quoting the Pattern Format section of the gitignore documentation:

    If there is a separator at the beginning or middle (or both) of the pattern, then the pattern is relative to the directory level of the particular .gitignore file itself. Otherwise the pattern may also match at any level below the .gitignore level.

    This means that if the .gitignore file is defined in the root level of your working directory, while configs is in a subdirectory, you need to either provide the path to configs relatively from the .gitignore:

    *.json
    !path/relative/from/gitignore/to/configs/*.json
    

    or use the double asterisk to make the pattern match across multiple directory levels:

    *.json
    !**/configs/*.json
    

    However, this second rule will exclude the json content of any configs directory in your project. Therefore, if you need to exclude only the json files of a particular configs folder, this exclusion won't do it. The previous rule is most likely what you need in this scenario.


    Just for completeness, I've listed the behavior of each one of your exclusions to better understand what was going on:

    *.json
    
    !configs          # excludes both files and directories named configs at any level below the .gitignore
    !configs/         # excludes only directories named configs at any level  below the .gitignore
    !configs/*        # excludes any content of a directory named configs at the same level of the current .gitignore
    !configs/*.json   # excludes any json file in a directory named configs at the same level of the current .gitignore