The Working Tree, the Index, and the Repository
The staging area is the one part of Git nobody explains, and it is the reason git add exists as a separate step from git commit. Move one file through all three places and watch which commands touch which.
The staging area is the one part of Git nobody explains, and it is the reason git add exists as a separate step from git commit. Move one file through all three places and watch which commands touch which.
git init creates one hidden directory
Turning a folder into a repository is one command, and it adds exactly one thing.
$ cd my-project$ git init# Initialized empty Git repository in /Users/you/my-project/.git/That .gitdirectory is the repository. Your files are not "in Git" — they are in the folder, exactly where they were, and .git sits alongside them holding every commit, every branch, and all the configuration.
.git/ HEAD # which branch you are on, as one line of text config # this repository's settings objects/ # every commit, every version of every file refs/heads/ # one file per branch, each holding a commit hash index # the staging area — a single binary fileA file exists in three places at once
This is the model that makes every later command make sense. At any moment, one file has up to three different versions on your machine simultaneously.
Working tree
The actual file on disk, as your editor sees it. Git never changes this unless you ask it to.
Index (staging area)
What your next commit will contain. A draft. Changed by git add, and by nothing else you do in an editor.
HEAD (the repository)
The version in the last commit on your current branch. Frozen, hashed, and effectively permanent.
Edit a file and the working tree differs from the index. Run git add and the index catches up. Run git commit and the repository catches up. All three are now identical, and git status says nothing to report.
Working tree
the folder you edit
Your actual files on disk. Git watches this and reports differences; it never changes anything here unless you ask.
Staging area
also called the index
nothing here
A draft of your next commit. Nothing here is saved yet — it is a list of exactly which changes you intend to record.
Repository
.git, on your machine
Committed history. Once a change lands here it has a hash and a parent, and it is very hard to lose.
# run something
Notice that git restore --staged and git restore both sound like undo and unstaging is completely safe while the other one destroys your edit with no warning and no way back. They are one word apart.
The staging area is a draft of your next commit
Nearly every other version control system commits whatever is on disk. Git makes you name the changes first, and beginners reasonably experience this as an extra step for no reason. It buys one specific thing: the ability to commit some of your current work.
A real afternoon does not produce one clean change. You fix the bug you sat down to fix, rename a confusing variable you tripped over, and delete some dead code. Three unrelated things, in four files. Without staging you have one choice: one commit called "stuff".
# Commit the bug fix on its own$ git add src/auth/redirect.ts$ git commit -m "Fix the redirect when next is empty" # Then the rename, separately$ git add src/lib/users.ts src/lib/session.ts$ git commit -m "Rename usr to currentUser for readability" # And the deletion last$ git add src/legacy/$ git commit -m "Delete the unused legacy exporter"Three commits that each do one thing, from one messy working tree. Six months later, when somebody bisects a bug to the rename commit, they can revert it without also reverting the bug fix. That is what the staging area is for.
git status is the instrument you never stop reading
Run it constantly. It is free, it changes nothing, and it tells you the state of all three trees in one screen. Experienced people run it more often than beginners, not less.
On branch mainYour branch is up to date with 'origin/main'. Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: src/auth/redirect.ts Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: src/lib/users.ts Untracked files: (use "git add <file>..." to include in what will be committed) notes.mdUnder "Changes to be committed". These go into the next commit. redirect.ts is here.
Under "Changes not staged". Git knows this file and has seen it change, but the change is not in the next commit yet.
Git has never seen this file and is not watching it. It will not be committed, ever, until you git add it once.
Untracked is the category that catches people. A brand-new file is invisible to git commit -a and absent from every diff. The number of times a new file has been left out of a commit and broken the build for everybody else is not small.
# Same information, one line per file, once you know the letters$ git status --short# M src/auth/redirect.ts staged modification# M src/lib/users.ts modified, not staged# ?? notes.md untrackedA commit freezes the index, not the folder
This sentence is the whole chapter and it is worth reading twice. git commit records the index, not your working tree.
So if you stage a file, then keep editing it, and then commit — the commit contains the version as it was when you staged it. Your newer edit is still sitting in the working tree, uncommitted. Git will tell you this, in the status output, in a section you have to actually read.
$ git add report.md # index now matches disk# ... you keep typing ...$ git status --short# M report.md staged: the version from the git add# M report.md not staged: everything you typed sinceThe same file appears twice, because there genuinely are two different versions of it. The fix is to git addagain before committing. This is not a bug and it is not a trap — it is exactly the behaviour that makes staging useful — but it is the single most common source of "I committed it and the change is not there".
Key takeaways
- git init adds one .git directory. Your files do not move; the repository sits next to them.
- Deleting .git deletes every commit and keeps every file. There is no undo.
- A file exists in three places at once: the working tree on disk, the index, and the last commit.
- git add copies working tree to index. git commit copies index to repository.
- The staging area exists so you can commit part of a messy afternoon as several clean commits.
- git status names all three states: staged, modified-but-not-staged, and untracked.
- Untracked files are invisible to git commit -a, which is how new files get left out of commits.
- A commit freezes the index, not the folder — so editing after staging leaves the newer version behind.
- git diff --staged shows exactly what you are about to commit. Read it every time.
Quick check
Answer these to unlock the next chapter — 3 of 4 to pass. You can retake it anytime.
Answer every question to check.
Make a free account to read on
Every chapter is free — an account is how your progress, XP, and streak follow you from your laptop to your phone, and how you show up on the leaderboard. No payment, no trial.