Gitignore Not Working? Check Tracked Files Before git rm --cached
If .gitignore contains your file but Git still reports changes to it, check whether the file is already tracked. An ignore rule does not remove a path from Git’s index. For a tracked file, git check-ignore can also print nothing even when the rule matches.
Use git ls-files to check tracking, then git check-ignore --no-index to inspect the rule independently. If the file should become local-only, remove that exact path from the index with git rm --cached and commit the removal with the ignore rule. Your local copy stays; a teammate pulling the commit can lose their previously tracked copy.
Separate tracking from rule matching
Run these commands from the repository root, replacing local.env with the actual relative path:
git ls-files -- local.envgit check-ignore -v -- local.envgit check-ignore -v --no-index -- local.envThe commands answer different questions. Git’s ignore reference limits ignore rules to untracked files. The check-ignore manual explains that the default check skips tracked files, while --no-index bypasses index membership for the check.
With a tracked local.env and the rule /local.env, the first command prints the path, the second prints nothing, and the third identifies the rule:
.gitignore:1:/local.env local.envThe gap is diagnostic: the pattern matches, but the file is tracked. Rewriting the same pattern will not change that state.
| Result | What to check next |
|---|---|
ls-files lists the path; --no-index shows an excluding rule | Stop tracking the path if it should be local-only |
ls-files lists the path; neither rule check shows a match | Add or correct the ignore rule before untracking |
ls-files is empty; ordinary check-ignore shows an excluding rule | The path is already untracked and ignored |
A verbose result shows a pattern beginning with ! | That matching rule re-includes the path; inspect rule order |
A verbose match alone is not proof of exclusion: negated patterns begin with !. To find other tracked paths that match ignore rules, use the read-only inventory documented by git ls-files:
git ls-files -ci --exclude-standardReview that list as candidates. Do not pipe it directly into a removal command.
Remove one path from the index
First inspect git status --short and preserve any unrelated staged work. Add an appropriate rule to the root .gitignore; for this example it is:
/local.envPreview the exact removal, apply it, and inspect the staged result:
git rm -n --cached -- local.envgit rm --cached -- local.envgit add -- .gitignoregit diff --cached --name-statusgit diff --cached -- .gitignoreThe staged name summary for the example is:
A .gitignoreD local.envAn existing .gitignore would show M instead of A. The D records removal from the next committed tree; it does not mean the local working file was erased. According to the git rm manual, --cached leaves working files alone and -n previews the operation.
If the command refuses because the staged file differs from both HEAD and the working file, inspect git diff --cached -- local.env and git diff -- local.env. Resolve which content to retain before retrying; do not automatically add -f.
Before committing, confirm that the entire staged diff contains only the intended ignore-rule change and file removal. If unrelated work is staged, finish or separate that work first. Then commit the reviewed index:
git commit -m "Stop tracking local configuration"git show --name-status --format= HEADThe commit should include the D entry for local.env. Do not replace this with git commit --only -- .gitignore local.env: in an additional fixture on the same Git version, that command committed only the new ignore file and put the still-existing local.env back into the index. The removal was lost. The git commit reference explains that explicit paths select their working-tree contents rather than the already staged changes.
There is no need to rebuild the whole index or stage the entire repository for a one-file correction.
Verify the result in this working copy
After the commit:
git ls-files -- local.envgit check-ignore -v -- local.envgit status --short --ignored -- local.envtest -f local.env && printf 'local file retained\n'For the fixture below, ls-files prints nothing, check-ignore identifies /local.env, status prints !! local.env, and the last command confirms the local file remains. These checks establish three separate facts: untracked, ignored, and present on disk.
Ignoring a file in Git does not exclude it from a Docker build context. If the same configuration file must stay out of an image, inspect .dockerignore separately; the Docker build-context guide explains that boundary.
Reproduce what a teammate receives
The following disposable two-clone fixture was executed during article verification on October 2, 2026, with git version 2.54.0 (Apple Git-157) on macOS. It uses dummy configuration, a local clone, and a clean teammate working tree. It does not test merge conflicts or locally modified teammate files.
Run it in Bash. Keep it separate from your project:
fixture_dir="$(mktemp -d)"git init -q -b main "$fixture_dir/author"cd "$fixture_dir/author"git config user.name Fixture
printf 'MODE=demo\n' > local.envgit add -- local.envgit commit -qm "Track fixture"git clone -q . "$fixture_dir/teammate"
printf '/local.env\n' > .gitignoreprintf 'MODE=changed\n' > local.envgit check-ignore -v --no-index -- local.envgit rm --cached -- local.envgit add -- .gitignoregit commit -qm "Stop tracking local config"
test -f local.env && printf 'author: present\n'git -C "$fixture_dir/teammate" pull --ff-onlyif test -f "$fixture_dir/teammate/local.env"; then printf 'teammate: present\n'else printf 'teammate: absent\n'figit show HEAD~1:local.envThe significant observed results were:
author: present... delete mode 100644 local.envteammate: absentMODE=demo--cached preserved the author’s working file, but the commit contained a deletion. The clean teammate clone applied that deletion on its fast-forward pull. The final command also retrieved the original contents from the earlier commit: stopping tracking did not erase history.
Before sharing this change, have collaborators preserve needed local configuration outside the repository and provide a tracked template with dummy values. They can recreate their ignored local file after updating. Do not treat this clean-clone result as a prediction for every dirty working tree; local edits can affect whether an update proceeds.
If the committed file contained real credentials, follow GitHub’s sensitive-data guidance: revoke or rotate the secret first, then evaluate history cleanup separately. A successful ignore check only proves the current exclusion rule, not removal from old commits or other copies.
References: