The three trees: working, staging, repo
Understand the three zones Git uses to track your work — and why a separate staging step gives you precise control over every commit.
What you'll learn
- What the working directory, staging area, and repository each store
- How git add and git commit move changes between those zones
- Why staging lets you craft clean, focused commits instead of all-or-nothing snapshots
Before you start
The previous lesson ended on a puzzle: when you commit, Git does not snapshot your working folder directly — it snapshots a staging area in the middle, one you load by hand. That middle zone sounds like a needless extra step until you see what it buys you. So let us name all three zones precisely, watch a change travel through them, and only then ask why the one in the middle earns its place.
The core mental model
Every file in a Git project lives in exactly one of three zones at any moment. Think of them as three distinct containers — not folders you can open in Finder, but states of knowledge that Git maintains.
Working Directory — your real files, exactly as they exist on disk right now. When you open a file in your editor and type something, you are changing the working directory. Git can see these changes, but it has not recorded them anywhere yet.
Staging Area (also called the index) — a holding zone where you compose the next snapshot. Nothing is permanent here; it is a proposed commit waiting for your approval. You populate it deliberately, one file (or one hunk) at a time.
Repository (the .git directory) — the permanent, compressed history of every commit you have ever made. Once a snapshot lands here, it is safe and retrievable forever, even if you delete the file from your working directory.
Visualizing the flow
Walking through the flow
1. Edit a file (working directory changes)
Open README.md and add a line. From Git’s perspective the file is now modified — changed on disk relative to what was last committed — but Git has not been asked to record anything.
2. Stage the change (git add)
git add README.md
Git takes a snapshot of README.md as it is right now and places that snapshot in the staging area. The working directory still has your edits; the staging area now also knows about them.
3. Commit the snapshot (git commit)
git commit -m "Add project overview to README"
Git wraps everything in the staging area into a permanent commit object, writes it to the repository, and clears the staging area. The working directory is unchanged.
Reading the three zones with git status
git status is your live map of which zone each file is in. Learn to read it fluently.
Scenario A — a brand new file that has never been added:
git status
On branch main
Untracked files:
(use "git add <file>..." to include in what will be committed)
notes.txt
nothing added to commit but untracked files present
notes.txt exists in the working directory but is invisible to the staging area and repository.
Scenario B — an existing file edited but not yet staged:
git status
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
modified: README.md
no changes added to commit
Scenario C — after git add README.md:
git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: README.md
“Changes to be committed” means the file is in the staging area, ready for git commit.
Why does the staging area exist?
This is the question most beginners never ask — and the answer is the whole point.
Imagine you spent an afternoon making two unrelated changes: you fixed a bug in auth.py and added a new feature in dashboard.py. Without a staging area you would have to commit both together, muddying your history. With the staging area you can do this:
git add auth.py
git commit -m "Fix login redirect bug"
git add dashboard.py
git commit -m "Add user dashboard page"
Two clean, focused commits from one editing session. The staging area is a curation tool — it lets you decide exactly what story your history tells, independently of the order you actually wrote the code.
A quick note on git init
When you run git init in a folder, Git creates a hidden .git directory. That directory is the repository — it holds the index (staging area) and all committed history. The files outside .git are your working directory. Everything flows from that one git init command.
In one breath
Every changed file sits in exactly one of three zones: the working directory (your real files on disk), the staging area (a snapshot you compose by hand as the proposed next commit), and the repository (the permanent .git history). git add copies a file’s current state from working directory into staging; git commit seals everything staged into permanent history and clears the stage. The staging area is the part beginners skip and experts lean on — it is a curation tool that lets you split one messy editing session into clean, focused commits, so your history tells the story you choose rather than the order you happened to type.
| Zone | What it holds | How you move changes in |
|---|---|---|
| Working Directory | Your current files on disk | Edit in your editor |
| Staging Area / Index | The proposed next commit | git add |
| Repository (.git) | Permanent committed history | git commit |
git status shows which zone each changed file is in at any moment — reading it fluently is 80 percent of understanding Git.
Practice
Before the quiz, trace the snapshot rule: you edit auth.py and dashboard.py in one sitting, run git add auth.py, then make one more tweak to auth.py, then git commit -m "fix auth". Exactly which version of which file lands in that commit — and what does git status show for each of the two files the moment the commit finishes?
Quick check
A question to carry forward
You can now name the three zones and read git status like a map. But so far “stage a change” has meant the simplest case — git add one-file.py. Real editing sessions are messier: you touch eight files, want six of them in this commit and two in the next, and inside a single file you may want only some of the lines. Can the staging area really be that surgical — down to individual hunks of one file — and once something is staged, how do you write a commit message your future self will actually thank you for? That is the next lesson: staging and commits, in full.
Practice this in an interview
All questionsA git commit captures code, but an ML run also depends on the exact training data, hyperparameters, environment, and randomness, none of which live in Git. Datasets are too large for Git and change independently of code, so you need a data-versioning tool like DVC or lakeFS to pin a content hash of the data to the commit. Full reproducibility means versioning code, data, config, environment, and seeds together and linking them.
DVC (and lakeFS) version raw datasets and model artifacts as immutable snapshots tied to Git commits, giving reproducibility and rollback. A feature store manages computed features for training and serving, its main job being to keep offline and online feature definitions in sync to prevent training-serving skew. They are complementary: DVC answers what data made this model, while a feature store answers how do I serve the same features consistently.
Experiment tracking logs every run, its parameters, metrics, and artifacts, so you can compare and reproduce experiments during development. A model registry is the curated, governed catalog of the few models you actually intend to deploy, with versioning, stage or alias management, approvals, and lineage. You need both because tracking gives breadth for exploration while the registry gives the controlled, auditable path to production.
Register every candidate as an immutable, versioned artifact, then move it through environments (dev to staging to prod) gated by automated checks rather than promoting straight to prod. In modern MLflow you use aliases like champion and challenger instead of the deprecated stage labels, and promotion is a governed, auditable action with sign-off and an easy rollback by repointing the alias. Always validate in staging and roll out progressively (canary or shadow) before full traffic.