Learning Objectives
By the end of this lesson you will be able to:
- Name the four states a file can be in and what moves it between them
- Explain what the staging area is and why Git has one at all
- Predict which state a file is in from a
git statusoutput - Describe the three "trees" Git compares on every command
The Model That Makes Everything Else Obvious
Git keeps three copies of your project at all times. Learn these three and you have learned Git:
| Tree | Where it lives | What it holds |
|---|---|---|
| Working Directory | your visible folder | the files as they are right now |
| Staging Area (index) | inside .git |
the change you're assembling for the next commit |
| Repository (HEAD) | inside .git |
the last committed snapshot, and every one before it |
Everything Git tells you is a comparison between two of these three. That's the whole trick.
The Four States
A file moves through four states, and it moves in one direction:
untracked ──git add──▶ staged ──git commit──▶ committed
▲ │
│ (edit it)
└───────git add───── modified
Untracked — the file exists in your folder, but Git has never recorded it. Brand-new files start here.
Modified — the file is in the repository, but the version on your disk differs from the last committed one. You edited it. Git noticed. Nothing has been recorded yet.
Staged — you've told Git "this exact change goes into the next commit." It's queued up, sitting in the staging area, waiting.
Committed — the change is written into the repository's history, permanently, with your name and message attached.
Notice that both untracked and modified files take the same road forward: git add puts them in the staging area, git commit writes them to history.
Why the Staging Area Exists
This is the step beginners want to skip, and the one that makes Git worth using.
Real work is messy. In one afternoon you might fix a typo in the regression checklist, rewrite half of an environment config, and drop in a screenshot for a bug you're still investigating. Three unrelated changes, sitting in one folder.
Without a staging area you'd have exactly one option: commit all of it, in one blob, under a message like "stuff". A year later, someone tries to understand when the config changed and finds it buried in a commit that also touched a checklist and a screenshot.
With a staging area, you choose:
git add checklist.md # only the typo fix
git commit -m "Fix typo in login smoke checklist"
The config and the screenshot stay exactly where they were — modified and untracked, untouched, waiting for their own commits when they're ready.
The staging area is how you make history readable. It lets the commit reflect one idea instead of one afternoon.
Reading git status Like a Pro
git status groups files by state. Here's a repository with one of each:
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: checklist.md
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
modified: config/test-env.yaml
Untracked files:
(use "git add <file>..." to include in what will be committed)
screenshots/bug-441.png
Read the section headings, not the filenames:
- "Changes to be committed" = staged. This is what the next commit will contain.
- "Changes not staged for commit" = modified but not staged. Real edits, not going anywhere yet.
- "Untracked files" = Git has never recorded these.
If a file appears under both the first and second heading, that's not a bug — it means you staged a change and then edited the file again. The staged version goes into the commit; the newer edit doesn't. Run git add again if you want both.
The whole model in one picture:
WORKING DIRECTORY STAGING AREA REPOSITORY
(files you edit) (.git/index) (.git — history)
┌────────────────────┐ ┌────────────────┐ ┌────────────────────┐
│ checklist.md M │ add │ │ commit│ 4f2a1c9 Add cases │
│ test-env.yaml M │──────▶│ checklist.md │──────▶│ 91bd4e7 Update DB │
│ bug-441.png ?? │ │ │ │ c7f0a12 Fix wait │
└────────────────────┘ └────────────────┘ └────────────────────┘
M = modified staged: queued permanent, with your
?? = untracked for the commit name and message
Only checklist.md was staged, so only checklist.md goes into the next commit. The config and the screenshot stay exactly where they are.
Pro Tip: Nothing in Git is permanent until it's committed — and even a commit is only permanent locally until you push. That means the staging area is a completely safe place to experiment.
git addsomething you didn't mean to?git restore --staged <file>puts it back. No harm done.
Key Takeaways
- Git maintains three trees: working directory, staging area, and repository
- Files move untracked/modified → staged → committed, via
git addandgit commit - The staging area exists so a commit can contain one logical change instead of everything you did today
git statusgroups files by state — read the headings to know which state each file is in- A file can be staged and modified at the same time; only the staged version gets committed