GitWorktree.org logoGitWorktree.org

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.

Setting skip-worktree
# 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 clean

What 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.

Setting assume-unchanged
# 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 clean

Comparison Table

Aspect--skip-worktree--assume-unchanged
Intended purposeSparse 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 itNot guaranteed — Git may unset the flag and overwrite the file
Survives git checkout?Yes, unless the file has upstream changesMay be reset by Git
PrecedenceHigher — if both flags are set, skip-worktree takes priorityLower
Common use caseSparse checkout; sometimes repurposed for local config overridesLarge files in a repo that rarely change (SDKs, vendored deps)

How to Set and Unset Each Flag

skip-worktree
# 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'
assume-unchanged
# 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.

You Might Also Like