← Blog
Product Engineering

Stop tracking a Git file without deleting the local copy

Separate Git’s index from the working tree, verify the staged deletion, migrate local configuration safely, and recover without losing the file.

By Emmanuel Corels

A file can be correct for one workstation and wrong for a shared repository. Local database settings, generated output, editor state, and developer-only credentials often begin life as ordinary tracked files; adding them to .gitignore later does not stop Git tracking them. The safe repair is an index change, not a filesystem deletion.

First establish what must survive

Git’s index is the proposed content of the next commit. The working tree is what is currently on disk. git rm path normally removes a path from both places. git rm --cached path removes it only from the index, leaving the working-tree file in place.

Before changing either, determine whether the repository contains sensitive material. Untracking a credential in the next commit does not erase it from older commits, forks, caches, CI logs, or clones. If a secret was committed, revoke or rotate it first, then handle history rewriting as a separate coordinated incident.

git status --short
git ls-files --error-unmatch -- config/local.env
git diff -- config/local.env
git log --all --oneline -- config/local.env

The second command succeeds only when the path is currently tracked. Review the unstaged difference and history before proceeding. Make a protected copy outside the repository if the local file cannot be recreated. Do not place that backup in another path that a broad cleanup command might remove.

Ignore first, then change the index

Add the narrowest useful rule to the repository’s .gitignore. Keep a redacted example file when teammates need a schema:

# Local runtime configuration; never commit real values
/config/local.env

# Commit config/local.env.example with safe placeholders

A leading slash anchors the rule at the repository root. A trailing slash denotes a directory. Avoid an overbroad pattern such as *.env unless the project truly intends to ignore every matching file. Git’s official documentation is explicit that tracked files are not affected by ignore rules; the index operation is still required.

Remove one file from the index while preserving the working copy:

git rm --cached -- config/local.env

The -- ends option parsing, so a path beginning with a dash cannot be interpreted as an option. For a directory, use -r only after checking the exact path:

git rm -r --cached -- build/output

Do not copy a recipe that removes the entire index and re-adds everything unless you have intentionally reviewed the repository-wide effect. It creates a large staging surface, can expose ignored or generated content through a mistaken rule, and makes code review harder.

Read the staged result correctly

After --cached, git status reports a deletion because the next commit will no longer contain the path. That does not mean the local file was deleted. Verify all three views:

test -f config/local.env
git diff --cached --name-status
git check-ignore -v -- config/local.env
git status --short

You should see the file on disk, a staged deletion, and the exact ignore rule that now matches it. git diff --cached must contain only the intended deletion and the .gitignore change. If the file contains secrets, avoid commands or CI output that print its contents.

Commit the policy and index change together:

git add -- .gitignore config/local.env.example
git commit -m "Stop tracking local runtime configuration"

A teammate pulling this commit will see the tracked file removed from their checkout because the repository has deleted it. Their untracked local file may also create a checkout conflict depending on timing and content. Announce the migration, provide an example/template, and document how each environment provisions its private configuration.

Production rollout without configuration loss

Never make a production deploy depend on an ignored file that exists only because an old checkout left it behind. Move runtime configuration to a deliberate channel: a secret manager, deployment-system variables, a protected host file, or an orchestrator secret. Confirm ownership, mode, backup policy, rotation, and how a fresh host receives it.

Test from a clean clone in a temporary location. Supply non-production configuration through the intended channel, build the application, and run its configuration validation. A clean-clone test catches hidden dependencies that a long-lived workstation masks.

  • CI breaks after the commit: the pipeline relied on the tracked file. Create it from protected variables or use an explicitly safe test fixture.
  • The file reappears in a commit: the ignore rule is wrong, negated later, or a force-add was used. Diagnose with git check-ignore -v and review hooks.
  • A whole directory is staged: cancel and repeat with a narrower path; never approve a bulk deletion by assumption.
  • The secret remains exposed: untracking is not remediation. Rotate it and coordinate history cleanup with repository owners.
  • Case-only path mismatch: verify the exact tracked spelling with git ls-files, especially across case-insensitive and case-sensitive filesystems.

Rollback and recovery

Before committing, restore the index entry without touching the working file:

git restore --staged -- config/local.env

On older Git installations, git reset -- config/local.env performs the equivalent index reset. Remove or revise the ignore rule separately. If git rm was run without --cached and the deletion is not committed, restore the tracked version with git restore -- config/local.env—but first preserve any unique local content because restoring replaces the working-tree version.

After the change is committed, prefer a new commit that re-adds a genuinely appropriate, non-secret file. Reverting the original commit may restore obsolete or sensitive content, so inspect the patch first. For leaked credentials, recovery means rotation, access-log review, and possibly coordinated history rewriting; it is not merely putting the file back.

Operational acceptance checklist

  • The local file still exists and the application can read it under the service account.
  • The staged and committed tree omit the private path.
  • The ignore rule is narrow and verified with git check-ignore -v.
  • A safe example documents required keys without real values.
  • A clean clone builds and tests using the approved configuration channel.
  • Any exposed secret has been rotated independently of the Git cleanup.

Attribution and current verification

This guide was prompted by mveerman’s Stack Overflow question and bdonlan’s accepted answer, used under CC BY-SA. The accepted git rm --cached solution remains correct. Its current behavior and safety conditions were independently verified against Git’s official git rm documentation and the official gitignore documentation.

Primary source: Review the official reference ↗