Git is practically a universal standard when it comes to source code version control. Git allows you to track changes to all files in your software development project.
However, Git is not suitable for tracking all file types. Generally speaking, you should not track large binary files, files containing secrets and sensitive information, or any file that is generated as part of a build process and can be easily reproduced.
To avoid tracking certain types of files and directories, Git employs .gitignore files. In this blog post we will learn about this file type and how it works.
What is a .gitignore file?
Most developers today are familiar with Git and use it for source code version control. In general, you want to include most of your files in your Git repository. Files that you want to keep a historical record of are called tracked files. However, not all files in a directory should be tracked.
Typical examples of files you do not want to track with Git are:
- Binary files - for instance, Terraform provider binaries
- Secrets and sensitive files - files containing passwords or personal identifying information
- Dependencies - such as the .terraform directory for Terraform projects
- Temporary files - files that briefly exist during execution of a specific tool
These files can end up in your repository, so we need a way to make sure Git ignores them. This is what the .gitignore file is for; it contains one or more patterns that tell Git which files and directories to ignore.
Syntax of .gitignore
A .gitignore file can contain blank lines, comments and patterns. Blank lines have no meaning but can be used to separate patterns from one another.
A comment starts with a [.code]#[.code] and must be placed at the beginning of a row:
# this is a comment
Any non-blank row that is not a comment is considered a pattern, and any files or directories that match a pattern are ignored by Git.
Patterns containing a leading [.code]/[.code] or a [.code]/[.code] somewhere in the middle of the pattern are evaluated relative to the location of the file. A pattern with no or a trailing [.code]/[.code] matches files and directories in any subdirectory relative to the ignore file.
Patterns with a [.code]/[.code] at the beginning or middle will only locate matches in the same directory as your .gitignore file.
Patterns without [.code]/[.code] will match everything in that directory, including all folders within it. Patterns ending in [.code]/[.code] will only match the folders in that directory.
In the following examples, we assume that the file is in the root folder of the repository.
The simplest type of pattern are exact matches:
# ignore files and directories named file.txt or directory1 anywhere in the repository
file.txt
directory1
To specifically match a directory, add a [.code]/[.code] at the end of the pattern:
# ignore directories named directory1 anywhere in the repository
directory1/
You can use wildcards [.code]*[.code] to match parts of a file or directory name:
# ignore all .txt files in a directory named docs in the root of the repository
docs/*.txt
You can negate a pattern by adding a [.code]![.code] at the beginning of the row. This allows you to negate patterns defined earlier in the file:
# ignore .txt files in docs/, except for a file named important.txt
docs/*.txt
!docs/important.txt
There are several instances for using two consecutive asterisks [.code]**[.code]:
# ignore anything inside a directory named docs in the root of the repository
docs/**
You can negate a pattern by adding a [.code]![.code] at the beginning of the row. This allows you to negate patterns defined earlier in the file:
# ignore .txt files in docs/, except for a file named important.txt
docs/*.txt
!docs/important.txt
There are several instances for using two consecutive asterisks [.code]**[.code]:
# ignore anything inside a directory named docs in the root of the repository
docs/**
# ignore directories named docs anywhere inside your repository
**/docs
# ignore .txt files in any subdirectory of a directory named docs in the root of the repository
docs/**/*.txt
You can use a [.code]?[.code] to match a single character except for [.code]/[.code]:
# ignore e.g. test1.txt, test2.txt, etc but not test.txt or testing.txt
test?.txt
Finally, you can also match a single character belonging to a specific category using a regular expression:
# ignore test1.txt or test2.txt but not test3.txt or test4.txt etc
test[1-2].txt
How to create a .gitignore file?
A .gitignore file is a simple text file. It is automatically recognized by Git, so once you have created the file it will be used by your Git client.
Create the file and add the patterns you want to ignore.
Where to place .gitignore?
You can place .gitignore files anywhere in your repository. Each file will be active for the same directory where it is located and all subdirectories, but will not affect its parent directory. Your repository could contain any number of .gitignore files.
A best practice is to use a single file placed in the root of your repository. This file will apply to all files in all subdirectories. Keeping a single file simplifies matters for everyone using the repository and makes troubleshooting issues easier.
Use cases for .gitignore
What you choose to avoid tracking in Git will vary depending on the content of your repository and any framework or tools that you use. In the following subsections, we cover a few common use cases for this file.
Ignoring credential files
If you add temporary credentials to any file in your repository, you should immediately update .gitignore to include these files. It is a common occurrence to add credentials to a file and then accidentally commit and push the credentials to your version control system.
If you have a file named credentials.txt you can add the following pattern to ignore it:
# ignore credentials files
credentials.txt
If you commonly use a specific name for local credentials you can add them to your personal git ignore rules, see Global .gitignore files later in this blog post.
If you work with SSH certificates you might also want to include patterns for the types of certificates you work with, an example is for PEM files:
# ignore .pem and .pfx files anywhere in the repository
*.pem
*.pfx
Ignoring Terraform provider binaries
Terraform downloads provider binaries and external modules to a directory named .terraform. This directory should not be tracked by Git since the provider files are large files and are not suitable for Git.
Add the following pattern to ignore all .terraform directories:
**/.terraform/
See Example of using .gitignore with Terraform later in this blog post for a full example for Terraform.
Ignoring IDE specific files
If you use VS Code as your IDE you will most likely want to include the .vscode directory where user-specific settings for VS Code are stored. There are similar directories for other IDEs. Sample patterns for VS Code and JetBrains IDEs are these:
# VS Code settings directory
.vscode/
# JetBrains settings directory
.idea/
There will be other similar directories or files if you use different IDEs or editors.
Advanced uses of .gitignore
Using .gitignore is generally simple, but there are a few advanced uses you should be aware of.
Global .gitignore files
You can create a global .gitignore file on your local system. The patterns defined in this file will be active for all Git repositories on your system.
A good use case for a global file is to ignore specific personal files that you commonly add to your repositories but do not want to track with Git. Examples are credential files or log files.
Create the file somewhere on your local system and add the patterns for the files and directories you wish to ignore. A good location to store the file is in your home directory.
Next, configure your Git client to use the global file:
$ git config --global core.excludesFile ~/.gitignore
Override .gitignore patterns
Even if a file or directory is ignored, you can still force Git to track it by using the [.code]--force[.code] or [.code]-f[.code] flag in the [.code]git add[.code] command.
Given the following ignored patterns:
# ignore all .log files
*.log
If you still want to track a file named output.log you can use the following command:
$ git add --force output.log
$ git commit -m "Add output log file"
Ignoring a file you are currently tracking with git
If you want to stop tracking a file you are currently tracking, you need to first delete the file from Git but make sure to keep it in your working directory:
$ git rm --cached my-file.txt
rm 'my-file.txt'
Next, add a pattern for the file to .gitignore:
# the rest of the .gitignore file is omitted
my-file.txt
What to do when .gitignore is not working?
If you are experiencing issues where Git seems to ignore files that you did not expect, there are a few things you can do.
First, if possible, make sure you are only using a single .gitignore placed in the root of your repository. This simplifies troubleshooting and makes it much easier for everyone working in the repository to understand which files are ignored. You might discover that there are additional files in use, and their patterns are causing your issue.
Next, check if you are using a global .gitignore file. You can list your current Git config and check if the [.code]core.excludeFile[.code] configuration is set:
$ git config --list | grep -i core.excludesFile
core.excludesfile=/path/to/global/.gitignore
You can also check your global Git config file located in your home directory. If you see the following section configured in the file, you have a global .gitignore file in use:
[core]
excludesFile = /path/to/global/.gitignore
If you are still experiencing issues with files being ignored by Git that you did not expect, you can use the command [.code]git check-ignore[.code] to see why this is.
To see why a file named test1.txt is ignored by Git, run the following in your repository:
$ git check-ignore -v test1.txt
.gitignore:1:test[1-2].txt test1.txt
The output shows that test1.txt is ignored due to the pattern defined in row 1 in the file, and the specific pattern is [.code]test[1-2].txt[.code].
Example of using .gitignore with Terraform
Terraform creates a number of files and directories that you generally do not want to track with Git.
When you run [.code]terraform init[.code], Terraform creates a directory named .terraform where it downloads provider binaries and external modules. These are easily downloaded each time you need them and should not be tracked by Git. To ignore the .terraform directory, add the following pattern:
**/.terraform/
This pattern ignores all .terraform directories no matter where in your repository they are located.
The Terraform state file might contain secret values and resource attributes that you do not want to include in Git. To ignore the state file and any state backup file, include the following pattern:
.tfstate
.tfstate.*
If you are using Terraform variable files you will likely want to avoid adding these to .gitignore since they might also contain sensitive values. Add the following pattern to ignore variable files:
.tfvars
.tfvars.json
If you only use HCL variable files you can remove the *.tfvars.json pattern.
Other things you might want to ignore include override files, Terraform plan files (these files have no name convention so you need to come up with your own convention), crash log files and Terraform CLI configuration files.
A full example for Terraform looks like this:
# .terraform (provider binaries, external module source code, …)
**/.terraform/*
# state files
*.tfstate
*.tfstate.*
# Crash log files (if you run Terraform locally)
crash.log
crash.*.log
# Variable files
*.tfvars
*.tfvars.json
# Override files (HCL or JSON, depending on your configuration)
override.tf
override.tf.json
*_override.tf
*_override.tf.json
# Terraform CLI configuration files
.terraformrc
terraform.rc
Final thoughts
Most software development projects will require the use of .gitignore. You want to avoid tracking large binary files, secrets or sensitive information, as well as any build output that can easily be reproduced. Keeping these files outside of Git makes your repository faster and more secure.
Frequently Asked Questions
Q. How can I troubleshoot a .gitignore file?
The easiest way to troubleshoot issues with ignored patterns is to use the [.code]git check-ignore[.code] command. This command takes a path name (file name or directory), and if there is a match in a .gitignore file in your repository it will show where the pattern is defined.
Add the [.code]-v[.code] or [.code]--verbose[.code] flag to output file names and row numbers where the matching pattern is found.
$ git check-ignore -v my-pattern.txt
.gitignore:1:*.log docs/debug.log
Q. Where can I find common example .gitignore files?
GitHub keeps a repository with a large number of .gitignore template files specifically for different languages, tools and frameworks. You can use these files as a starting point for your own projects.
The repository is found here.
Q. How do I ignore a folder using .gitignore?
To match a specific directory anywhere in your repository, add a trailing [.code]/[.code]:
# ignore directories named .terraform anywhere in your repository
.terraform/
You can also specifically ignore a single directory by using the full path to the directory relative to the .gitignore file:
# ignore the directory infrastructure/.terraform
infrastructure/.terraform
Q. What should be included in .gitignore for Terraform?
In general, a .gitignore file for Terraform should ignore a number of things:
- Your state file terraform.tfstate and any state backup files
- The .terraform directory where provider binaries and external modules are downloaded
- Terraform variable files *.tfvars
- Terraform CLI configuration files .terraformrc and terraform.rc
You might also want to include crash log files (crash.log and crash.*.log) and Terraform override files if you use these (override.tf files).
Finally, if you output the result of [.code]terraform plan[.code] to a file, you should exclude it from Git. Plan output has no default naming convention, so this depends on what you name your plan files.