Skip to content

Inspecting Changes with git diff

Before saving a commit, Git allows us to inspect exactly what changed.

To understand the diff commands, remember the three important states:

  1. Working Tree: The files we are currently editing.
  2. Staging Area (Index): The version being prepared for the next commit.
  3. HEAD: The latest commit in the current branch.

Git provides two related commands:

Terminal window
git diff

and:

Terminal window
git diff --staged

They display different comparisons.

Working Tree

The files you are currently editing.

Staging Area

The version prepared for the next commit.

HEAD

The latest commit on the current branch.

Command Compares Question it answers
git diff Working Tree ↔ Staging Area What have I changed but not staged yet?
git diff --staged Staging Area ↔ HEAD What have I staged for the next commit?

What happens to the Staging Area after a commit?

Section titled “What happens to the Staging Area after a commit?”

After creating a commit, the Staging Area does not become literally empty.

Normally, its contents match the new commit:

Working Tree = Staging Area = HEAD

When tools say that the Staging Area is “empty,” they mean:

There are no staged differences waiting to be committed.

They do not mean that the Index contains no information. The Index still represents a complete proposed snapshot of the project, and immediately after a commit that snapshot normally matches HEAD.

Because all three states match, neither command displays any changes:

Terminal window
git diff
git diff --staged

Suppose the latest commit contains this version of auth.js:

console.log("Application started");
console.log("User login");

Immediately after the commit:

Working Tree = Staging Area = HEAD

We change auth.js to:

console.log("Application started");
console.log("User authenticated");

The new change exists only in the Working Tree:

Working Tree ≠ Staging Area = HEAD

Now run:

Terminal window
git diff

Git displays something similar to:

diff --git a/auth.js b/auth.js
index d32428a..0e33c45 100644
--- a/auth.js
+++ b/auth.js
@@ -1,2 +1,2 @@
console.log("Application started");
-console.log("User login");
+console.log("User authenticated");

This output appears because git diff compares the Working Tree with the Staging Area.

At this moment:

Terminal window
git diff --staged

shows nothing because we have not staged the new modification yet. The Staging Area still matches HEAD.

console.log("User login");

This line existed in the previous version but was removed or replaced.

The - sign is added by Git to explain the difference. It is not part of the JavaScript code.

console.log("User authenticated");

This is the new line that was added.

The + sign is also a diff marker and is not part of the code.

console.log("Application started");

This line did not change. Git displays it as context to help us locate the modification.

--- a/auth.js
+++ b/auth.js

These labels represent the old and new sides of the comparison.

The a/ and b/ prefixes are comparison labels. They do not mean that Git created two physical folders.

@@ -1,2 +1,2 @@

This is a hunk header. It identifies where this group of changes appears in the compared versions.

Beginners do not need to memorize its numbers yet.

Now run:

Terminal window
git add auth.js

git add updates the Staging Area with the current version of auth.js.

The state becomes:

Working Tree = Staging Area ≠ HEAD

Now the default command:

Terminal window
git diff

shows nothing.

This does not mean that the modification disappeared. It means there is no longer a difference between the Working Tree and the Staging Area.

To inspect what is staged for the next commit, run:

Terminal window
git diff --staged

Git displays:

diff --git a/auth.js b/auth.js
index d32428a..0e33c45 100644
--- a/auth.js
+++ b/auth.js
@@ -1,2 +1,2 @@
console.log("Application started");
-console.log("User login");
+console.log("User authenticated");

The output may look similar, but the comparison is now different:

Staging Area ↔ HEAD

Therefore, git diff --staged answers:

What changes are currently prepared for the next commit?

--staged does not stage the files. It only displays changes that were already staged using git add.

After reviewing the staged changes, we can create the commit:

Terminal window
git commit -m "Update authentication message"

The commit records the staged snapshot in the repository history.

After the commit, the states normally match again:

Working Tree = Staging Area = HEAD

Therefore:

Terminal window
git diff
git diff --staged

both display nothing.

Why do we need commits if the Staging Area contains a snapshot?

Section titled “Why do we need commits if the Staging Area contains a snapshot?”

The Staging Area is only a temporary preparation area for the next commit. It is not a permanent historical checkpoint.

Its contents can be changed whenever we run git add again.

A commit, however, creates a recorded point in the project history. It includes:

  • The project snapshot.
  • A commit ID.
  • A commit message.
  • Author information.
  • Date and time.
  • A link to its parent commit.

The Staging Area prepares the next snapshot, while the commit permanently records that snapshot in the project history.

A useful analogy is:

The Staging Area is the preview before taking a photograph. The commit is the photograph saved in the album.

Suppose we stage auth.js:

Terminal window
git add auth.js

Then we edit the same file again without running git add a second time.

The file now has two relevant versions:

  • The first modification is in the Staging Area.
  • The second modification exists only in the Working Tree.

In that situation:

Terminal window
git diff

shows the new unstaged modification.

While:

Terminal window
git diff --staged

shows the earlier modification already prepared for the next commit.

This demonstrates that git add stages the file’s contents as they exist at the moment the command is executed. Later edits are not staged automatically.

The default git diff normally does not display the contents of a completely untracked file because Git does not yet have an earlier tracked version to compare it with.

git diff
Working Tree ↔ Staging Area
Shows unstaged changes.
git diff --staged
Staging Area ↔ HEAD
Shows staged changes prepared for the next commit.