Skip to content
LatchkeyLatchkey home

git update-index --assume-unchanged and --skip-worktree

git update-index --assume-unchanged <file> tells git to stop checking that tracked file for changes (a performance hint), while --skip-worktree marks it as locally modified and not to be touched.

These two flags both hide changes to tracked files, but they mean different things. assume-unchanged is a performance hint git may ignore; skip-worktree expresses real intent to keep a local version. CI scripts misuse them often, so the distinction matters.

What it does

git update-index manipulates the staging index directly. --assume-unchanged sets a bit telling git not to bother checking that file for modifications (a speed hint on slow filesystems); git is free to ignore it, and operations like checkout or pull can overwrite the file. --skip-worktree is stronger: git tries to preserve the local file across many operations, intended for config you must keep locally edited.

Common usage

Terminal
git update-index --assume-unchanged config/local.json
git update-index --no-assume-unchanged config/local.json   # undo
# keep a locally edited file from being overwritten
git update-index --skip-worktree .env.local
# list files with the skip-worktree bit (S in column 2)
git ls-files -v | grep '^S'

Options

FlagWhat it does
--assume-unchangedSet the assume-unchanged bit (performance hint)
--no-assume-unchangedClear the assume-unchanged bit
--skip-worktreeMark the file to be preserved locally
--no-skip-worktreeClear the skip-worktree bit
--refreshRe-check stat info for index entries
--chmod=+x <file>Set the executable bit in the index

In CI

Do not use assume-unchanged to hide secrets or config in CI: a fresh clone does not carry these bits, and any pull/checkout can overwrite the file anyway. If a checkout or merge in CI reports the file changed despite the bit, that is assume-unchanged working as designed (it is only a hint). For genuinely local-only files, .gitignore or skip-worktree is more reliable, but neither survives a clean clone.

Common errors in CI

"error: Your local changes to the following files would be overwritten by checkout" can appear even on assume-unchanged files, because the bit is advisory. "fatal: Unable to mark file <x>" means the path is not tracked; the flags only apply to files already in the index. The bits are per-clone and silently absent in CI runners.

Using this in CI

CI checkouts are shallow and detached by default, which changes the answer this command gives you. Commands that read history, branch names, or tags need the checkout configured for it.

.github/workflows/ci.yml
- uses: actions/checkout@v4
  with:
    fetch-depth: 0   # history, tags, and git describe all need this

- run: |
    git rev-parse --is-shallow-repository   # expect false
    git rev-parse --abbrev-ref HEAD          # prints HEAD when detached

Frequently asked questions

git update-index --assume-unchanged and --skip-worktree?
These two flags both hide changes to tracked files, but they mean different things. assume-unchanged is a performance hint git may ignore; skip-worktree expresses real intent to keep a local version. CI scripts misuse them often, so the distinction matters.
What it does?
git update-index manipulates the staging index directly. --assume-unchanged sets a bit telling git not to bother checking that file for modifications (a speed hint on slow filesystems); git is free to ignore it, and operations like checkout or pull can overwrite the file.
In CI?
Do not use assume-unchanged to hide secrets or config in CI: a fresh clone does not carry these bits, and any pull/checkout can overwrite the file anyway. If a checkout or merge in CI reports the file changed despite the bit, that is assume-unchanged working as designed (it is only a hint).
Common errors in CI?
"error: Your local changes to the following files would be overwritten by checkout" can appear even on assume-unchanged files, because the bit is advisory. "fatal: Unable to mark file <x>" means the path is not tracked; the flags only apply to files already in the index. The bits are per-clone and silently absent in CI runners.

Related guides

References

Run this faster and cheaper on Latchkey managed runners - self-healing included. Start free → 30-day trial · No credit card