Home / Alt manpages / git-diff(1)

  • git-diff(1)
  • User command
  • linux

Read Git changes safely with git diff

You will use git diff to answer the questions that matter during a review: what changed in the working tree, what is staged, and how does one commit or branch differ from another? The examples use Git 2.43.0, installed from the local git-man package. Allow 10 to 15 minutes, assuming you already have a Git working tree.

This command only reads repository state unless you ask it to write a report with --output. It does not stage, commit, reset or discard anything. That makes it a good first command when you are not yet sure what happened.

Checkpoint: establish the repository and baseline

  1. Change into the repository you intend to inspect and confirm the Git version:

    $ cd /path/to/project
    $ git --version
    git version 2.43.0
    $ git status --short
     M src/example.c
    ?? notes.txt

The status output is not a diff, but it tells you which kinds of changes to look for. A leading M means the working file differs from the index. A leading M means the index differs from HEAD. An untracked file has no diff until you add it to the index, so inspect it separately with a suitable viewer before staging it.

1. Inspect unstaged changes

Run the command with no revision arguments:

$ git diff
diff --git a/src/example.c b/src/example.c
index 3d2f1ab..91c4e77 100644
--- a/src/example.c
+++ b/src/example.c
@@ -12,3 +12,4 @@ int main(void) {
     return 0;
 }
+/* explain the return value */

This compares the working tree with the index, also called the staging area. It shows changes you have made but have not staged. The patch format is the default. Lines beginning with - are removed from the old side, lines beginning with + are added on the new side, and the surrounding context has neither marker.

To focus on one path, put -- before the path. This is a useful habit when a filename could otherwise be mistaken for a revision:

$ git diff -- src/example.c docs/usage.md

If the output is long, page it with your normal terminal pager or limit the shape of the report. --stat gives a compact summary, while --name-only lists only changed paths:

$ git diff --stat
$ git diff --name-only
src/example.c
docs/usage.md

2. Inspect staged changes before committing

After git add, the ordinary command will no longer show those edits because they now match the index. Compare the index with the latest commit instead:

$ git diff --cached
$ git diff --staged

--staged is a synonym for --cached. With no commit named, Git compares the index with HEAD. This is the review that should happen immediately before a commit. If the repository has no HEAD yet, the command shows all staged changes.

A common distraction is seeing a clean git diff and assuming nothing changed. Check both views when you are unsure:

$ git diff --quiet; echo "unstaged status: $?"
$ git diff --cached --quiet; echo "staged status: $?"
unstaged status: 1
staged status: 0

Here, the first command found unstaged differences and the second found none. The --quiet option suppresses the patch; its exit status is the signal. Do not treat a non-zero status as a shell failure until you know which comparison you asked for.

3. Compare commits and branches without guessing about ranges

Name two endpoints to compare two snapshots:

$ git diff HEAD~1 HEAD -- src/example.c
$ git diff main feature/login --stat

The two-dot spelling, such as main..feature/login, is accepted as another way to compare two commits, but git diff is comparing endpoints, not listing every commit in a range. For the change introduced by a topic branch since it split from main, use three dots:

$ git diff main...feature/login --

That compares the merge base of the two names with the tip of feature/login. It answers "what does this branch add compared with where it started?" It does not answer "what commits exist on both sides?" Use git log for that question.

If you want your current work compared with a branch tip, one revision is enough:

$ git diff main --

This includes both staged and unstaged changes in the working tree relative to main. Use git diff --cached main when you specifically want the staged snapshot compared with that commit.

4. Make review output easier to trust

For a quick review, these options are practical:

  • --check warns about conflict markers and whitespace errors in the proposed change. Its definition of a whitespace error follows core.whitespace; by default that includes trailing spaces and a space followed by a tab in initial indentation.
  • --word-diff shows changed words instead of whole changed lines. Its default plain mode uses [-removed-] and {+added+}, which can be ambiguous if those delimiters occur in the input.
  • -U20 gives 20 lines of context instead of the usual three. More context can make a review clearer, but it also makes reports larger.
  • Disable colour when copying a report into logs or scripts, so terminal control codes do not become part of the data.

Run the whitespace check before committing:

$ git diff --cached --check
src/example.c:15: trailing whitespace.

A clean run prints nothing and exits successfully. A reported problem gives a non-zero status. Do not combine --check with --exit-code; the manual marks those options as incompatible.

5. Compare ordinary files when Git is not the point

--no-index compares two filesystem paths, including files outside a repository:

$ git diff --no-index -- /tmp/config.before /tmp/config.after
diff --git a/tmp/config.before b/tmp/config.after
index ...
--- a/tmp/config.before
+++ b/tmp/config.after

This form implies --exit-code, so identical files return zero and different files return one. A non-zero result can therefore mean "differences found", not "the command broke". It does not alter either input. Keep paths after -- when you want to make the boundary obvious.

Common traps and recovery

Nothing appears. You may be looking at the wrong comparison. Run git status --short, then check both git diff and git diff --cached. Untracked files are not included in either diff until they are staged.

The diff looks noisy. Check whether whitespace or generated files are involved. --ignore-space-at-eol ignores end-of-line whitespace differences; use it only when whitespace is not part of what you are reviewing. A cleaner display does not change the underlying files.

You used the wrong revision. Stop before staging or committing anything. Re-run with explicit names and a path separator, for example git diff HEAD~1 HEAD -- path/to/file. These inspection commands are reversible because they do not change repository state.

Be cautious with output redirection. git diff --output=review.patch writes a report, while shell redirection such as > review.patch truncates an existing file before Git starts. Choose a new filename if the old report matters.

Done means

  • You can distinguish working-tree changes from staged changes.
  • You have reviewed the exact staged patch with git diff --cached.
  • You know that two-dot and three-dot comparisons answer different questions for branch work.
  • You have used --check where whitespace or conflict markers could matter.
  • You have not confused an exit status of one, meaning differences found, with a failed inspection.