The Three-Tree Model
The Three-Tree Model
After this page you can name Git’s three snapshots of the same project and say which command copies between them.
Three snapshots, one project
Everyday Git is three versions of the same tree:
| Name | On disk | Role |
|---|---|---|
| Working directory | your files | What you edit |
| Index (staging area) | .git/index |
What the next commit will contain |
| HEAD commit | an object in .git/objects |
Last committed snapshot on this branch |
working directory --git add--> index --git commit--> HEAD
<--git restore-- <--git restore --staged / git reset--
~/desk/desk-api/ .git/index .git/objects
README.md, src/… next commit last commit (HEAD)
git addcopies working-tree bytes into the index (and writes blob objects).git commitwrites a tree from the index, then a commit pointing at that tree and at the current HEAD, then slides the branch name forward.git restore <file>copies from the index (or a commit) onto the working tree.git restore --staged <file>copies from HEAD into the index, leaving your working-tree edit in place.
There is no fourth “GitHub tree.” A remote is another repository’s names plus objects you have fetched.
Why the index exists
The index lets the next commit be a chosen subset of what is on disk. You can edit five files and commit two.
echo "ok" >> README.md
echo "wip" >> notes.txt
git add README.md
git status
# README.md is staged; notes.txt is notgit commit -a copies already tracked files from the working tree into the index and commits. New files still need git add. Prefer explicit git add until that distinction is boring.
status is the three-tree report
git statusGit compares:
- working tree vs index → “Changes not staged for commit”
- index vs HEAD → “Changes to be committed”
- untracked files → paths the index does not know
When all three match, status is clean. That is the happy state after a commit.
reset in one sentence
git reset moves HEAD and optionally the index and working tree. Soft / mixed / hard are which of the three trees follow. Chapter 7 (reset) and chapter 17 (reflog) use this on purpose. For now, prefer git restore to throw away an unstaged edit, and git restore --staged to unstage.
Check it
git status
git diff # working tree vs index
git diff --staged # index vs HEADIf git diff is empty and git diff --staged is not, the next commit already contains the change. If both are empty and status still lists a file, it is untracked — git add it.