Using .gitignore the Right Way


Have you ever wondered what kind of patterns .gitignore allows? Was it **/*/target, target/* or *target*?? Read on and find out!


Everyone who uses Git sooner or later has to define a .gitignore in a newly created project. We simply don’t want to version control everything, especially generated files like Maven’s target or Gradle’s build folder. So how exactly can we specify which files to exclude? Can we use Ant-style syntax like **/*, simple Wildcards *target or even Regex [a-z]{0,3}?

The Truth

There is only one source of truth. The official Git documentation on gitignore:

If only people would read this before posting to Stackoverflow

As it turns out Git does not use regex, nor wildcards, nor Ant-style syntax, but unix glob patterns (specifically those valid for fnmatch(3)). Don’t worry, you don’t need to read the fnmatch(3) documentation, simply refer to the tables in the next sections.

The Most Important Use Cases

First things first, how can we exclude every target folder created by Maven in every sub-module?

The answer is very easy: target/
This will match any directory (but not files, hence the trailing /) in any subdirectory relative to the .gitignore file. This means we don’t even need any * at all.

Here is an overview of the most relevant patterns:

.gitignore entry Ignores every…
target/ folder (due to the trailing /) recursively
target file or folder named target recursively
/target file or folder named target in the top-most directory (due to the leading /)
/target/ folder named target in the top-most directory (leading and trailing /)
*.class …every file or folder ending with .class recursively

Advanced Use Cases

For more complicated use cases refer to the following table:

.gitignore entry Ignores every…
#comment …nothing, this is a comment (first character is a #)
\#comment …every file or folder with name #comment (\ for escaping)
target/logs/ …every folder named logs which is a subdirectory of a folder named target
target/*/logs/ …every folder named logs two levels under a folder named target (* doesn’t include /)
target/**/logs/ …every folder named logs somewhere under a folder named target (** includes /)
*.py[co] …file or folder ending in .pyc or .pyo. However, it doesn’t match .py!
! Doesn’t ignore any file even if it matches an exclude pattern, e.g. *.md.
NOTE This does not work if the file is located within a ignored folder.


Maven based Java project


Important Dot Files in Your Home Folder

# ignore everything ...
# ... but the following

Wait, There’s More

There are several locations where Git looks for ignore files. Besides looking in the root folder of a Git project, Git also checks if there is a .gitignore in every subdirectory. This way you can ignore files on a finer grained level if different folders need different rules.

Moreover, you can define repository specific rules which are not committed to the Git repository, i.e. these are specific to your local copy. These rules go into the file .git/info/exclude which is created by default in every Git repository with no entries.

One useful file you can define yourself is a global ignore file. It doesn’t have a default location or file name. You can define it yourself with the following command:

git config --global core.excludesfile ~/.gitignore_global

Every rule which goes into this file applies to every Git repository in your user account. This is especially useful for OS-specific files like .DS_Store on MacOS or thumbs.db on Windows.


As we have seen it is fairly easy to ignore files and folders for the typical use cases. Even though ignore rules don’t support regex, gitignore is highly flexible and can be adapted to more complicated project structures using unix globs and different files on different levels.

So now you know. Go ahead, rework your existing .gitignore files and add a global ignore file to you system!

More Resources

For each and every detail on gitignore refer to these resources.

Author: Christian Guggenmos
Tags: git
Categories: development, git