Reading — step 1 of 5
Read
The Index (Staging Area)
Every git tutorial says "git add stages your changes" and every git beginner nods without knowing what that means. Here's what it means: git constantly juggles three snapshots, the index is the middle one, and git status — the command you'll implement — is nothing but two comparisons among the three.
The three trees
At any moment, three versions of your project coexist:
- HEAD — the snapshot you last committed. Frozen in the object store.
- The index (staging area) — the snapshot your next commit will be. A real, editable file:
.git/index— a sorted list ofpath → blob-hashentries (plus file metadata for change-detection speed). - The working tree — the actual files you're editing. Uncommitted, unhashed, just bytes on disk.
git add file.txt does something concrete now that you know the object model: hash the file's current contents into a blob (your hash-object code!), then update the index's entry for that path to the new hash. The working file isn't moved anywhere — a snapshot of it entered the index. Edit the file again and the index still holds the old snapshot; that's why re-adding after edits is a thing, and it stops being annoying once you see the index as "the exact bytes I've approved for the next commit."
Commit, then, is exactly: write the index out as a tree object, wrap a commit object around it, advance the branch (last lesson). The index isn't a queue of changes — it's the draft of the next tree.
Why a middle tree at all?
Because "everything I've touched" and "what I mean to commit" are different sets. The index lets you build a commit deliberately — stage two of five changed files, even stage parts of a file (git add -p just stages a blob containing some of your edits) — so commits can be coherent units instead of "whatever my directory looked like at 6pm." It's the feature people miss most in systems without it.
git status = two diffs
With three trees, status falls out mechanically:
- HEAD vs index → "Changes to be committed" (staged). Path in index with different hash than in HEAD's tree = staged modification; in index but not HEAD = staged new file; in HEAD but not index = staged deletion.
- Index vs working tree → "Changes not staged" (modified). Hash the working file and compare with the index entry. (Real git avoids re-hashing everything by caching file size/mtime in the index and only hashing suspects — that metadata is why
.git/indexis more than a plain list, and why status is fast on 100k-file repos.) - In the working tree but in neither snapshot → "Untracked files."
One file can appear in both lists (staged an edit, then edited again) — the two comparisons are genuinely independent, and now you know why status prints them as separate sections.
Your exercise: Implement git status
Given the three trees' contents, produce the staged / modified / untracked classification. It's set-and-map logic — build path→hash maps for HEAD and index, compare per path, then sweep the working tree for unknowns. The bugs that bite: forgetting deletions (present in one map, absent in the other — both directions), double-reporting the staged-then-edited file (correct behavior: report it in both sections!), and ordering (the spec fixes output order; sort your paths). When it passes, git status's output format will read to you as what it is — two diffs and a leftover scan, labeled politely.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…