Skip to content
The Index (Staging Area)
step 1/5

Reading — step 1 of 5

Read

~3 min readRefs & History

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:

  1. HEAD — the snapshot you last committed. Frozen in the object store.
  2. The index (staging area) — the snapshot your next commit will be. A real, editable file: .git/index — a sorted list of path → blob-hash entries (plus file metadata for change-detection speed).
  3. 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/index is 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…