Read staged and working differences separately

Comparison Last tested 26 October 2026

A normal Git diff compares working files with the staging area; a staged diff compares the staging area with the current commit. A file edited again after staging can therefore have two different diffs at once. Read both before committing, because the latest text on disk may not be the text prepared for the next commit.

Name the three versions

This comparison involves the committed version, the staged version and the working file. In the example they contain three deliberately different lines: base, staged draft and working draft. Giving each version distinct content makes it possible to see which pair a command compares without relying on colors or a particular terminal display.

The staging area is often called the index. Its role here is concrete: it holds the version prepared by the last add operation. Editing the file afterward changes the working copy but does not automatically replace that prepared version. The example commits without staging the second edit, then checks which text reached the new commit.

The official diff reference defines the normal and staged comparisons separately1. The distinction is not between “important” and “unimportant” changes. It is between different endpoints. A small unstaged change can be essential to a task, and a large staged change can still be wrong. Reading the diff tells you what differs, not whether the chosen content is suitable.

Run one complete example

The script creates a new repository with a single tracked text file. It commits the base line, changes and stages the file, then changes the file again. It prints the two diffs and inspects all three versions before making the next commit. Run it from an empty directory with Git 2.56.0 available.

Download the exact MIT-licensed example. The file printed here is the file that was executed, including the assertions after the commit.

#!/bin/sh
# SPDX-License-Identifier: MIT
set -eu
export TZ=UTC LC_ALL=C
export GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_GLOBAL=/dev/null
export GIT_AUTHOR_NAME='Example Author'
export GIT_AUTHOR_EMAIL='author@example.invalid'
export GIT_COMMITTER_NAME='Example Author'
export GIT_COMMITTER_EMAIL='author@example.invalid'
export GIT_AUTHOR_DATE='2026-10-25T12:00:00Z'
export GIT_COMMITTER_DATE='2026-10-25T12:00:00Z'
test "$(git --version)" = 'git version 2.56.0'
mkdir diff-example
cd diff-example
git -c init.templateDir= init -q -b main
printf 'base\n' > note.txt
git add note.txt
git commit -qm 'Base'
printf 'staged draft\n' > note.txt
git add note.txt
printf 'working draft\n' > note.txt
printf '%s\n' '--- staged difference ---'
git diff --cached -- note.txt
printf '%s\n' '--- working difference ---'
git diff -- note.txt
test "$(git show HEAD:note.txt)" = base
test "$(git show :note.txt)" = 'staged draft'
test "$(cat note.txt)" = 'working draft'
printf '%s\n' '--- staged version committed ---'
git commit -qm 'Commit staged draft'
test "$(git show HEAD:note.txt)" = 'staged draft'
test -z "$(git diff --cached -- note.txt)"
test -n "$(git diff -- note.txt)"
printf 'commit: staged draft committed; working draft remains\n'

Read the staged difference first

The staged section compares the committed base with the staged draft. Its removed line is base, and its added line is staged draft. The later working edit is absent from this comparison because it has not replaced the staged version. This gives the script a specific next-commit expectation before the commit is made.

Use the staged comparison to answer what is currently prepared. Read added and removed lines in context, including surrounding content that may explain the effect of the change. A file name in status is a useful prompt to inspect it; it is not a substitute for that inspection. The example uses one line only so the two endpoints remain obvious.

If the staged material is not what you want, decide whether to adjust staging, edit the file or stop and review the intended change. Those are separate actions. The unstaging guide explains a case where material leaves the next commit while the working edits remain. Reading a diff by itself changes none of these versions.

Then read the working difference

The working section compares staged draft with working draft. Its starting point is the staged version, not the original committed base. That is why its removed line differs from the removed line in the staged section. Both outputs can be correct at the same time: they describe different transitions in the same file.

This is a useful way to understand a file that status describes as both staged and modified. One difference belongs between the current commit and the prepared version; another belongs between the prepared version and the current file. Do not collapse them into a single claim that “the file is staged” and assume every later edit is included.

The assertions read the committed text, the indexed text and the working text independently. They establish the actual content of the three endpoints in this example. The diff output then explains the changes between those endpoints. Checking both views prevents an attractive-looking patch from being the only evidence that the setup was correct.

See what the commit actually saves

The commit in the example has no file arguments and does not request an automatic update of tracked files. It saves the prepared version. Afterward, the assertion reads staged draft from the new commit, the staged diff is empty, and the working diff still contains the later replacement. The working file has not silently become the committed version.

That result is useful when a task contains changes that belong in separate commits. It also reveals a common mistake: editing after staging, assuming the first add operation remains a live connection to the file, and then committing without checking the prepared content. Add records the content supplied at the time it runs2. It does not subscribe the staging area to future edits.

In a real project, inspect the result after committing as well as before. A clean staged diff tells you that the staging area matches the new commit for the inspected paths; it does not tell you that the working tree is clean, tests passed or every intended file was included. The example deliberately retains a working edit to make that limit visible.

Keep paths and scope explicit

Each diff in the printed example limits its view to note.txt. The separator before the path distinguishes the path from command options or revision arguments. With more files, a limited diff can make a focused review easier, but it also leaves other paths outside the view. Do not describe a path-limited inspection as a review of the entire repository.

Likewise, an empty diff must be interpreted with its endpoints and scope. An empty staged diff and a nonempty working diff are not contradictory. The first says there is no difference in the selected staged comparison; the second says there is still a difference in the selected working comparison. Naming those comparisons is more precise than saying that Git shows “no changes.”

Where Git 2.56.0 changes staging

The new resolved-conflict option is relevant when unmerged paths need to be staged after their markers have been removed. It does not change which endpoints these two diff forms compare. The stash comparison runs a selected conflict-resolution case and checks that unrelated working edits remain unstaged. This page’s ordinary tracked-file example does not use that conflict workflow.

The practical review order stays the same: identify the current commit, inspect what is staged, inspect what is still only in the working file, and decide which state should change next. Keep tests and task acceptance separate from the diff. A patch can explain a change accurately while the changed program still needs its own verification.

Not covered on this page

Sources

  1. git-diff — Git 2.56.0. ↩

  2. git-add — Git 2.56.0. ↩

How we test: methodology. Found a mistake? Corrections.