← Back to all posts

Staged vs Unstaged Changes: Predict Your Next Commit

Edit one file twice to see why git add stores a snapshot, how two diffs explain MM status, and which contents the next ordinary commit records.

A rounded orange Git mark with connected nodes representing repository states

You edit a file, run git add, edit it again, and commit. The second edit is missing from the commit. Git did not lose it: git add stored the earlier version in the index, and an ordinary git commit recorded that snapshot.

Staged and unstaged describe comparisons between states, not two permanent categories of files. The same tracked file can have both kinds of change at once.

Make the difference visible in one file

Use Bash, Git, and an empty practice directory. These commands were checked with Git 2.55.0. They create only a local repository.

practice_dir=$(mktemp -d)
cd "$practice_dir"
git init -b main
git config user.name 'Practice Learner'
git config user.email 'learner@example.invalid'
printf 'port=8000\n' > service.conf
git add service.conf
git commit -m 'Baseline'

printf 'port=8001\n' > service.conf
git add service.conf
printf 'port=8002\n' > service.conf

There are now three contents worth inspecting:

State Inspection Port
HEAD, the current commit git show HEAD:service.conf 8000
Index, the candidate snapshot git show :service.conf 8001
Working tree, the file on disk cat service.conf 8002

The colon form reads the index entry. It is particularly useful when a file has been edited again after staging: the editor shows the working tree, not the candidate commit.

Run git status --short. You should see:

MM service.conf

For this ordinary, non-conflicted tracked file, the first column describes the index relative to HEAD, and the second describes the working tree relative to the index. The status manual also explains untracked paths and the different meanings used for conflicts.

Ask each diff a precise question

git diff --cached -- service.conf
git diff -- service.conf

The cached diff asks what the index would change relative to HEAD. It shows:

-port=8000
+port=8001

The plain diff asks what remains on disk beyond the staged snapshot:

-port=8001
+port=8002

Neither diff alone describes every edit since the last commit. If you want the working file compared directly with HEAD, use git diff HEAD -- service.conf. That answers a different question and does not tell you which part is already staged. Git’s diff documentation defines these comparisons.

A useful pre-commit routine is therefore to read the cached diff to review the proposed commit, then the plain diff to decide what is being left behind. An empty plain diff does not mean there is nothing to commit; it can mean all tracked edits have been staged.

Commit the snapshot and inspect the remainder

Without running git add again:

git commit -m 'Commit staged port'
git show HEAD:service.conf
cat service.conf
git status --short

The commit contains 8001. The working file still contains 8002, and status now reports:

 M service.conf

The index and HEAD agree after the commit. The remaining change is between the index and the file on disk. You can stage and commit that change separately, or discard it if it was only a scratch edit.

This example intentionally uses ordinary git commit. Options and path arguments can change the input selection. In particular, git commit -a stages modifications and deletions of already tracked files before committing; it does not automatically include new untracked files. Read the commit manual before treating that shortcut as equivalent to a reviewed index.

Decide the next snapshot without guessing

If both edits belong together, run git add service.conf before committing. That replaces the index entry with the current file contents. If only part of a larger diff belongs in the commit, use git add -p -- service.conf and review git diff --cached afterward. Patch selection operates on changes; it does not continuously track future edits to the file.

To unstage while preserving the working file:

git restore --staged -- service.conf

To discard only the working-tree changes relative to the current index:

git restore -- service.conf

Those two commands have different destinations. The restore/reset/revert walkthrough follows that distinction through an undo and a history reversal.

New files add one more boundary: git diff does not display the contents of a completely untracked file. Read git status, inspect the file, then stage it deliberately. Avoid replacing a precise git add service.conf with git add . just to make the status display shorter.

For guided practice, LabEx lists Working with Files and Staging Area in its Git course. After any exercise, predict the contents of the next commit before making it, then check git show HEAD:<path>. Getting that prediction right is a better checkpoint than merely obtaining a clean status.

References

  • Git status — interpreting the index and working-tree columns.
  • Git diff — selecting the states being compared.
  • Git commit — the index snapshot and alternative content-selection modes.
  • Git add — updating the index and selecting patches.