in form of a Q&A
- TL;DR
- What is
.gitignore
? - Which files would I want to ignore?
- What's the benefit of excluding certain files?
- Should I rely on
.gitignore
for safeguarding sensitive data? - Why do developers usually include executables in the
.gitignore
file? - Where can I find
.gitignore
templates? - How to format a
.gitignore
file? - How to check which files will be ignored?
- What happens to files that were being tracked before adding
.gitignore
? - How to stop tracking previously tracked files?
- Why would I want to ignore
.gitignore
? - Where should I put
.gitignore
? - And what is
.gitkeep
? - What gitmoji should I use when adding or updating a
.gitignore
file? - Did Git first introduce ignore functionality and glob pattern usage?
- What are some other common ignore files in software development?
- Where to locate examples of ignore files?
- Sources / further reading
Add a .gitignore
file to your project's working directory to tell Git which files have to be ignored. Each line in the file should contain a glob pattern matching files that you do not want to track in your Git repository.
For instance, the line
*.tmp
tells Git to ignore all files with the extension .tmp
.
Comment lines in .gitignore
start with a #
, empty lines are ignored.
If .gitignore
contains files that had previously been tracked, remove them with
git rm --cached
Be aware that relying solely on .gitignore
is not the recommended approach for preventing the accidental exposure of confidential data in your Git repository!
Use GitHub templates, GitLab's pre-populated projects, or an online generator for inspiration on .gitignore
file entries.
.gitignore
is a text file containing a list of all files that should be ignored by git, that is no change in those files should be recorded in the Git repository.
From the Git documentation:
gitignore - Specifies intentionally untracked files to ignore
Note that the .gitignore
file starts with a dot (.
). This means you might not be able to see it by default depending on your operating system. For instance, on Linux/Unix/Mac, if you type ls
you won't see files starting with .
. In order to see those files too you need to use the option -a
like this:
ls -a
Files starting with a .
are also known as dotfiles and they are usually configuration files. Typical examples of dotfiles are .bashrc
, the configuration file for interactive bash sessions or the .ssh
folder containing configuration files and keys for the SSH client.
See also:
- What are dot-files? on askubuntu.com
- Awesome dotfiles, a curated list of dotfiles resources, and
- https://dotfiles.github.io/, a guide to dotfiles on GitHub.
Typically, output files generated from code runs, such as compiled code, logfiles, in general anything that's temporary and can be re-generated.
For example, the line:
*.log
in a .gitignore
file will tell Git to ignore all files with the extension .log
(the asterisk *
is known as a globbing pattern.
Globbing is the operation that expands a wildcard pattern into the list of pathnames matching the pattern. Matching is defined by:
?
matches any single character.*
matches any string, including the empty string.
For instance, the command
ls myFile?.txt
will return a list of all files such as myFile1.txt
, myFile2.txt
, myFile_.txt
, myFilea.txt
, etc.
and the command
ls *.csv
will return all files with the extension .csv
(e.g. myFile1.csv
, myFile2.csv
, anotherFile.csv
, .csv
, etc.).
Source: glob
Of course, if you have a logfile in your repository to showcase something about logfiles, then you won't want to ignore that file because of it being an integral part of your code release.
The name “glob” is the abbreviation of “global” and it originates from the 1971 Bell Labs' Unix version. Here is the description of /etc/glob
from the Unix Programmer's Manual (K. Thompson and D. M. Ritchie, November 3, 1971 available online):
glob
is used to expand arguments to the shell containing*
or?
. It is passed the argument list containing the metacharacters; glob expands the list and calls the command itself
Traveling back to the 70s, here's a how the original page looked like (source):
From the README file in Git's Git repository:
“git” can mean anything, depending on your mood
But its author Linus Torvalds also claims to have named Git after “git”, the British slang word for “unpleasant person” (as in: “that mean old git”).
Excluding certain files from version control using .gitignore
serves several important purposes:
-
Limiting Storage Footprint: Ignoring unnecessary files helps reduce the size of your repository. This is particularly important for large files, temporary files, or files generated during the build process, which can quickly inflate the size of your repository and consume storage space.
-
Maintaining a Clean Repository: By excluding irrelevant files, you keep your repository clean and focused on the core files essential to your project. This makes it easier to navigate, understand, and collaborate on the codebase without clutter from unrelated files.
-
Improving Performance: Ignoring unnecessary files can improve the performance of Git operations such as cloning, fetching, and pushing. With fewer files to process, these operations can be faster and more efficient, especially for distributed teams or large projects.
-
Facilitating Collaboration: Ignoring editor or IDE-specific files, build artifacts, or dependencies ensures consistency across different development environments. It prevents conflicts and compatibility issues that may arise when collaborators use different tools or configurations.
-
Enhancing Security and Privacy: .gitignore helps prevent sensitive information, such as API keys, passwords, or personal configuration files, from being accidentally committed to the repository. By excluding such files, you reduce the risk of exposing sensitive data to unauthorized users. However, please refer to the following section for caveats.
In summary, excluding files using .gitignore
promotes efficient repository management, improves performance, enhances security, and fosters collaboration by maintaining a focused and clutter-free codebase.
Adding files containing sensitive information to .gitignore
is not a good idea and you should better keep those files in a different location than your Git workspace.
.gitignore
does not provide foolproof protection against unintentional disclosure of confidential data (think of the case that a collaborator deletes the .gitignore
file). To mitigate the risk of disclosing sensitive information it is crucial to implement additional security measures, such as proper access controls, encryption, and regular audits.
Here are some recommended practices for handling sensitive data in a Git repository:
- Avoid Hardcoding Secrets: refrain from hardcoding sensitive information directly into the source code. Use configuration files or environment variables instead. Save a template to your Git repository and the actual secrets locally or in a vault service.
- Security Audits: use tools like
git-secrets
or gitleaks to scan your code for passwords and other sensitive information before committing it to a git repository. - Encrypt Sensitive Files: use
git-crypt
to protect sensitive files by encrypting them when committed.
Adding executables to the .gitignore
file is a common practice in software development for a few reasons:
- Build Process Artifacts: Including executables in version control is redundant, as they can be recreated whenever needed by anyone with the source code and the necessary build tools. Uploading executables to a Git repository defies the essential purpose of version control, which is designed for tracking changes in source code.
- Platform Independence: Executables are platform-specific. Including them in version control can cause issues when team members are using different operating systems. By ignoring executables, you avoid potential conflicts.
- Repository Size and Speed: executables tend to be large files, leading to slower repository operations.
- Security: The presence of executables in a public Git repository can pose security risks and may potentially be exploited in various ways:
- Malicious Code Injection: Executables might be modified to include malicious functionalitys. Users who download and run these modified executables may inadvertently introduce security threats into their systems.
- Information Disclosure: Executables might contain hardcoded credentials or other confidential information.
The gitignore repository on GitHub provides a collection of .gitignore
templates for many programming languages.
If you've ever worked with Latex you will be familiar with the .log
, .aux
, etc. files that can reasonably be excluded from your Git repository. Here's how an excerpt from the Tex
template looks like:
## Core latex/pdflatex auxiliary files:
*.aux
*.lof
*.log
*.lot
*.fls
*.out
*.toc
## Intermediate documents:
*.dvi
*.xdv
*-converted-to.*
# these rules might exclude image files for figures etc.
# *.ps
# *.eps
# *.pdf
## Generated if empty string is given at "Please type another file name for output:"
.pdf
(the .pdf
file at the end with no name, just an extension, is the typical file you would want to delete anyway)
Note: GitHub will propose to choose from its list of .gitignore
templates when creating a new project. Gitlab does not have this option for generic projects but it includes a .gitignore
file in pre-populated template projects.
There's also an online generator of .gitignore
files: gitignore.io. Enter the programming languages used in your repository and the generator will create a .ignore
file for you.
Here are the formatting rules for .gitignore
:
-
a line starting with a hash (
#
) is a comment and is ignored, for example:# this is a comment line
-
blank lines are ignored, and so they can be used as a separators for readability
-
backslash (
\
) is the escape character, so for instance\#
at the beginning of a patterns means that the pattern begins with a hash (\#
undoes the special meaning of the hash character) -
trailing spaces are ignored unless they are quoted with backslash (
\
) -
a prefix
!
negates the pattern thus including files that were excluded by a previous pattern. Of course!
can be escaped with a backslash (\!
) for matching files that begin with an exclamation point. -
the slash (
/
) is the directory separator and it has a different meaning depending on its position in the gitignore search pattern:/
at the end of the pattern means that the pattern only matches directories/
at the beginning or middle (or both) of the pattern means that 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.
-
globbing:
- an asterisk
*
matches anything except a slash (/
) - the character
?
matches any one character except/
- the range notation, e.g.
[a-zA-Z]
, can be used to match one of the characters in a range - two asterisks (
**
) match any number of subdirectories
- an asterisk
Note: %
represents my shell prompt.
Let's say you have in your Git repository a file named myFile1
and a directory myDir
containig three files: myFile1
, myFile2
, myFile3
:
% tree
.
├── myDir
│ ├── myFile1
│ ├── myFile2
│ └── myFile3
└── myFile1
1 directory, 4 files
Assume your .gitignore
file contains the lines:
# skip all files like myFile[0-9]
myFile[0-9]
Then these files will be skipped:
./myFile1
./myDir/myFile1
./myDir/myFile2
./myDir/myFile3
But if .gitignore
file contains the lines:
# skip all files like myFile[0-9] but only if in the root folder
/myFile[0-9]
then only ./myFile1
will be skipped.
When using a negation pattern in .gitignore, you might encounter an unexpected result. For example, consider the following .gitignore
file:
myDir # Ignore the entire myDir folder
!myDir/myFile.txt # Exception: don't ignore myFile.txt
At first glance, you might expect myFile.txt
to be tracked by Git because you've added an exception. However, this won't work as expected. Once Git ignores a folder, it stops scanning its contents, including any files inside, so myFile.txt
will still be ignored.
The reason why the negation pattern has no effect on a file if is parent directory is excluded is that:
Git doesn’t list excluded directories for performance reasons, so any patterns on contained files have no effect, no matter where they are defined.
(from: pattern format in the official documentation).
It's still possible to achieve the desired behavior—ignoring all files in the folder except myFile.txt
—by using a workaround. You can modify your .gitignore
like this:
myDir/* # Ignore all files in myDir
!myDir/myFile.txt # Exception: Track myFile.txt
This pattern ensures that Git ignores everything in myDir
but still checks for myFile.txt
explicitly. With this approach, myFile.txt
will be tracked while the rest of the folder remains ignored.
Git provides a handy utility to debug .gitignore
: git-check-ignore
.
To check whether a path or file is going to be ignored by git
use:
git check-ignore [<options>] <pathname>…
git check-ignore [<options>] --stdin
My .gitignore
file contains one single line, the kind of pattern I want to ignore are all files like myFile1
, myFile2
, ... but only if they are in the root folder.
% cat .gitignore
# skip all files like myFile[0-9] but only if in the root folder
/myFile[0-9]
With the above .gitignore
the file myFile1
in the top directory is skipped:
% git check-ignore -v myFile1
.gitignore:2:/myFile[0-9] myFile1
while myFile1
in myDir
is not (no output)
% git check-ignore -v myDir/myFile1
To get an output also for non-matching files use the option -n
in combination with -v
:
git check-ignore -v -n myDir/myFile1
:: myDir/myFile1
In this case, the file myDir/myFile1
is shown in the output of git check-ignore
but the file won't be ignored by git
as it does not match any pattern in .gitignore
.
Check all files in the Git working directory:
% find . -not -path './.git/*' | git check-ignore --stdin -v
This command will scan all files and folders in your current directory and check whether they're going to be ignored according to the directives in .gitignore
. For files that are going to get ignored the .gitignore
rule is also indicated (line number in .gitignore
) when using the option -v
.
Credit for the find
command: this answer on Stackoverflow (Git command to show which specific files are ignored by .gitignore).
Since .gitignore
does not have a retroactive effect, you need to untrack the files that were tracked before the introduction of .gitignore
.
To tell Git to stop tracking a file (aka ignoring it) you can use git rm --cached
.
See also “How do I make Git forget about a file that was tracked, but is now in .gitignore?”.
The question “How do I make Git forget about a file that was tracked, but is now in .gitignore?” ranks git
(source: data.stackexchange.com) and with a score of
Post Link | Score | tags | creationdate |
---|---|---|---|
How do I undo the most recent local commits in Git? | 26326 | git version-control git-commit undo |
2009-05-29 18:09:14 |
How do I delete a Git branch locally and remotely? | 20387 | git version-control git-branch git-push git-remote |
2010-01-05 01:12:15 |
What is the difference between 'git pull' and 'git fetch'? | 13816 | git version-control git-pull git-fetch |
2008-11-15 09:51:09 |
How can I rename a local Git branch? | 11594 | git version-control git-branch |
2011-07-06 03:20:36 |
How do I undo 'git add' before commit? | 11331 | git undo git-add |
2008-12-07 21:57:46 |
How do I force "git pull" to overwrite local files? | 9635 | git version-control overwrite git-pull git-fetch |
2009-07-14 14:58:15 |
How do I check out a remote Git branch? | 8631 | git git-checkout remote-branch |
2009-11-23 14:23:46 |
How do I make Git forget about a file that was tracked, but is now in .gitignore? | 8241 | git gitignore git-rm |
2009-08-13 19:23:22 |
How do I remove local (untracked) files from the current Git working tree? | 8123 | git branch git-branch |
2008-09-14 09:06:10 |
How to modify existing, unpushed commit messages? | 7648 | git git-commit git-rewrite-history git-amend |
2008-10-07 15:44:47 |
Note: gitignore_tests %
represents my shell prompt.
Remove all files in the directory gitignore_tests
gitignore_tests % rm -rf .git
gitignore_tests % rm -rf .gitignore
Initialize an empty git
repository
gitignore_tests % git init
Initialized empty Git repository in /Users/myUsername/Documents/gitignore_tests/.git/
Create two files in the working directory gitignore_tests
and commit them to the repository
gitignore_tests % touch myFile1 myFile2
gitignore_tests % git add .
gitignore_tests % git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: myFile1
new file: myFile2
gitignore_tests % git commit -m "add myFile1 myFile2"
[master (root-commit) a5bc16b] add myFile1 myFile2
2 files changed, 6 insertions(+)
create mode 100644 myFile1
create mode 100644 myFile2
Now create a .gitignore
file
gitignore_tests % cat << EOF > .gitignore
# skip all myFile0, myFile1
myFile[0-1]
EOF
Note: I used this solution to add text to a file on the command line with here documents.
Add the .gitignore
file to the repository
gitignore_tests % git add .
gitignore_tests % git commit -m "add .gitignore"
[master 13ce4cf] add .gitignore
1 file changed, 2 insertions(+)
create mode 100644 .gitignore
You can see that myFile1
is still being tracked by running ls-files
:
gitignore_tests % git ls-files
.gitignore
myFile1
myFile2
If you change and commit myFile1
, the changes will be tracked by git
.
gitignore_tests % cat << EOF >> myFile1
some text
EOF
gitignore_tests % git add .
gitignore_tests % git commit -m "modify myFile1"
[master 9e8c612] modify myFile1
1 file changed, 1 insertion(+)
To remove myFile1
from the git
index use git rm --cached
gitignore_tests % git rm --cached myFile1
rm 'myFile1'
Now myFile1
does not appear in ls-files
gitignore_tests % git ls-files
.gitignore
myFile2
and it is not tracked by git
anymore
gitignore_tests % git commit -m "untrack myFile1"
[master 7f978a2] untrack myFile1
1 file changed, 7 deletions(-)
delete mode 100644 myFile1
gitignore_tests % cat << EOF >> myFile1
some other text
EOF
gitignore_tests % git add .
gitignore_tests % git commit -m "modify myFile1 again"
On branch master
nothing to commit, working tree clean
gitignore_tests %
Oftentimes one finds the line
.gitignore
in .gitignore
files.
This is not good practice but be aware that it is allowed.
.gitignore
is usually located in Git project's working directory (this is the directory containing the .git
folder, the actual Git repository).
However a .gitignore
file can also be located in any subfolder of the top-level of the working tree. Files to be ignored can also be specified as options from the command line. Patterns from different .gitignore
files are taken into account by git
following some rules of precedence.
.gitignore
files that are not in the root directory of your Git project have an “ignore” effect on the folders in which they're located and on their subfolders.
From the .gitignore
documentation:
Each line in a gitignore file specifies a pattern. When deciding whether to ignore a path, Git normally checks gitignore patterns from multiple sources, with the following order of precedence, from highest to lowest (within one level of precedence, the last matching pattern decides the outcome):
Patterns read from the command line for those commands that support them.
Patterns read from a
.gitignore
file in the same directory as the path, or in any parent directory (up to the top-level of the working tree), with patterns in the higher level files being overridden by those in lower level files down to the directory containing the file. These patterns match relative to the location of the.gitignore
file. A project normally includes such.gitignore
files in its repository, containing patterns for files generated as part of the project build.Patterns read from
$GIT_DIR/info/exclude
.Patterns read from the file specified by the configuration variable
core.excludesFile
.
.gitkeep
is a dummy file used in empty directories to trick Git into tracking them. Git would otherwise not add empty directories.
While .gitkeep
is a common choice, any other file name can be used to keep empty directories tracked. Some prefer to use the file .keep
or even an empty .gitignore
(or containing the line !.gitignore
to prevent it from being ignored in case already untracked).
See also: What are the differences between .gitignore and .gitkeep? and How do I add an empty directory to a Git repository?.
Git tracks content rather than the directory structure itself, meaning that it tracks and identifies files based on their content, not their file names or locations. The directory structure is recorded separately, but empty folders are ignored because they contain no content to track.
This approach makes sense not only from a performance perspective—since empty folders could add unnecessary overhead—but also conceptually, as Git is designed to track meaningful content changes. An empty folder doesn’t represent a significant change in version control, so it’s excluded.
To clarify further, Git's storage system consists of two components: a content-addressable mechanism for storing file data and a structure that tracks the directory hierarchy.
Before being stored, files are hashed by computing a cryptographic hash (SHA-1) of the file's content. This hash is a unique 40-character string that acts as an identifier for the file in Git’s internal repository. Even if the file is renamed or moved, as long as the content remains the same, its hash will stay the same. This system makes Git very efficient when handling identical content across different versions of files, even if the file names change or the file is moved in the directory structure.
A file is saved as blob object (raw content of the file without any metadata like file name or location) and, since files are indexed by their hashes, if two files have the same content, Git only stores one blob and references it in multiple places.
Git does track the directory structure of files, but it does so in a separate step from the content. While file content is stored as blob object, the file names and directory hierarchy are stored using tree objects.
A tree object represents a directory. It contains references (pointers) to other objects in the repository, such as:
- blobs (which represent file content)
- other tree objects (which represent subdirectories)
A thorough description of Git's internal data representation and storage mechanisms can be found in 'Git from the Bottom Up'.
Let’s say you have a project with this structure:
project/
├── docs/ (empty folder)
├── src/
│ └── main.c
├── README.md
└── hello_file
- The
README.md
file’s content is stored in a blob object with a SHA-1 hash, for example,aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d
. - If the content of
hello_file
is different from that ofREADME.md
, it is stored in a separate blob object with its own unique SHA-1 hash. Ifhello_file
has the same content asREADME.md
, Git will reference the existing blob object instead of creating a new one. - The
src/
directory is represented as a tree object, which references the blob formain.c
. - The root
project/
directory is another tree object, which references the blob(s) forREADME.md
andhello_file
, as well as the tree forsrc/
. - The
docs/
folder is ignored by Git because it is empty.
To track the docs/
folder, you would typically add a placeholder file like .gitkeep
:
project/
├── docs/
│ └── .gitkeep (placeholder file to track empty folder)
├── src/
│ └── main.c
├── README.md
└── hello_file
By adding .gitkeep
, Git now recognizes and tracks the docs/
directory.
The use of emojis in software development and documentation has emerged as a playful yet effective way to enhance communication and clarity. In the Git world, emojis are referred to as gitmojis, and there is an initiative (https://gitmoji.dev/) aimed at standardizing their use to represent various types of commits.
Based on the Gitmoji convention, the correct emoji for committing changes to a .gitignore
file is: 🙈 (:see_no_evil:). A commit message would look like:
🙈 Add .gitignore
No, Git's .gitignore
files were not the first to introduce ignore functionality and glob patterns. These features already existed in other version control systems before Git. For example:
- Mercurial, another popular distributed version control system, introduced the
.hgignore
file for specifying patterns to ignore files and directories. Mercurial's ignore file serves a similar purpose to Git's.gitignore
and also uses glob patterns. - Subversion (SVN), a centralized version control system, has the
svn:ignore
property that allows users to specify patterns to ignore files and directories within the repository.
Git's implementation of the .gitignore
file has become widely known and adopted due to Git's popularity and widespread use in software development.
Ignore files play a crucial role in managing a project's source code or other assets by keeping repositories clean and focused on relevant files.
Here's a list of of common ignore files used in different contexts other than Git:
.hgignore
: Used in Mercurial repositories for specifying patterns to ignore files and directories..npmignore
: Used in Node.js projects to specify files and directories that should be ignored when publishing packages to the npm registry..dockerignore
: Used in Docker projects to specify files and directories that should be excluded from the Docker build context when creating Docker images..eslintignore
: Used in projects using ESLint to specify files and directories that should be ignored during linting..prettierignore
: Used in projects using Prettier to specify files and directories that should not be formatted by Prettier..babelignore
: Used in projects using Babel to specify files and directories that should not be transpiled by Babel ("transpiling" means converting modern JavaScript code into older versions that are compatible with a wider range of browsers or environments)..rsyncignore
: Used with the rsync utility to specify files and directories that should be excluded from synchronization.
You can use GitHub's advanced search to discover a variety of ignore files for inspiration.
You can search by path
or there's even a programming language called “Ignore List”
As an aside, I'd like to mention that I find GitHub's search feature (https://github.com/search) very helpful for discovering interesting repositories or code snippets.
- Ignoring files A quick start guide on how to configure Git to ignore files you don't want to check in to GitHub.
- gitignore by the Git community
- Pro Git book, by Scott Chacon and Ben Straub available online. A thorough and accessible guide to Git, covering everything from the basics to advanced usage.
- Ignore files that have already been committed to a Git repository
- How do I make Git forget about a file that was tracked, but is now in .gitignore?
- .gitignore A clearly presented, well-structured, and easily comprehensible tutorial on
gitignore
by Atlassian. - Git from the bottom up A comprehensive guide that delves into the inner workings and architecture of Git, explaining how Git operates at a fundamental level and how its core principles influence its behavior.
- Multiple .gitignore in subfolders
- Don't ignore .gitignore
- Learning All about GitIgnore : Ignoring Files and Folders
- Git command to show which specific files are ignored by .gitignore
- git-check-ignore Documentation on the
git-check-ignore
tool for debugging.gitignore
.