Lesson 01 · foundation

The Three Trees

Where your changes actually live — and why git status stops being noise.

You already run add, commit, push. This lesson gives you the map underneath them. Everything later in this workspace — undoing mistakes, branching, resolving a conflict, reviewing a teammate's PR — is a movement between the three places below. Learn these once and the rest is bookkeeping.

The map

Working Directory

the files you edit

Your actual folder on disk. The only tree your editor can see.

Staging Area

a.k.a. the index

A draft of your next commit. You assemble it deliberately.

Repository

the .git folder

Permanent history. Every committed snapshot lives here forever.

working → staging  git add staging → repository  git commit

The staging area is the piece most people never form a picture of, and it is the whole reason Git feels fussy compared to "save file". Its purpose: a commit should be a deliberate, coherent unit — not a dump of everything you happened to touch. You edited three unrelated things this afternoon? Stage and commit them separately. The staging area is where you decide.

Pro Git, 2.2 "The staging area is a file … that stores information about what will go into your next commit." A commit records the snapshot you set up in the staging area — not the current state of your folder.
Recording Changes to the Repository →

Reading git status

Run it. Every line is telling you which tree a file is sitting in:

$ git status
On branch main

Changes to be committed:        ← in the STAGING AREA
        modified:   auth.js

Changes not staged for commit:  ← in the WORKING DIR only
        modified:   README.md

Untracked files:                ← working dir, Git has never seen it
        notes.txt

Three sections, three trees. auth.js is queued for the next commit. README.md is edited but will not be included. notes.txt Git isn't even watching yet.

Tracked vs. untracked A tracked file is one that was in the last snapshot (or has been staged). Everything else is untracked — Git ignores it entirely until a git add brings it in. This is why a new file needs add even the very first time.

The same thing in VS Code

CLIVS Code Source Control panel
git statusThe panel itself — it is a permanent git status
“Changes not staged” / “Untracked”the Changes group
“Changes to be committed”the Staged Changes group
git add <file>the + icon on a file row
git restore --staged <file>the icon on a staged row

If you have ever hit Commit in VS Code with nothing in Staged Changes and been asked "do you want to stage all your changes?" — that prompt is the staging area asking you to stop skipping it.

The two diffs

Because there are three trees, there are two gaps between them — and therefore two different diffs. This trips up almost everyone:

git diff              # working dir vs. STAGING — what you have NOT staged yet
git diff --staged     # staging vs. LAST COMMIT — what you are ABOUT to commit
The classic confusion You stage everything with git add ., then run git diff and it prints nothing. Nothing is broken. git diff compares working dir to staging, and after git add . those two are identical. You wanted git diff --staged.

Habit worth building now, before your first team PR: run git diff --staged immediately before every commit. It is a five-second review of exactly what you are about to put into permanent history — and it catches stray debug lines and pasted secrets while they are still cheap to remove.

Do this now (5 minutes, in a real repo)

Reading this does not make it stick — typing it does. In any scratch repo:

echo "one"   > a.txt
echo "two"   > b.txt
git status              # both untracked

git add a.txt
git status              # a.txt staged, b.txt still untracked

echo "one-changed" > a.txt
git status              # a.txt appears in BOTH sections — read that carefully

git diff                # shows the "one-changed" edit  (working vs staging)
git diff --staged       # shows the original "one"      (staging vs last commit)

git commit -m "add a"
git status              # only b.txt remains

The step to sit with is the fourth one: a single file listed in two sections at once. That is only possible because staging holds a copy, not a pointer to your file. Once that feels obvious, this lesson has done its job.

Your win You can now read git status as a location report rather than a wall of text, and you know which of the two diffs to reach for. Every "undo" command you learn next is defined by which tree it touches — so this map is the prerequisite for the whole recovery playbook.

Go deeper

Primary source — read this one: Pro Git §2.2, "Recording Changes to the Repository". Fifteen minutes, and it is the authoritative version of everything above. The file-lifecycle diagram near the top is worth committing to memory.