Git from First PrinciplesPost 4 of 16 · 16 of 16 written
- How to use this guide
- 01Why version control exists
- 02Git's data model: a content-addressed object database
- 03References: branches, HEAD and tags are pointers
- 04The three areas: working tree, index, repository
- 05Daily commands, mapped onto the model
- 06Branching and integration: merge, rebase, cherry-pick
- 07Undoing things safely
- 08Remotes and collaboration
- 09Workflows and conventions
- 10Git for AI and ML work
- 11Git with AI coding agents
- 12Power tools
- 13Under the hood: build a commit by hand
- 14Debugging mindset and recovery playbook
- 15Practice path, fluency checklist and glossary
- 16Sources
4. The three areas: working tree, index, repository
Every change you make lives in one of three places, and every “where did my change go?” question is answered by asking which one.
- Working tree. The ordinary files on disk that you and your editor touch. Git does not watch them; it compares them to the other two areas when you ask.
- Index, also called the staging area. One binary file,
.git/index, listing the exact snapshot your next commit will contain: every tracked path with its mode and blob hash. - Repository. The object database in
.git/objectsplus the refs. Permanent and content-addressed.
The index is a full snapshot, not a list of changes
This is the second big correction to most mental models. Right after a commit, the index is identical to that commit’s tree. git add replaces one entry in this draft, and writes the file’s blob into the database immediately. git commit turns the draft into tree objects and a commit.
git ls-files --stage
# 100644 a1b2c3... 0 README.md
# 100644 9f8e7d... 0 src/train.pyThat listing is your next commit. The 0 is a stage number; during a merge conflict you will see 1, 2 and 3 there (Part 6).
Why a staging area exists at all
It lets you build each commit on purpose instead of committing whatever happens to be on disk. You can commit part of your work, split two unrelated fixes into two commits, or stage single hunks with git add -p.
With AI coding agents this becomes a review gate. The agent edits the working tree; you stage only the hunks you have read and approved (Part 11).
File states
- Untracked: in the working tree, never added to the index.
- Unmodified: working tree, index and HEAD all agree.
- Modified: the working tree differs from the index.
- Staged: the index differs from HEAD.
- Ignored: untracked and matched by
.gitignore, sostatusstays quiet about it.
A file can be staged and modified: you added it, then edited it again. git commit takes the staged version, not the one on disk. This surprises everyone once.
The three comparisons
| Command | Compares | Answers |
|---|---|---|
git diff |
Index → working tree | What have I changed but not staged? |
git diff --staged |
HEAD → index | What will my next commit contain? |
git diff HEAD |
HEAD → working tree | Everything changed since the last commit |
git status runs these comparisons and summarizes them in words. Read its output every time; it usually tells you the next command too.
One change, start to finish
- You edit
train.py. The working tree now differs from the index. Status says “modified, not staged”. - You run
git add train.py. Git writes a blob for the new content and points the index entry at it. Status says “changes to be committed”. - You edit
train.pyagain. The file is now both staged (first edit) and modified (second edit). - You run
git commit -m "Tune learning rate". Git writes trees from the index, writes a commit whose parent is the old HEAD, and moves the branch. The second edit is still only on disk.
[embedded content: three areas plus the remote · the commands between them]
Read the arrows left to right to record and share work, right to left to bring stored versions back; the bottom path is what happens when you switch branches.
What Git can and cannot recover
The database only knows about content that reached it. That single fact tells you what is safe:
- Committed work is recoverable for weeks through the reflog, even after a bad reset or rebase.
- Staged but never committed content exists as a blob.
git fsck --lost-foundcan dig it out. - Unstaged edits exist only on disk. If a command overwrites the working tree, they are gone for good.
The commands that overwrite the working tree are the dangerous ones: git restore <file>, git reset --hard, git checkout -- <file>, git clean, and git switch --discard-changes. Before any of them, ask: is anything I care about unstaged?
The practical rule follows directly. Commit early and often on a local branch. Local commits are cheap, private and recoverable, and you can tidy them before sharing (Part 7).
Check yourself
- You stage a file, edit it again, and commit. Which version is in the commit?
- What does
git diffshow right aftergit add ., and why? - Why can Git restore a commit you reset away, but not an edit you never staged?