Introduction
The index is a binary file at .git/index that mirrors a tree object: a sorted list of paths with mode, hash, and stat information. It is the staging area for the next commit and a cache that lets Git skip rehashing unchanged files.
Inspecting it
git ls-files --stage
# 100644 a1b2c3... 0 README.md
# 100644 e4f5g6... 0 src/main.c
Stage 0 means "no conflict". During a merge, conflicted paths get stages 1 (base), 2 (ours), 3 (theirs).
What stat caching buys you
The index also stores ctime, mtime, size, and inode for each path. git status compares these to the working tree before hashing; if they match, the file is assumed unchanged. This is why git status is fast on huge trees.
git update-index --refresh
git update-index --really-refresh
Modifying the index
git add file.txt # stage from working tree
git rm --cached file.txt # remove from index, keep on disk
git restore --staged file.txt # unstage
git update-index --chmod=+x bin/run # change executable bit
Assume unchanged and skip-worktree
Two flags tell Git to ignore certain paths:
git update-index --assume-unchanged config.local
git update-index --skip-worktree config.local
assume-unchanged: a performance hint; Git may still notice changes.skip-worktree: stronger; intended for files the user keeps locally modified.
Neither replaces .gitignore; both are workspace-local hacks, not shareable.
Reading and writing trees
git write-tree # snapshot index as tree object
git read-tree HEAD # reset index to a tree
git read-tree --reset -u HEAD # also update working tree
These plumbing commands underlie commit, checkout, and merge.
Conflicts in the index
During a merge, the index becomes the source of truth for what is unresolved:
git ls-files -u
# 100644 a1... 1 src/main.c
# 100644 b2... 2 src/main.c
# 100644 c3... 3 src/main.c
Resolving a conflict means writing the final content and git add-ing it, collapsing back to a single stage-0 entry.
Sparse checkout and the sparse index
Sparse checkout populates the working tree with only a subset of the repository's tree, useful in monorepos. With cone mode plus the sparse index, the index itself shrinks to the active cone, making operations on huge repos fast:
git sparse-checkout init --cone
git sparse-checkout set src/myteam docs
git sparse-checkout list
git sparse-checkout disable
The sparse index marks omitted entries with a special bit so Git can lazily expand them only when commands need to. This feature requires Git 2.32 or newer for full functionality.
Common mistakes
Believing the index is just "what is staged". It is also a cache and a conflict tracker. Deleting .git/index when stuck; rebuild with git read-tree HEAD, but you lose any uncommitted staging. Using --assume-unchanged as a substitute for .gitignore; the next clone will not honor it. Finally, expecting the index to track empty directories; it cannot. Use a placeholder file (.gitkeep) if you need to commit an empty folder.