.gitignore
Beginner Git CLI VS Code UI IntelliJ UI GitLab UI GitHub UI
Why this matters
git add . is convenient and dangerous: it stages every untracked file, including the 200 MB of installed packages, the editor's settings folder, the file with your API key. .gitignore is the list of things Git must never offer for staging. A good one makes git status quiet and honest; a missing one is how secrets and junk enter history.
What to ignore
| Category | Examples | Why |
|---|---|---|
| Generated files | build/, dist/, __pycache__/, *.pyc, *.class, node_modules/ |
Regenerated from source; huge; change on every build |
| Dependencies | node_modules/, .venv/, vendor/ |
Installed by the package manager from the manifest |
| Secrets and local configuration | .env, *.pem, secrets.yml, config.local.* |
Must never leave your machine |
| Editor and OS noise | .vscode/, .idea/, .DS_Store, Thumbs.db, *.swp |
Personal, not part of the project |
| Logs and temporary files | *.log, tmp/, *.tmp |
Noise |
Never ignore: source, tests, documentation, data the project needs, manifests, lock files, the pipeline definition, .gitignore itself.
The file
.gitignore is a plain text file at the root of the repository (extra ones can sit in subfolders). One pattern per line; # starts a comment. The playground's:
# Python
__pycache__/
*.pyc
.venv/
# Secrets and local settings — never commit these
.env
*.local
# Editors and operating systems
.DS_Store
Thumbs.db
.idea/
.vscode/The six pattern shapes that cover almost every case:
| Pattern | Matches |
|---|---|
name |
any file or folder called name, in any folder |
name/ |
only folders called name (and everything inside) |
*.log |
any file ending in .log, in any folder |
/build |
only build at the root of the repository |
docs/*.tmp |
.tmp files directly inside docs |
!keep.log |
an exception: do not ignore this even if an earlier pattern matches |
Patterns apply to untracked files only; a file already in history is unaffected (see the trap below).
Templates
Every language has a standard list. GitLab and GitHub both offer templates when creating a project (.gitignore template dropdown), and github.com/github/gitignore is the shared source: Python.gitignore, Node.gitignore and so on. Start from the template for your language and add the secrets and editor lines.
Check whether a file is ignored
$ git check-ignore -v .env src/__pycache__/trailguide.cpython-313.pyc.gitignore:7:.env .env
.gitignore:2:__pycache__/ src/__pycache__/trailguide.cpython-313.pycEach line names the file, the line number and the pattern that matched. No output means "not ignored". git status --ignored lists all ignored files at the end of the status:
$ git status --ignoredIgnored files:
(use "git add -f <file>..." to include in what will be committed)
.env
src/__pycache__/
nothing to commit, working tree cleanAnd if you try to add an ignored file, Git refuses and tells you why:
$ git add .envThe following paths are ignored by one of your .gitignore files:
.env
hint: Use -f if you really want to add them.
hint: Disable this message with "git config set advice.addIgnoredFile false"Take that refusal as a gift. -f exists for the rare legitimate case; a secret is never one.
The trap: already tracked
Ignore rules do not apply to files Git already tracks. If todo.txt was committed and you then add it to .gitignore, it keeps showing as modified. Untrack it once, keeping the file on disk:
$ git rm --cached todo.txtrm 'todo.txt'$ git status -s M .gitignore
D todo.txtCommit both: the file is removed from history going forward, stays on your disk, and the pattern keeps it out from now on. (The file remains in old commits; for secrets that is not enough — lesson 8.7.)
A global ignore for your own noise
Editor folders and .DS_Store are about your machine, not the project. Keep them in a personal, global ignore file so that every repository ignores them, even ones whose .gitignore forgot:
$ git config --global core.excludesFile ~/.gitignore_global
$ printf '.DS_Store\nThumbs.db\n.idea/\n.vscode/\n*.swp\n' > ~/.gitignore_globalProject-specific ignores still belong in the project's .gitignore, so colleagues get them too.
In the tools
Edit .gitignore with any editor, then git status to confirm the file disappeared from the list, git check-ignore -v <file> to see which rule matched.
Right-click an untracked file in Source Control → Add to .gitignore appends its path to the file. Ignored files appear greyed out in the Explorer. Install the gitignore extension to pull language templates from the shared collection.
Right-click a file in the Commit window's Unversioned Files → Ignore (or Add to .gitignore), which offers to ignore the file, its extension, or its folder. IntelliJ's own .idea/ folder: ignore it unless your team deliberately shares run configurations.
When creating a project, choose a .gitignore template to start with the right language list. Afterwards, .gitignore is a normal file: edit it in the repository view like any other, with a commit and (on protected branches) a merge request.
Same: the Add .gitignore dropdown on the new repository page, and ordinary file editing afterwards. Files that are ignored never appear on GitHub because they were never pushed.
Common mistakes
- Ignoring after committing. Untrack with
git rm --cachedonce. - Ignoring too much. A pattern like
*.mdin a docs project hides the documentation. Test withgit check-ignore -v. - Putting personal editor folders into the project's
.gitignoreand nothing else. Fine, but the global ignore file protects you everywhere. - Committing
.env"just for now". Git remembers forever. Use.env.example.
Try it yourself
Goal: add an ignore rule, prove it works, and fix a file that was committed before being ignored.
- In the playground, create
tmp/scratch.log(make the folder first) and rungit status -s. - Append
*.logto.gitignore; rungit status -sandgit check-ignore -v tmp/scratch.log. - Create
todo.txt, add and commit it; then addtodo.txtto.gitignore, edit the file, and rungit status -s. - Untrack it with
git rm --cached todo.txt, rungit status -s, and commit.
Expected result: step 2 shows only M .gitignore and check-ignore names the *.log rule; step 3 shows todo.txt still modified despite the rule; after step 4 it shows D todo.txt staged and the file still exists on disk.
Show solution
The .log file vanished from status as soon as the pattern existed because it was untracked. todo.txt was tracked, so the pattern did nothing until git rm --cached removed it from the index. After the commit, ls todo.txt still finds the file; Git simply no longer looks at it.