skip-worktree vs assume-unchanged
Note: These are flags for git update-index, not subcommands of git worktree. Despite the name, --skip-worktree has nothing to do with the git worktree command. Both flags modify how Git tracks index entries, but they were designed for different purposes.
What Is skip-worktree?
The --skip-worktree flag was designed for sparse checkout. It tells Git: “Do not write this file back to the working directory, and if the file is absent, treat it as unchanged.” Git will skip the file in git status and will prefer to keep whatever is (or is not) on disk rather than updating it during checkout.
In practice, many developers repurpose this flag to hide local modifications to tracked config files. While this often works, it is not the flag’s intended purpose, and Git may still overwrite the file in some situations (e.g., when a merge needs to update it). For local config overrides, a .gitignore-based pattern (see below) is more reliable.
# Mark a file so Git ignores your local changes
git update-index --skip-worktree config/database.yml
# The file is still tracked, but your local edits are invisible to Git
git status
# nothing to commit, working tree cleanWhat Is assume-unchanged?
The --assume-unchangedflag tells Git: “This file is unlikely to change, so don’t bother checking it.” It was originally designed as a performance optimization for large repositories where stat-ing every file on disk was slow. Git skips the file during status checks, making git status faster.
Unlike --skip-worktree, Git may still reset the flag automatically during certain operations like git pull or git checkout if the file changes upstream.
# Tell Git not to check this file for changes
git update-index --assume-unchanged path/to/large-file.bin
# Git now skips the file during status checks
git status
# nothing to commit, working tree cleanComparison Table
| Aspect | --skip-worktree | --assume-unchanged |
|---|---|---|
| Intended purpose | Sparse checkout — tell Git not to populate a file in the working directory (often repurposed for local config overrides) | Performance optimization — skip stat calls on files that rarely change |
Survives git pull? | Usually — but Git may still overwrite the file if a merge or rebase needs to update it | Not guaranteed — Git may unset the flag and overwrite the file |
Survives git checkout? | Yes, unless the file has upstream changes | May be reset by Git |
| Precedence | Higher — if both flags are set, skip-worktree takes priority | Lower |
| Common use case | Sparse checkout; sometimes repurposed for local config overrides | Large files in a repo that rarely change (SDKs, vendored deps) |
How to Set and Unset Each Flag
# Set the flag
git update-index --skip-worktree <file>
# Unset the flag
git update-index --no-skip-worktree <file>
# List all files with skip-worktree set
git ls-files -v | grep '^S'# Set the flag
git update-index --assume-unchanged <file>
# Unset the flag
git update-index --no-assume-unchanged <file>
# List all files with assume-unchanged set
git ls-files -v | grep '^h'In the output of git ls-files -v, the first column indicates file status. S (uppercase) indicates skip-worktree is set. h(lowercase) indicates assume-unchanged is set — the lowercase replaces the normal H for tracked files.
Common Use Cases
Local Config Overrides
A common scenario: your project has a config/settings.yml file checked into the repo with default values. Each developer needs to override certain values locally (database host, API keys, etc.) without those changes showing up in git status.
# Edit the config file with your local values
vim config/settings.yml
# Tell Git to ignore your local changes
git update-index --skip-worktree config/settings.yml
# Now git status is clean, and your local config stays put
git status
# nothing to commit, working tree clean
# If you later want to commit changes to this file:
git update-index --no-skip-worktree config/settings.yml
git add config/settings.yml
git commit -m "Update default settings"Which Should You Use?
Neither flag was designed for hiding local config overrides, but if you must choose one, --skip-worktreeis the safer option. It has higher precedence in Git’s internal logic and is less likely to be silently reset during normal operations. However, both flags can be overridden by Git when it needs to update the file (e.g., during a merge that touches it).
Use --assume-unchanged for its original purpose: a performance optimization in large repositories where you want Git to skip stat calls on files you know have not changed.
A better long-term solution for local config overrides is to use a pattern like config.yml (tracked) + config.local.yml (in .gitignore), so you never need index flags at all.