Most developers know one way to keep a file out of Git: add it to .gitignore. That works until you need to ignore something that is only yours, like an editor folder or a scratch notes file, and you do not want to commit a line about it to a shared repository. Git actually has three places for ignore rules, each for a different audience. Picking the right one keeps your project's .gitignore clean and stops your personal clutter from leaking into every pull request. I ran every command below on Git 2.43.0.
The three ways at a glance
| Method | Where it lives | Shared with team? | Applies to | Use it for |
|---|---|---|---|---|
.gitignore |
Inside the repo (committed) | Yes | That repo (and subfolders) | Build output, dependencies, secrets: things nobody should commit |
.git/info/exclude |
Inside the repo's .git folder |
No | That repo only | Your personal files in one project |
| Global excludes file | Your home directory | No | Every repo on your machine | Editor and OS junk |
That split matches how the official gitignore documentation describes them: patterns that "all developers will want to ignore" belong in .gitignore, patterns "specific to a particular repository but which do not need to be shared" belong in info/exclude, and patterns a user wants ignored "in all situations", such as editor backup files, belong in the file named by core.excludesFile.
1. .gitignore: rules for the whole team
Create a file called .gitignore in the repository root and commit it. A small example for a Laravel project:
.env
/vendor/
/node_modules/
/public/build/
*.log
In my test repo, with .env, build/ and *.log listed, git status stopped showing all three, and ordinary source files were untouched. You can check which rule matched a file with git check-ignore -v:
$ git check-ignore -v .env storage/logs/app.log build/out.js
.gitignore:1:.env .env
.gitignore:3:*.log storage/logs/app.log
.gitignore:2:build/ build/out.js
The output is file:line:pattern, then the path. A file that is not ignored prints nothing and the command exits with status 1, which makes it easy to use in scripts. A .gitignore in a subfolder applies only to that folder and below (I confirmed that *.dat in sub/.gitignore ignored sub/a.dat but not a.dat in the root).
2. .git/info/exclude: private rules for one repo
This file uses exactly the same syntax, but it is never committed or pushed, because it lives inside .git:
echo "notes.txt" >> .git/info/exclude
git status --short # notes.txt is gone from the list
git check-ignore -v notes.txt
# .git/info/exclude:7:notes.txt notes.txt
Use it for a local scratch file, a personal script, or a config you need in one project but that has nothing to do with anyone else. Nobody else has to know it exists. One limit: it is not copied by git clone, so a fresh clone starts without it.
3. Global excludes file: your editor and OS junk, everywhere
Files like .idea/, .vscode/ and .DS_Store come from your tools, not from the project. Put them in one file and tell Git about it once:
printf '.idea/\n.vscode/\n.DS_Store\n' > ~/.gitignore_global
git config --global core.excludesFile ~/.gitignore_global
In the test, that made .idea/w.xml ignored in a repo that had no mention of .idea anywhere. If you never set core.excludesFile, Git also reads ~/.config/git/ignore by default (the documentation gives $XDG_CONFIG_HOME/git/ignore, falling back to $HOME/.config/git/ignore), and I confirmed that file was picked up without any config.
If two sources disagree, the closest one wins. In my test, a global rule ignored *.tmp while the repo's .gitignore contained !*.tmp, and the file showed up in git status. The documented order, highest to lowest, is command-line patterns, then .gitignore files, then info/exclude, then core.excludesFile.
The gotcha that catches everyone: already-tracked files
Adding a file to .gitignore does not make Git forget it. The documentation says it plainly: "Files already tracked by Git are not affected." I tracked .env, then ignored it, then edited it. git status still reported M .env, and git check-ignore -v .env printed nothing. To see what the rule would do, add --no-index:
git check-ignore -v --no-index .env
# .gitignore:1:.env .env
To stop tracking the file without deleting it from disk:
git rm --cached .env
git commit -m "Stop tracking .env"
After that, the file stayed on my disk, and further edits no longer showed up in git status. Be aware of what this does not do: the old content is still in every earlier commit. If the file held real credentials, rotate them. Removing a secret from history is a separate job.
Pattern mistakes worth knowing
-
Leading slash anchors to the root.
buildmatched bothbuild/out.jsandsrc/build/xin my test./buildmatched only the top-level one. -
Trailing slash means directories only.
build/ignores a folder named build but not a file with that name. -
You cannot re-include a file inside an ignored folder. With
build/followed by!build/keep.txt,keep.txtstayed ignored. The documentation explains why: "It is not possible to re-include a file if a parent directory of that file is excluded." Ignore the folder's contents instead, withbuild/*then!build/keep.txt. That version madebuild/keep.txtappear in the status output whilebuild/out.jsstayed ignored. -
Forcing a file in.
git add fileon an ignored path fails with a hint;git add -f fileadds it anyway. Use that rarely.
Handy commands
git status --ignored # show ignored files too
git ls-files -o -i --exclude-standard # list ignored files that exist on disk
git check-ignore -v path/to/file # which rule matched?
Frequently asked questions
Should .env go in .gitignore?
Yes, along with any file holding credentials. Commit a .env.example with placeholder values so new developers know what to set.
Why is my .gitignore rule not working?
Almost always because the file is already tracked. Run git ls-files path/to/file. If it prints the path, use git rm --cached as shown above.
Can I ignore a file only on my machine without touching the repo?
Yes, that is what .git/info/exclude is for. For files you want ignored in every project, use the global excludes file.
References
Originally published on DEV Talk.
Top comments (0)