TechEarl

The Git Staging Area Explained

The Git staging area (the index) is the in-between layer where you assemble exactly what goes into your next commit. Here is what git add really does, and why it exists.

Ishan Karunaratne⏱️ 8 min readUpdated
Share thisCopied
Understanding the Git staging area and what git add does to the index before a commit

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:

AreaWhat it isHow a file gets there
Working directoryThe actual files on disk that you editYou edit them in your editor
Staging area (index)The snapshot queued for the next commitgit add <file>
Repository (commit history)The permanent, recorded snapshotsgit 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:

bash
git status
git status showing a file moving from unstaged to staged after git add
git add moves a change into the staging area: red becomes green.
text
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:

bash
git add app.js
git status
text
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   app.js

The 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:

bash
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:

bash
git diff --staged

git 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:

bash
git add -p app.js

The -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:

text
Stage this hunk [y,n,q,a,d,s,e,?]?

The choices you will actually use:

KeyAction
yStage this hunk
nDo not stage this hunk
sSplit the hunk into smaller pieces (when two changes are close together)
qQuit; 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:

bash
git restore --staged app.js

This 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:

bash
git reset HEAD app.js

Both 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:

StateWhere the change livesCommand to move it forwardSafe to undo?
ModifiedWorking directory onlygit add to stageYes, edits are yours
StagedIndex, queued for commitgit commit to recordYes, git restore --staged
CommittedRepository historygit push to shareYes, 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:

bash
# 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.

Tagsgit staging areaGitVersion Controlgit addgit index

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts

The Regex (*ACCEPT) Control Verb, Explained

What the PCRE (*ACCEPT) backtracking control verb does, how it forces an immediate successful match, how it behaves inside capturing groups, which engines support it, and where it is genuinely useful.

Git Branching Explained for Beginners

What a Git branch actually is, how HEAD points at your current spot, and the commands to create, switch, list, rename, and delete branches with confidence.