Keep local changes to a tracked config file without committing them
How do I keep my own version of a tracked file without Git nagging me?
Short answer
git update-index --skip-worktree <file> makes Git ignore your local modifications to a tracked file. The cleaner long-term answer is to commit an example file, ignore the real one, and have everyone copy it.
git update-index --skip-worktree config/local.json
Git stops noticing your local edits to that tracked file
Does this match your situation?
- A tracked config file needs different values on your machine.
- You keep accidentally committing local database URLs or debug flags.
- git stash keeps dragging along a file you never want to commit.
Step-by-step fix
The proper fix — example file plus ignore rule
Track a template and ignore the real file. This is the pattern nearly every project converges on, and it works for new contributors with no setup ritual.
step 1git mv config/local.json config/local.example.json echo "config/local.json" >> .gitignore git add .gitignore config/local.example.json git commit -m "Track example config, ignore the local one" cp config/local.example.json config/local.jsonThe quick fix — skip-worktree
When the file must stay tracked, this tells Git to assume the index version is correct and ignore what's on disk. Your edits stop appearing in status and diff.
step 2git update-index --skip-worktree config/local.json git status # your changes no longer showKeep a list of what you've hidden
This is the flag's real danger: months later a colleague's change to that file will silently fail to apply and it looks like a Git bug. Know how to audit it.
step 3git ls-files -v | grep '^S' # S = skip-worktree git ls-files -v | grep '^[a-z]' # lowercase = assume-unchangedShow the PowerShell (Windows) version
PowerShell (Windows)git ls-files -v | Select-String '^S'A pull that touches a skip-worktree file will fail or be silently ignored. Turn the flag off before pulling changes to it.
Turn it back off
Restore normal tracking when you need to see or accept changes to the file again.
step 4git update-index --no-skip-worktree config/local.json git statusDon't use --assume-unchanged for this
It looks equivalent but it's a performance promise, not an ignore mechanism — Git may discard it at any time and your local edits reappear or get overwritten.
step 5# avoid for this purpose: # git update-index --assume-unchanged <file>
Why this works
Git has exactly two states for a path: tracked or untracked. .gitignore only applies to untracked files, so a committed config file is always compared against the index no matter what ignore rules say. skip-worktree works around this by marking the index entry 'trust me, don't look at the filesystem', which suppresses the comparison. It's a local flag stored in your index only — nobody else sees it, which is what makes it convenient and also what makes a forgotten one so confusing later.
How to stop it happening again
- Commit a .example config and ignore the real one from the start.
- Read local overrides from environment variables instead of files.
- Layer config: a tracked default plus an ignored local file that overrides it.
Commands used in this guide
git update-indexManipulate the index directly - including telling Git to ignore changes to a tracked file.
git ls-filesList files in the index, with precise filters for state.
git rmDelete a file from the repository - or just stop tracking it.
git configRead and write Git settings for one repo, your user, or the whole machine.
Still stuck?
Search the full command reference and every other rescue guide — there are 56 of them, covering everything from detached HEAD to force-push disasters.
Browse all guides →Frequently asked questions
How do I ignore changes to a file that's already tracked?
git update-index --skip-worktree <file> makes Git ignore your local modifications. The more maintainable fix is to untrack it with git rm --cached, add it to .gitignore, and commit a .example version instead.
What's the difference between --skip-worktree and --assume-unchanged?
skip-worktree means 'I have deliberate local changes here, leave them alone' and is designed to persist. assume-unchanged is only a performance hint that the file won't change, and Git is free to disregard it - which makes it unreliable for hiding real edits.
How do I find files I've marked skip-worktree?
git ls-files -v and look for lines starting with S. Lowercase status letters indicate assume-unchanged. Check this before concluding that Git is ignoring an incoming change for no reason.
Related rescue guides
Why is .gitignore not working for files that are already tracked?
.gitignore only applies to untracked files. Once a file has been committed, Git keeps tracking it no matter what the ignore rules say. Remove it from the index with git rm --cached <file> — that stops tracking while leaving the file on your disk.
Read the fix →Always fixableGit says files changed but only the mode differs — how do I ignore that?
Git tracks the executable bit, so a file going from 100644 to 100755 counts as a change. Set core.fileMode false to ignore mode differences, or fix the bit properly with git update-index --chmod when it genuinely matters.
Read the fix →Always fixableHow do I undo git add without losing my changes?
git restore --staged <file> removes a file from the staging area and leaves your edits completely untouched. The older equivalent is git reset HEAD <file>. Neither one touches the contents of your file, so this is a zero-risk operation.
Read the fix →Always fixableI committed a secret — how do I remove it from Git history?
Rotate the secret first — that's the only step that genuinely protects you. Then purge it from history with git filter-repo (or the BFG Repo-Cleaner) and force-push. Deleting the file in a new commit does nothing, because the old commit still contains it and is still fetchable.
Read the fix →