The Git staging area, also called the index, is a holding area where you build up exactly what your next commit will contain. When you run git add, you are not saving anything permanently. You are copying a snapshot of a file into the staging area, telling Git "include this version in the next commit." The commit only records what is staged, nothing else.
That one extra step trips up almost everyone new to Git. Most tools have two states: a file is either saved or unsaved. Git has three. Understanding the third one is the difference between fighting Git and working with it.
The three places a file can live
Every tracked file in a Git project exists in up to three states at once:
| Area | What it is | How a file gets there |
|---|---|---|
| Working directory | The actual files on disk that you edit | You edit them in your editor |
| Staging area (index) | The snapshot queued for the next commit | git add <file> |
| Repository (commit history) | The permanent, recorded snapshots | git commit |
The flow is one-directional when you save work: edit in the working directory, stage with git add, record with git commit. The staging area is the gate between "stuff I changed" and "stuff I am committing."
If you have just started a project, the setup walkthrough for a new repo covers getting to this point, and this is the same staging step you would use when first adding Git to an existing project. If you are completely new, start with the Git for beginners guide, which frames all of this from scratch.
What git add actually does
git add takes the current contents of a file and writes a snapshot of it into the index. Run git status after editing a file and Git tells you it is modified but not staged:
git status
On branch main
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: app.js
no changes added to commit (use "git add" to commit)Stage it, and the same file moves into the "Changes to be committed" group:
git add app.js
git statusOn branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: app.jsThe key thing to understand: git add snapshots the file at the moment you run it. If you edit app.js again after staging it, that newer edit is not staged. Git will now show the file in both groups at once: the staged snapshot in "Changes to be committed" and the fresh edit in "Changes not staged for commit." You have to git add again to update the staged copy. This catches people constantly, so it is worth seeing once on purpose.
To stage everything at once:
git add .That stages all changes in the current directory and below, including new files. New files are otherwise invisible to Git until you add them, since untracked files are not part of any commit yet. If a file keeps showing up that you never want committed, that is what a .gitignore file is for.
Why does staging exist at all?
This is the question worth answering, because the staging area feels like pure overhead until you see what it buys you.
The point is that a commit and a set of edits are not the same thing. You might make five unrelated changes in one editing session: a bug fix, a typo correction, a new feature, and two debug console.log lines you will delete later. If commits were just "save everything I changed," you would be forced to lump all five into one messy commit.
The staging area lets you compose commits deliberately. Stage only the bug fix, commit it with a clear message, then stage the feature work and commit that separately. Your history ends up as a series of focused, single-purpose commits instead of one "fixed stuff" blob. That pays off every time you read a diff, hunt a regression, or write a clear commit message, since each commit is about exactly one thing. And if your edits got messier than your history should be, you can always squash several focused commits into one with an interactive rebase after the fact.
It also gives you a review checkpoint. The gap between "I edited files" and "this is now permanent history" is a moment to look at precisely what you are about to record:
git diff --stagedgit diff on its own shows working-directory changes that are not yet staged. git diff --staged (also spelled --cached) shows what you have staged, which is exactly what the next commit will contain. Get into the habit of running it right before you commit. You can automate the same checkpoint too: a pre-commit hook can check what you have staged before it becomes a commit, catching debug lines or secrets before they land in history.
Staging part of a file with git add -p
Here is where the staging area stops being a chore and starts being a tool. Suppose you fixed a bug and added a debug line in the same file. You want to commit the fix but not the debug line. git add app.js would stage both. Instead, stage interactively:
git add -p app.jsThe -p flag (short for --patch) walks you through the file one "hunk" (a contiguous block of changes) at a time and asks what to do with each:
Stage this hunk [y,n,q,a,d,s,e,?]?The choices you will actually use:
| Key | Action |
|---|---|
y | Stage this hunk |
n | Do not stage this hunk |
s | Split the hunk into smaller pieces (when two changes are close together) |
q | Quit; stop reviewing hunks |
? | Show help for all the options |
Press y on the bug fix, n on the debug line, and only the fix gets staged. Commit, and the debug line stays in your working directory, unstaged, ready to be removed or kept. This is the cleanest way I know to keep commits focused when your edits got tangled together. It is also a gentle nudge to review your own diff line by line before it becomes history.
How to unstage a file
You staged something you did not mean to. Nothing is committed yet, so this is completely safe to undo. The modern command is:
git restore --staged app.jsThis removes the file from the staging area and leaves your working-directory edits untouched. The file goes back to "Changes not staged for commit." Your actual changes are not lost; only the staged snapshot is dropped.
On older Git (before 2.23, when git restore was introduced), the equivalent is:
git reset HEAD app.jsBoth do the same job here: unstage without touching your edits. git status even prints the right one for your version in its hints, so you can copy it straight from there.
Be careful not to confuse unstaging with discarding. Unstaging keeps your edits; discarding throws them away. If you actually want to throw the edits away, that is a different operation covered in discarding local changes in Git. And if you want to set work aside temporarily without committing or losing it, git stash is the tool for that.
Staging area vs working directory vs commit
To pin the mental model down, here is the same change at each stage:
| State | Where the change lives | Command to move it forward | Safe to undo? |
|---|---|---|---|
| Modified | Working directory only | git add to stage | Yes, edits are yours |
| Staged | Index, queued for commit | git commit to record | Yes, git restore --staged |
| Committed | Repository history | git push to share | Yes, but it is now history |
Once a change is committed it is part of history, and undoing it means rewriting that history rather than just dropping a staged snapshot. If you committed too early, undoing the last commit walks through the options, and the broader reset vs revert comparison explains which approach fits which situation. Even then nothing is truly gone: you can recover commits you thought you lost via the reflog.
A typical staging workflow
Putting it together, a normal session looks like this:
# See what changed
git status
# Stage the specific files for this commit
git add app.js styles.css
# Or stage selectively within a file
git add -p utils.js
# Review exactly what will be committed
git diff --staged
# Record it
git commit -m "Fix price rounding on the checkout total"The staging area is the step where you slow down for a second and decide what belongs together. Skip it (with shortcuts like git commit -am) once you understand it and the situation is simple. But knowing it is there, and what it does, is what lets you build a clean history instead of a pile of save points.
FAQ
Sources
Authoritative references this article was fact-checked against.
- Pro Git: Recording Changes to the Repositorygit-scm.com
- git-add: official Git documentationgit-scm.com
- git-reset: official Git documentationgit-scm.com





