By admin , 28 April 2026

Introduction

A .gitignore file lists patterns Git should treat as untracked and never offer to add. It keeps build artifacts, secrets, OS metadata, and editor scratch files out of your repository.

Pattern syntax

  • name: matches files or directories named name at any depth.
  • /name: matches only at the repository root.
  • name/: matches directories only.
  • *.log: glob; matches any .log file.
  • **/build: matches build at any depth.
  • !important.log: re-include a previously ignored path.
  • # at the start of a line is a comment.

A realistic example

# .gitignore
# Build output
/dist/
/build/
*.o
*.pyc

# Dependencies
/node_modules/
/vendor/

# Editors
.idea/
.vscode/
*.swp

# OS
.DS_Store
Thumbs.db

# Secrets
.env
*.pem

Multiple .gitignore files

You can place a .gitignore at any level. Patterns are relative to the file's directory, and deeper files override shallower ones. There is also a global ignore file for your personal preferences:

git config --global core.excludesFile ~/.gitignore_global

Use that for editor swap files and OS junk, not project-specific paths.

Tracking already-tracked files

Adding a path to .gitignore does not remove it from the repository. Untrack a file while keeping it on disk:

git rm --cached secrets.env
git commit -m "Stop tracking secrets.env"

Debugging ignores

Why is Git ignoring (or not ignoring) a path? Ask:

git check-ignore -v path/to/file

The output names the file and line that matched.

Order of evaluation

Git evaluates ignore rules from several sources, in this order: command-line arguments, then per-directory .gitignore files (deepest first), then $GIT_DIR/info/exclude, and finally the global core.excludesFile. Later (more specific) rules override earlier ones, and a negation (!pattern) only re-includes a path if its parent directory is not itself excluded. This last rule trips many users:

# Wrong: cannot un-ignore a file in an ignored directory
/build/
!/build/keep.log

# Right: ignore contents but keep the directory in
/build/*
!/build/keep.log

Per-repo and global excludes

Two extra ignore mechanisms complement .gitignore. The per-repo, untracked file .git/info/exclude applies only to your clone (good for IDE files you do not want to commit to .gitignore for the whole team). The global core.excludesFile applies to all your repos:

echo ".idea/" >> .git/info/exclude
git config --global core.excludesFile ~/.gitignore_global
echo ".DS_Store" >> ~/.gitignore_global

Use these for personal preferences; reserve committed .gitignore for team-wide rules.

Common mistakes

Adding secrets.env to .gitignore after it has already been committed and assuming the secret is gone. It is still in history; rotate the secret and consider using git filter-repo to scrub it. Another trap: ignoring a directory with build when you meant /build, accidentally suppressing a file named build deep in the tree. Finally, committing .gitignore itself is essential; without it, every collaborator must invent their own ignore rules. .gitignore belongs in the repository, not outside it.