Topic · Foundations & Daily Workflow

Tracking Changes

Git feels calm when you can answer three questions at any moment: what changed, what is staged for the next commit, and what can I safely undo? This lesson builds that loop around status, diff, add, and restore.

The three places a change can live

Before the commands, anchor the model. A Git repository has a committed snapshot at HEAD, a staging area called the index, and the files you are editing in the working tree. Tracking changes is the act of comparing and moving content among those places.

Tracked, untracked, modified, and staged

A tracked file is one Git already knows about because it is in the last commit or has been staged. An untracked file exists in your working tree but is not in the last commit or the index. A tracked file becomes modified when its working-tree content differs from the index or HEAD. A change is staged when the index contains the exact content you intend to put in the next commit.

HEAD
Last committed snapshot

This is the baseline Git compares against. It answers: what did the project look like after the most recent commit?

Index
Next commit draft

This is not a folder you edit directly. It is Git's prepared snapshot for the next commit, built by staging content.

Working tree
Your live files

This is where your editor writes. It can contain experiments, mistakes, finished changes, and files Git has never seen.

The subtle part: git add stages content at that moment, not a magical live link to the file. If you stage a file, edit it again, and then commit, the commit gets the staged version unless you stage again. That is why a file can appear as both staged and modified.

Use status as your map

git status is the command you run before almost every Git decision. It tells you which branch you are on, whether your working tree is clean, which changes are staged, which tracked changes are not staged, and which files are untracked.

Terminalfull status
git status

When Git says nothing to commit, working tree clean, the index and working tree match HEAD. There is no staged content waiting to commit and no visible unstaged change. When Git lists sections, read them as locations:

Status section Meaning Useful next command
Changes to be committed The index differs from HEAD. This is what the next commit will record. git diff --staged
Changes not staged for commit Tracked files in the working tree differ from the index. git diff, then git add or git restore
Untracked files Files exist in the working tree but are not in HEAD or the index. git add if wanted, or leave for now

For a compact view, use short status:

Terminalshort status
git status --short
# or
git status -s

Short status uses two columns before the path. The left column describes the index; the right column describes the working tree. So M app.js means a modification is staged, M app.js means a tracked file is modified but not staged, MM app.js means one version is staged and another edit remains unstaged, and ?? notes.txt means the file is untracked.

Read unstaged changes with diff

git diff without extra arguments shows changes in your working tree that are not staged yet. More precisely, it compares the working tree to the index. That makes it your review tool before staging.

Terminalunstaged review
git diff
git diff README.md
git diff -- src/parser.js

In a patch, lines beginning with - are removed from the old side and lines beginning with + are added on the new side. The surrounding unchanged lines give context. You do not need to memorize every header immediately, but learn to read the body well enough to answer: is this change intentional, complete, and part of the commit I am about to make?

Review before staging

A useful loop is git status, then git diff, then git add. The order matters because it makes staging a deliberate choice instead of a cleanup reflex.

Inspect the next commit with diff --staged

Once you stage changes, plain git diff may go quiet because there are no unstaged differences left. That does not mean the next commit is empty. It means the difference moved into the index. To inspect staged content, compare the index to HEAD:

Terminalstaged review
git diff --staged
# same idea, older spelling
git diff --cached

This command is the most reliable answer to "what will my next commit contain?" It is also the best final check before writing the commit message. If git diff --staged contains two unrelated ideas, split them before committing. Your future self will thank you when debugging history later.

A file can be in two places

If you stage config.yml and then edit it again, git diff --staged config.yml shows the staged version, while git diff config.yml shows the additional unstaged edit. Git is not confused. It is tracking two different comparisons.

Stage intentionally with add

git add adds file contents to the index. For a new file, this starts tracking it. For a modified tracked file, this updates the staged version. For a deleted tracked file, broader forms such as git add -A stage the deletion too.

Terminalcommon staging commands
git add README.md
git add src/
git add -A
git add --patch

Use path-specific staging when you know exactly what belongs in the commit. Use git add -A when you have already reviewed the whole working tree and you truly want all additions, modifications, and deletions. The dangerous habit is not -A itself; the dangerous habit is using it as a substitute for reviewing.

Partial staging

Sometimes one file contains two changes: a bug fix and a formatting cleanup, or a feature and a debug print you do not want to keep. git add --patch (or git add -p) lets you stage selected hunks interactively.

Terminalpartial staging
git add -p src/search.js

In patch mode, Git asks about each hunk. The most common answers are y to stage the hunk, n to leave it unstaged, s to split a hunk when possible, e to edit the patch manually, and ? for help. This is one of the habits that turns commits from "whatever happened today" into small, reviewable snapshots.

Undo with restore

git restore is for restoring files in the working tree or the index from another source. Used carefully, it answers two different beginner questions: "How do I unstage this?" and "How do I throw away my local edit?"

