A .gitignore
file prevents certain files and directories from being tracked by Git. This file helps developers exclude temporary, auxiliary, or sensitive data from being included in the repository, thereby keeping the repository clean, lightweight, and secure.
How a .gitignore file works
At its core, the .gitignore
file operates through pattern matching. When Git processes the working directory for changes, it cross-references the list of files and directories against patterns defined in .gitignore
files. Here’s a deeper look at how it functions technically:
Pattern matching
Wildcard Support:
.gitignore
supports wildcards such as*
for matching any sequence of characters,?
for a single character, and[...]
for a set of characters. For instance,*.log
ignores all files with the.log
extension.Directory ignoring: By appending a slash (
/
) to the end of a pattern, Git specifically treats it as a directory. Without the slash, the pattern can match both files and directories.Negation: A pattern prefixed with
!
negates a match, allowing files that would otherwise be ignored to be included. For example,!important.log
would track a file namedimportant.log
even if.log
files are ignored.
Hierarchical and global .gitignore
files
Local
.gitignore
: Each repository can have its own.gitignore
file at the root level or additional.gitignore
files in subdirectories. The patterns in these files apply to the directory where the file is located and its subdirectories. To prevent unexpected behavior it’s recommended to have one.gitignore
file per repository.Global
.gitignore
: Users can also define a global.gitignore
file for all their repositories on a particular system, configured via thegit config
command. This is particularly useful for ignoring system-specific files (e.g.,.DS_Store
on macOS) across all projects.
Under the hood
When Git prepares to create a new commit, it looks at the working directory and stages changes. Here's what happens under the hood regarding .gitignore
:
Scanning: Git scans the working directory for changes. This includes new files, modified files, and deleted files compared to the last commit.
Filtering: Before staging changes, Git filters the list of changed files through the
.gitignore
patterns. If a file matches a pattern, Git ignores it, meaning it won't stage, commit, or even display the file as untracked. This filtering happens every time Git scans for changes, ensuring that ignored files remain out of the version control system.Priority and order: Git applies
.gitignore
rules from the top of a file to the bottom. Patterns defined in a.gitignore
file in a subdirectory take precedence over patterns in a higher directory or the root. Likewise, the global.gitignore
provides a base set of ignore rules that can be overridden by local.gitignore
files.
Efficiency
Internally, Git optimizes the handling of ignored files and directories for efficiency. When a directory is ignored, Git completely skips scanning it for changes, which can significantly speed up Git's operations in projects with large numbers of ignored files (e.g., node modules, and build artifacts).
The .gitignore
file functions as a filter for Git's tracking system, using pattern matching to exclude specified files and directories from version control. This system allows developers to maintain clean repositories by excluding non-essential or sensitive data from being tracked and shared.
Utilizing a .gitignore
file ensures your repository remains clean and only relevant files are tracked. Here are some best practices:
1. Commit .gitignore
early
Commit a .gitignore
file as early as possible in your project's lifecycle—ideally, as part of your initial commit. This helps to prevent accidentally committing unwanted files to the repository.
2. Customize .gitignore
for your project
While there are common patterns and files to ignore across many projects (e.g., log files, temporary files), each project might have specific needs. Customize your .gitignore
file based on the languages, frameworks, and tools you use.
3. Use comments for clarity
Use comments in your .gitignore
file (lines starting with #
) to explain why certain files or directories are ignored. This practice is particularly helpful for new team members or when revisiting a project after some time.
4. Leverage global .gitignore
for personal files
Use a global .gitignore
file for ignoring files specific to your development environment (like editor backup files, operating system files, etc.), so you don’t need to pollute project .gitignore
files with personal preferences.
5. Avoid ignoring files retroactively
If a file has already been committed to your Git repository, simply adding it to .gitignore
will not remove it from the repository or its history. You need to explicitly remove the file from the repository, commit that change, and then ensure it's ignored moving forward. Here’s how to retroactively ignore files that have already been committed:
Step 1: Add the file to .gitignore
First, make sure to add the filename or pattern matching the file you want to ignore to the .gitignore
file in the root of your repository. This step prevents the file from being tracked in future operations once it's removed.
# Example .gitignore entrypath/to/your/file.txt
Step 2: Remove the file from the repository
Use the git rm --cached
command to remove the file from the repository while keeping it in your working directory. The --cached
option is crucial because it only removes the file from the index (staging area) and not your local file system.
git rm --cached path/to/your/file.txt
If the file is located in multiple places or you're removing all files of a certain type, you might use wildcards. For example, to remove all .log
files:
git rm --cached *.log
Or, if you need to remove a directory (and its contents) from the repository but keep it locally, use the -r
flag:
git rm --cached -r path/to/your/directory/
Step 3: Commit the change
After removing the file(s) from the repository, you need to commit this change. This action records the removal of the file from the tracked content of the repository.
git commit -m "Remove file.txt from the repository"
Step 4: Push your changes
Finally, push your changes to the remote repository to ensure that the file removal is reflected in the remote repository as well.
git push origin your_branch_name
Additional notes
Historical data: Removing a file like this will prevent it from being tracked in future commits, but it does not alter the history. Previous commits will still contain the file. If you need to remove the file from the entire history of the repository (for example, if it contains sensitive data), you will need to use more complex techniques like
git filter-branch
,git rebase
, or the BFG Repo-Cleaner tool.Collaborators: If you're working with a team, inform your collaborators about the change. They'll need to pull the latest commits to sync their local repositories with the remote repository.
6. Should you commit .gitignore
in your repository?
Yes, you should commit the .gitignore
file to your remote repository. This ensures that all contributors to the project are on the same page regarding which files should not be tracked by Git.
Is .gitignore
generated by default?
No, .gitignore
is not generated by default when you create a new Git repository. However, many project templates, frameworks, and platforms (like GitHub when creating a new repository via the web interface) offer to include a .gitignore
file tailored for specific project types.
Example of a standard .gitignore
file
Below is an example of a .gitignore
file for a python project. It includes common directories and files that should not be tracked by Git in most python projects:
# Byte-compiled / optimized / DLL files__pycache__/*.py[cod]*$py.class# C extensions*.so# Distribution / packaging.Pythonbuild/develop-eggs/dist/downloads/eggs/.eggs/lib/lib64/parts/sdist/var/wheels/pip-wheel-metadata/share/python-wheels/*.egg-info/.installed.cfg*.egg# PyInstaller# Usually these files are written by a python script from a template# before PyInstaller builds the exe, so as to inject date/other infos into it.*.manifest*.spec# Installer logspip-log.txtpip-delete-this-directory.txt# Unit test / coverage reportshtmlcov/.tox/.nox/.coverage.coverage.*.cachenosetests.xmlcoverage.xml*.cover.hypothesis/.pytest_cache/pytestdebug.log# Translations*.mo*.pot# Django stuff:*.loglocal_settings.pydb.sqlite3db.sqlite3-journal# Flask stuff:instance/.webassets-cache# Scrapy stuff:.scrapy# Sphinx documentationdocs/_build/
This .gitignore
file example illustrates patterns for ignoring Python bytecode files, distribution packages, coverage reports, and various configuration files that should not be tracked in a Python-based project's Git repository.
If you are having problems with your .gitignore file not behaving the way you expect it to, see this guide on troubleshooting .gitignore files.
For further reading about the intricacies of the .gitignore file see the official git documentation.