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}
?
There is only one source of truth. The official Git documentation on gitignore
: https://git-scm.com/docs/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.
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 |
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! |
!README.md | Doesn’t ignore any README.md 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. |
target/
*.class
*.jar
*.war
*.ear
*.logs
*.iml
.idea/
.eclipse
# ignore everything ...
/*
# ... but the following
!/.profile
!/.bash_rc
!/.bash_profile
!/.curlrc
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!
For each and every detail on gitignore
refer to these resources.