Introduction
Every Git operation moves data between three areas: the working tree (your files), the index (next commit's draft), and the repository (committed history under .git). Naming what moves where is the single most useful mental model.
The areas
- Working tree: the directory you edit in.
- Index:
.git/index, the staged tree. - Repository: objects in
.git/objectsplus refs in.git/refs.
Commands by direction
# working tree -> index
git add file
git rm file
git mv old new
# index -> repository
git commit
git write-tree # plumbing
# repository -> index
git reset <commit>
git read-tree <tree>
# repository -> working tree (and usually index)
git checkout <commit> -- file
git restore --source <commit> file
git switch <branch>
The four diffs
You can compare any two areas:
git diff # working tree vs index
git diff --staged # index vs HEAD
git diff HEAD # working tree vs HEAD
git diff main feature # repository vs repository
Status decoded
git status -s
# XY filename
# X = index status, Y = working tree status
# M modified, A added, D deleted, R renamed, ?? untracked
For example MM means modified in both index and working tree (you staged a change and then edited again).
Reset, restore, checkout
Each affects different areas:
git reset --soft: only repository (move ref).git reset(mixed): repository + index.git reset --hard: repository + index + working tree.git restore --staged: index from HEAD.git restore: working tree from index.git restore --source=<commit>: working tree (and optionally index) from a specific commit.
An exercise
echo a > f.txt
git add f.txt
git commit -m "v1"
echo b >> f.txt
git add f.txt
echo c >> f.txt
git status
git diff
git diff --staged
git diff HEAD
Read each diff; understanding why they differ cements the model.
Plumbing for each area
Knowing the plumbing for each area demystifies higher-level commands:
- Working tree: ordinary filesystem operations.
- Index:
git update-index,git ls-files,git read-tree,git write-tree. - Object database:
git hash-object,git cat-file,git mktree,git commit-tree. - Refs:
git update-ref,git symbolic-ref,git for-each-ref.
git ls-files --stage # index
git ls-tree HEAD # repo
ls -R # working tree
Common mistakes
Trying to memorize commands instead of areas; once you know which area is the source and which is the destination, the right command is obvious. Conflating git restore file with git checkout file; they are equivalent for that case, but checkout's many other modes confuse new users. Using git reset --hard to "clean up" without realizing it nukes the working tree. And finally, expecting git commit to include unstaged edits; it does not (unless you pass -a for tracked files only).