Gitignore Not Working? 7 Causes and How to Fix Each

.gitignore almost always "stops working" because the file is already tracked: ignore rules only apply to untracked files, so Git keeps tracking anything you committed before adding the rule. Run git rm --cached <file> (or git rm -r --cached <folder>) and commit, and the rule takes effect from then on.

git rm --cached .env
git rm -r --cached logs
git commit -m "Stop tracking .env and logs"

Your files stay on disk; only Git's record of them is removed. If that isn't the cause, git check-ignore -v tells you exactly which rule applies to a path, and the sections below go through each failure in order of how often it happens.

How to see why a file is or isn't ignored

git check-ignore -v prints the file, line number and pattern that matched:

git check-ignore -v logs/app.log

Output from a test repository on Git 2.45:

.gitignore:2:logs/	logs/app.log

The format is source:line:pattern, a tab, then the path. No output and exit code 1 means no rule ignores that path. The source can also be .git/info/exclude or your global ignore file, which is useful when a file is ignored and you can't find the rule in .gitignore.

There is one catch: check-ignore skips tracked files, because the ignore rules don't apply to them. Add --no-index to test the pattern anyway. That difference is itself the diagnosis for cause 1.

1. The file was committed before you ignored it

The gitignore documentation states it in its first paragraph: "Files already tracked by Git are not affected". This is what it looks like. In a test repository where .env was committed first and ignored afterwards, editing it still shows a modification:

$ git status --short
 M .env
?? .gitignore

$ git check-ignore -v .env
$ echo $?
1

$ git check-ignore -v --no-index .env
.gitignore:1:.env	.env

The pattern is correct (--no-index finds it), but because .env is tracked, Git ignores the rule. The documented fix is to remove it from the index with git rm --cached:

$ git rm --cached .env
rm '.env'
$ git commit -m "Stop tracking .env"

After that commit, further edits to .env no longer appear in git status. Two warnings. The commit deletes the file for everyone who pulls it, so tell your team, and if it held a secret, the old commits still contain it: rotate the credential.

For a whole folder use -r. To untrack everything that the current rules ignore in one pass, the usual sequence is to empty the index and re-add:

git rm -r --cached .
git add .
git commit -m "Apply .gitignore"

Run git status before committing to check the list of deletions is what you expected.

2. A parent folder is ignored, so ! can't re-include a file

dist/
!dist/keep.txt

This does not keep dist/keep.txt. The documentation says: "It is not possible to re-include a file if a parent directory of that file is excluded." Git stops looking inside an excluded directory, so the negation is never checked. In a test repository git check-ignore -v dist/keep.txt returns .gitignore:4:dist/, the folder rule, not the negation. Ignore the folder's contents instead of the folder:

dist/*
!dist/keep.txt

3. The pattern doesn't match the path you think

Slashes change what a pattern matches. From the gitignore rules:

PatternMatches
buildA file or folder named build at any depth
build/Only folders named build, at any depth
/buildOnly build next to this .gitignore
docs/buildOnly docs/build relative to this .gitignore (a slash in the middle anchors it)
**/buildbuild anywhere, same as build
docs/**/*.pdfPDFs anywhere under docs, including docs itself

The rule that surprises people is the middle slash: config/local.json only matches at the level of the .gitignore file that contains it. Patterns are also relative to the .gitignore they are in, so a rule in src/.gitignore can't match files outside src.

4. The .gitignore is saved as UTF-16 (Windows PowerShell)

In Windows PowerShell 5.1, Microsoft's encoding reference says Out-File and the redirection operators > and >> create UTF-16LE files. So this common one-liner writes a file Git can't read as patterns:

"*.log" > .gitignore

Reproduced in Windows PowerShell 5.1 with Out-File -Encoding unicode (UTF-16LE, the 5.1 default): git status still listed debug.log as untracked, and git check-ignore returned exit code 1. Rewriting the same line as ASCII made the rule work immediately. Fix the file with an explicit encoding, or create it in your editor:

"*.log" | Out-File .gitignore -Encoding ascii

PowerShell 7 and later default to UTF-8 without a BOM, so this only affects the older built-in Windows PowerShell.

5. The file has the wrong name or isn't where you think

  • The name must be exactly .gitignore. Check it wasn't saved as .gitignore.txt: with file extensions hidden in Windows Explorer the two look identical. dir /a or ls -a shows the real name.
  • Rules apply to the folder containing the .gitignore and below. A .gitignore in a subfolder doesn't affect the repository root.
  • A .gitignore in a different repository, for example a parent folder that is not part of this repo, has no effect.

6. A later rule overrides an earlier one

Within one file, the last matching pattern wins. *.log followed later by !*.log leaves logs unignored. Across files, the documented precedence from highest to lowest is: patterns on the command line, the .gitignore in the same directory or a parent (deeper files win), .git/info/exclude, then the global file set by core.excludesFile (default ~/.config/git/ignore). git check-ignore -v shows which one actually decided.

7. A stray character in the pattern

Trailing spaces are not the problem many guides suggest: the documentation says they are ignored unless escaped with a backslash, and a test with build.txt (trailing space) still ignored build.txt. Characters that do matter are a leading # (the line becomes a comment; write \#file to match a name starting with #), a leading ! (negation; write \!file), and a backslash used as a Windows path separator. Patterns always use forward slashes, even on Windows.

If it still fails

Run both checks on the exact path and compare:

git check-ignore -v path/to/file
git check-ignore -v --no-index path/to/file
git ls-files path/to/file

If git ls-files prints the path, the file is tracked: go back to cause 1. If neither check-ignore command prints anything, no rule matches: fix the pattern. If you need to stop tracking something you already pushed and want to undo the commit that added it instead, see how to undo the last commit.

Frequently asked questions

Does git rm --cached delete my file?
No. git rm --cached removes the file from Git's index only, so it stops being tracked while the copy on your disk stays exactly as it is. The next commit records the file as deleted from the repository, which means other people who pull that commit will have the file removed from their working folder.
I committed a password or API key. Is adding it to .gitignore enough?
No. .gitignore only affects future additions, and git rm --cached only stops tracking from the next commit onward. The secret is still in every earlier commit and in any clone or fork that already has them. Revoke or rotate the credential first, then rewrite history if you need it gone from the repository.
How do I ignore a file only on my machine without editing .gitignore?
Add the pattern to .git/info/exclude in your repository. It uses the same syntax as .gitignore but is never committed, so it affects only your clone. For patterns you want in every repository, such as editor files, put them in the global ignore file, which defaults to ~/.config/git/ignore.
Why does git add -f work on an ignored file?
The -f (force) flag tells git add to add a file even when it matches an ignore rule. Once added, the file is tracked, and from then on .gitignore no longer applies to it, which is exactly the situation in cause 1 of this article.

Sources