Goal Command What it changes
Unstage a file, keeping your working-tree edits git restore --staged file The index only
Discard unstaged edits to a tracked file git restore file The working tree only
Interactively discard selected hunks git restore -p file Selected working-tree hunks
Restore both index and working tree git restore --staged --worktree file Both staged and unstaged content
Discard means discard

git restore file replaces your working-tree version of a tracked file with the version from the index by default. If the edit is not committed, staged, stashed, or saved elsewhere, this can delete work you meant to keep.

A daily tracking habit

The practical workflow is simple, but it rewards discipline. Treat the staging area as a draft of the next commit, not as a waiting room for everything you touched.

  1. Run git status to see the shape of the worktree.
  2. Run git diff to inspect unstaged edits before staging them.
  3. Stage only the content that belongs together with git add path or git add -p.
  4. Run git diff --staged to review the exact commit draft.
  5. Use git restore --staged or git restore -p if the draft includes the wrong content.
  6. Commit only when the staged diff tells one coherent story.

That loop is slower than "add everything, commit, hope" for about a week. After that, it becomes faster because your history becomes easier to review, revert, bisect, and explain.

Common pitfalls

Pitfall 1

Thinking git add file means "track this filename forever." It stages the file's current content. If you edit the file after staging, stage again or the next commit will contain the older staged version.

Pitfall 2

Trusting plain git diff after staging. Plain git diff shows unstaged changes. Once everything is staged, it can show nothing even though the next commit is full. Use git diff --staged for the commit draft.

Pitfall 3

Using git restore as a panic button. Restore is useful, but it can discard uncommitted edits. Read git status first and choose whether you mean to unstage, discard working-tree changes, or both.

Pitfall 4

Staging generated or secret files because they are untracked. Untracked means "Git does not know this file yet," not "this file should be committed." Inspect names carefully and set up ignore rules when a class of files should stay out of history.

Worked examples

Example 1: Stage a new README and verify it

You create a new README.md. At first, Git sees it as untracked:

Terminalnew file
git status --short
?? README.md

Stage it, then inspect the staged diff:

Terminalstage and review
git add README.md
git diff --staged README.md

If the diff is the README you meant to commit, it belongs in the next snapshot. If it includes scratch notes, edit the file and run git add README.md again to refresh the staged content.

Example 2: A file is staged and modified at the same time

You stage app.js, then notice and fix another line. Short status may show:

Terminaltwo comparisons
git status --short
MM app.js

The first M says the index differs from HEAD. The second M says the working tree differs from the index. Review both:

Terminalreview both versions
git diff --staged app.js
git diff app.js

If the second edit belongs in the same commit, run git add app.js again. If it belongs later, leave it unstaged or split it with patch mode.

Example 3: Stage only the bug fix, not the cleanup

You changed parser.js in two places: one real bug fix and one formatting cleanup. A whole-file git add parser.js would mix them. Use patch mode:

Terminalselect hunks
git add -p parser.js
# y = stage this hunk
# n = leave this hunk unstaged
# s = split the hunk if possible

Afterward, check your draft:

Terminalconfirm the draft
git diff --staged parser.js
git diff parser.js

The staged diff should contain the bug fix. The remaining plain diff can contain the cleanup for a later commit.

Example 4: Unstage without losing edits

You staged debug.log by accident. If the file should not be in the next commit, unstage it:

Terminalunstage
git restore --staged debug.log

This changes the index, not your working-tree file. Afterward, debug.log may appear as untracked or unstaged depending on whether Git already knew about it. If this kind of file should never be committed, the next topic on ignoring files will give it a permanent home outside status noise.

Example 5: Discard an unwanted tracked-file edit

You experimented in settings.json and want to throw away the unstaged working-tree edit. First review what you are about to lose:

Terminalreview before discard
git diff settings.json

If the diff is truly disposable, restore the file:

Terminaldiscard unstaged edit
git restore settings.json

Run git status --short again. The unstaged modification should be gone. If the file had staged changes too, those remain unless you also use --staged.

Sources & further reading

  • Git Basics - Recording Changes to the Repository Textbook Pro Git book, Git SCM

    The canonical narrative source for tracked versus untracked files, staging, short status, and the basic recording loop.

  • git-status Documentation Reference Git SCM

    The formal reference for long status, short status, porcelain output, and the two-column status codes.

  • git-diff Documentation Reference Git SCM

    The command reference for comparing the working tree, index, and commits, including --staged and --cached.

  • git-add Documentation Reference Git SCM

    The official reference for adding file contents to the index and interactively staging hunks with --patch.

  • git-restore Documentation Reference Git SCM

    The reference for restoring the working tree or index, including the difference between --staged and --worktree.

  • Saving Changes Tutorial Atlassian Git tutorials

    A secondary walkthrough of git add, git diff, and the everyday save-and-review workflow.