Live streamJoin us for "State of code review 2024" on July 18thRegister today

How to use a .gitignore file

Greg Foster
Greg Foster
Graphite software engineer


Note

This guide explains this concept in vanilla Git. For Graphite documentation, see our CLI docs.


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.

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:

  • 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 named important.log even if .log files are ignored.

  • 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 the git config command. This is particularly useful for ignoring system-specific files (e.g., .DS_Store on macOS) across all projects.

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:

  1. Scanning: Git scans the working directory for changes. This includes new files, modified files, and deleted files compared to the last commit.

  2. 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.

  3. 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.

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:

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.

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.

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.

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.

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:

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.

Terminal
# Example .gitignore entry
path/to/your/file.txt

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.

Terminal
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:

Terminal
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:

Terminal
git rm --cached -r path/to/your/directory/

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.

Terminal
git commit -m "Remove file.txt from the repository"

Finally, push your changes to the remote repository to ensure that the file removal is reflected in the remote repository as well.

Terminal
git push origin your_branch_name
  • 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.

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.

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.

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:

Terminal
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
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 logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
pytestdebug.log
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_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.

Give your PR workflow
an upgrade today

Stack easier | Ship smaller | Review quicker

Or install our CLI.
Product Screenshot 1
Product Screenshot 2