Lesson 3 of 30
Working directory, staging area, and HEAD — the actual model behind git add, git commit, and what git status is telling you.
15 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Every file in a Git repo exists in up to three places at once, and almost every confusing git status message is really just describing differences between these three:
git add, you're not queuing a commit — you're copying the current version of that file into this intermediate snapshot. It's a real file, .git/index, sitting between your working directory and history.git status is just diffing these three against each other and reporting the differences in plain English: "changes not staged" is working directory vs. staging area; "changes to be committed" is staging area vs. HEAD.
$ echo "version 1" > notes.txt
$ git add notes.txt
$ git commit -m "add notes"
At this instant all three trees agree: working directory, staging area, and HEAD all hold "version 1".
Now edit the file, but don't stage it yet:
$ echo "version 2" > notes.txt
$ git status
On branch main
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: notes.txt
Working directory says "version 2". Staging area and HEAD still say "version 1" — that mismatch between working directory and staging area is exactly what "changes not staged" means.
Stage it:
$ git add notes.txt
$ git status
On branch main
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: notes.txt
Now staging area says "version 2" too, but HEAD still says "version 1" — that's what "changes to be committed" means. The staging area is a real, inspectable snapshot at this point, independent of the working directory:
$ echo "version 3" > notes.txt
$ git status
Changes to be committed:
modified: notes.txt
Changes not staged for commit:
modified: notes.txt
Git is telling you two different things about the same file at the same time: staged content differs from HEAD ("version 2" vs "version 1"), and working directory differs from staged content ("version 3" vs "version 2"). If you commit right now, "version 3" is not what gets committed — "version 2" is, because commit only ever reads from the staging area, never from the working directory.
$ git commit -m "update notes"
$ git show HEAD:notes.txt
version 2
app.js is edited in the Working Directory. It differs from the last commit — Git calls this "modified."
git commit never reads the working directory. It commits whatever is in the staging area, full stop. Editing a file after git add and then running git commit without re-adding commits the old, staged version — not what's currently on disk.git add, not a pointer back to the working directory that gets re-read at commit time.git add can be run multiple times before a commit, each one overwriting the previous staged snapshot of that file. Staging isn't a one-shot action tied to a single commit — you can stage, edit more, and re-stage as many times as you want before committing once.The staging area is the same idea as a database transaction's write buffer, or a form's draft state before you hit submit — a deliberate middle step that lets you build up exactly what you want committed before it becomes permanent, decoupled from whatever the "live" state currently looks like.