342

Unless a repo consisted of several independent projects, it seems it would be simplest to just have one .gitignore file at the root of the repo than various ones throughout. Is there a standard best practice on this or some analysis online of when one approach is better than the other?

Conley Owens
  • 8,691
  • 5
  • 30
  • 43

6 Answers6

370

I can think of at least two situations where you would want to have multiple .gitignore files in different (sub)directories.

  • Different directories have different types of file to ignore. For example the .gitignore in the top directory of your project ignores generated programs, while Documentation/.gitignore ignores generated documentation.

  • Ignore given files only in given (sub)directory (you can use /sub/foo in .gitignore, though).

Please remember that patterns in .gitignore file apply recursively to the (sub)directory the file is in and all its subdirectories, unless pattern contains '/' (so e.g. pattern name applies to any file named name in given directory and all its subdirectories, while /name applies to file with this name only in given directory).

sanyassh
  • 8,100
  • 13
  • 36
  • 70
Jakub Narębski
  • 309,089
  • 65
  • 217
  • 230
  • 2
    Ah, for some reason I thought /Documentation/*.html would cover this, but I guess the * wild card will only match directories at one level. – Conley Owens Jul 22 '10 at 16:55
  • 14
    @ConleyOwens: with modern Git you can use `Documentation/**/*.html` (note that any slash anchors the pattern; the `/foo` is used to anchor file directly in directory) – Jakub Narębski Sep 14 '14 at 13:22
  • 2
    Third scenario: Generated `.gitignore` from e.g. `yarn`: Easier to leave it where it was generated, plus the generating tool may modify it further as part of an update or configuration change. – mrienstra Jan 28 '22 at 18:44
  • 1
    Because of your remarks about using "Documentation/**/*.html" and "/sub/foo", you convinced me that there likely are no situations where I would want to create multiple .gitignore files. (apart from those that are auto-generated) – John Pankowicz Feb 08 '22 at 17:06
  • @JohnPankowicz : I think it is a matter of convention and developer's choice whether to put all patterns in one large `.gitignore` file, or have separate `.gitignore` files for separate subsystems / separate parts with their own rules. – Jakub Narębski Feb 09 '22 at 19:09
  • Sorry, I was just being a bit flippant. It was just that you started out by saying there are 2 situations when you would want multiple ones. But then for both situations you showed how to easily avoid using multiples. I would much prefer not using multiples if it is easily avoided. – John Pankowicz Feb 09 '22 at 22:38
144

As a tangential note, one case where the ability to have multiple .gitignore files is very useful is if you want an extra directory in your working copy that you never intend to commit. Just put a 1-byte .gitignore (containing just a single asterisk) in that directory and it will never show up in git status etc.

Aristotle Pagaltzis
  • 112,955
  • 23
  • 98
  • 97
  • 7
    you can also use ".git/info/exclude" file for that – Ayell May 19 '16 at 00:01
  • 18
    Sure, if you don’t mind the fiddliness of having to open a file in a location somewhere off the repository root, then writing a whole path into it, and then remembering to clean up the entry if/when you delete the directory. Compare that with `printf \* > .gitignore` (cleanup is automatic when you delete the directory). I’m certain there are situations where `.git/info/exclude` is the more appropriate choice, but not many. – Aristotle Pagaltzis May 22 '16 at 07:35
  • 1
    Yes, when you want to exclude a file instead of a folder for example :p – Ayell May 23 '16 at 05:40
  • 5
    I said “if you want an extra directory in your working copy that you never intend to commit” in my answer. – Aristotle Pagaltzis Jun 19 '16 at 18:12
  • 1
    Since it creates less clutter in the root .gitignore, I like this approach a lot. – David A. Gray Sep 04 '18 at 03:58
  • BTW, the old school way to create a 1-byte text file is copy con: .gitignore, press Enter, then *, followed by Ctrl-Z and another Enter. – David A. Gray Sep 04 '18 at 03:59
  • @DavidA.Gray: That's on Windows, right? On Linux this would be `cat > .gitignore`, `*`, Enter (possibly optional), CTRL+D – MestreLion Jan 26 '23 at 22:28
  • @MestreLion Using a single-byte file is just for extra style points. Just use a text editor and put a single line with a `*` in it. FWIW though, the terminal only recognizes Ctrl-D at the start of a line, so if you use `cat`, you cannot end the file immediately after the asterisk. You have to also press Enter first, which will then go into the file, making it at least 2 bytes. If you insist on showing off, you need `printf \* > .gitignore` instead (because unlike `echo`, `print` does not automatically add a newline). – Aristotle Pagaltzis Jan 27 '23 at 23:47
  • @AristotlePagaltzis 1 less byte in the file, but 3 more keystrokes. Tough decision!!! LOL (btw, will git accept it? many *nix tools complain on missing trailing `\n`) – MestreLion Jan 28 '23 at 00:06
  • @MestreLion Yes it will, doesn’t complain either. – Aristotle Pagaltzis Jan 29 '23 at 01:19
81

You can have multiple .gitignore, each one of course in its own directory.
To check which gitignore rule is responsible for ignoring a file, use git check-ignore: git check-ignore -v -- afile.

And you can have different version of a .gitignore file per branch: I have already seen that kind of configuration for ensuring one branch ignores a file while the other branch does not: see this question for instance.

If your repo includes several independent projects, it would be best to reference them as submodules though.
That would be the actual best practices, allowing each of those projects to be cloned independently (with their respective .gitignore files), while being referenced by a specific revision in a global parent project.
See true nature of submodules for more.


Note that, since git 1.8.2 (March 2013) you can do a git check-ignore -v -- yourfile in order to see which gitignore run (from which .gitignore file) is applied to 'yourfile', and better understand why said file is ignored.
See "which gitignore rule is ignoring my file?"

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
33

Pro single

  • Easy to find.

  • Hunting down exclusion rules can be quite difficult if I have multiple gitignore, at several levels in the repo.

  • With multiple files, you also typically wind up with a fair bit of duplication.

Pro multiple

  • Scopes "knowledge" to the part of the file tree where it is needed.

  • Since Git only tracks files, an empty .gitignore is the only way to commit an "empty" directory.

    (And before Git 1.8, the only way to exclude a pattern like my/**.example was to create my/.gitignore in with the pattern **.foo. This reason doesn't apply now, as you can do /my/**/*.example.)


I much prefer a single file, where I can find all the exclusions. I've never missed per-directory .svn, and I won't miss per-directory .gitignore either.

That said, multiple gitignores are quite common. If you do use them, at least be consistent in their use to make them reasonable to work with. For example, you may put them in directories only one level from the root.

isherwood
  • 58,414
  • 16
  • 114
  • 157
Paul Draper
  • 78,542
  • 46
  • 206
  • 285
  • 2
    "an empty .gitignore is the only way to commit an "empty" directory." Actually I find that uploading a single README file (or a file named `empty` for that matter) is more common. – Marc.2377 Oct 26 '16 at 02:16
  • 12
    .gitkeep is a nice way to do this too... just another convention. I like the idea of using a readme file, because then you could explain what the directory is used for, in that read me file. – Gavin Pickin Jun 05 '17 at 16:44
  • @GavinPickin: As there are comments in `.gitignore`, it's the self-documenting variant of a IMHO cargo `.keep` (b/c it's yet another convention for a "problem" that has been solved within git by git and no need for another meta/special file). File-comment shows intent, ignore all, then not-ignore the ignore file itself and done. Anyone who is curious the directory is for, just look into the file. This way it encourages to use self-explanatory-directory names as well and does not hinder to work with a read-me file in the root, too. – hakre Aug 06 '21 at 08:57
26

There are many scenarios where you want to commit a directory to your Git repo but without the files in it, for example the logs, cache, uploads directories etc.

So what I always do is to add a .gitignore file in those directories with the following content:

# this directory is intentionally ignored
/*
!/.gitignore

With this .gitignore file, Git will not track any files in those directories yet still allow me to add the .gitignore file and hence the directory itself to the repo.

hakre
  • 193,403
  • 52
  • 435
  • 836
Lukman
  • 18,462
  • 6
  • 56
  • 66
  • I really like this usage-pattern as well, it is a very good rule of thumb and (just edited to make it obvious) has the bonus you can add a comment as well. – hakre Aug 06 '21 at 09:04
4

Another use-case for .gitignore in a subfolder is in monorepos where it may be more appropriate for each repo to specify which files that should be ignored rather than moving them all up to a single mega .gitignore which reaches down into each project.

Example: we have a monorepo based on the nx build system. Inside it we have a tools folder which contains various utilities, some of which are small independent projects in their own right. It seems appropriate in this case for each of these tools (which has its own package.json for example) to also have a separate .gitignore. It is easier to maintain, and easier for devs to see what is going on.

see sharper
  • 11,505
  • 8
  • 46
  • 65