Skip to content
masterclass Buddy

Git · Command

git diff

git diff shows line-level differences between two snapshots in a repository: the working tree, the index (staging area), commits, trees, or blobs. Outside a repository, or with --no-index, it compares two paths on disk.

Last updated

How it works

git diff always compares two endpoints and prints a unified patch by default. Which endpoints it uses depends on the arguments. With no revision, it compares the index with the working tree. With --cached, it compares a commit (HEAD by default) with the index. With one commit, it compares that commit with the working tree. With two commits, it compares the two trees. Path arguments after -- narrow the comparison to those files or directories. Other options change the output (stat, names only, raw, word diff), the diff algorithm, how whitespace is treated, and rename detection.

Syntax

git diff [<options>] [<commit>] [--] [<path>…]
git diff [<options>] --cached [--merge-base] [<commit>] [--] [<path>…]
git diff [<options>] [--merge-base] <commit> [<commit>…] <commit> [--] [<path>…]
git diff [<options>] <commit>...<commit> [--] [<path>…]
git diff [<options>] <blob> <blob>
git diff [<options>] --no-index [--] <path> <path> [<pathspec>…]

Parameters

NameTypeDescription
<commit>revision (commit or tree-ish)The snapshot to compare against. Give one to compare it with the working tree, or two to compare them with each other. A..B means the same as A B, and A...B starts from the merge base of A and B.
--cached / --stagedflagCompares the index with a commit, which is HEAD if you don't name one. It shows what the next commit would contain.
--merge-baseflagUses the merge base of the given commit and HEAD (or of the two given commits) as the 'before' side, instead of the commit itself.
--no-indexflagCompares two paths on the filesystem, even if they are not tracked by Git.
<path>…pathspecRestricts the diff to the named files, or to every file under the named directories.
<blob> <blob>object namesCompares the raw contents of two blob objects.

Returns

Prints a patch (or a stat, name list, or raw output, depending on the options) to stdout. With --exit-code or --quiet, the exit status is 1 if there are differences and 0 if there are none. --no-index implies --exit-code.

Key points

  • Run with no arguments, git diff shows only the edits that are not yet staged. Changes you have already staged with git add do not appear.
  • git diff --cached (also spelled --staged) compares the index with HEAD by default. This is how you check exactly what git commit is about to record.
  • The three-dot form A...B diffs B against the common ancestor of A and B. It shows only the work done on B's side since the two branches diverged.
  • --exit-code makes git diff behave like diff(1) for scripts: it exits with 1 if there are differences and 0 if there are none. --quiet does the same without printing anything.
  • --check looks for leftover conflict markers and whitespace problems in the changes. This makes it useful in pre-commit hooks.
  • --name-only and --name-status print only file names, with or without a status letter, and no patch. They are handy for piping into other tools.
  • You can switch the diff algorithm with --diff-algorithm (myers, minimal, patience, histogram). Myers is the default.

Common mistakes

  • In git diff, A..B and A...B do not mean the commit ranges they mean in git log. git diff only ever compares two endpoint snapshots.
  • --check exits with a non-zero status when it finds problems, and it cannot be combined with --exit-code.
  • When you use --no-index on two directories, any extra pathspecs must be relative paths.
  • A diff produced through a textconv filter is meant for people to read. You cannot apply it as a patch.

Example: Review unstaged, staged and branch changes

#!/usr/bin/env bash
set -euo pipefail

# Unstaged edits (working tree vs index)
git diff

# What the next commit will contain (index vs HEAD)
git diff --staged --stat

# Everything since the last commit, ignoring whitespace
git diff -w HEAD

# Files changed on this branch since it forked from main
git diff --name-status main...HEAD

# Fail a CI step if the working tree is dirty
if ! git diff --quiet; then
  echo "Uncommitted changes found" >&2
  exit 1
fi

# Block whitespace errors / conflict markers in staged changes
git diff --cached --check

The script shows the main comparison modes and then uses --quiet and --check as automated checks.

When to use it

Pre-commit review

Before committing, run git diff --staged to make sure only the intended hunks are staged.

Pull request scope

Run git diff --stat main...feature to see which files a feature branch changed relative to where it forked from main.

CI cleanliness check

After code generation or formatting, a CI job runs git diff --exit-code and fails if any files changed.

Comparing untracked files

Use git diff --no-index old.json new.json to get Git's diff output for two arbitrary files.

Interview questions

QWhat is the difference between git diff, git diff --cached, and git diff HEAD?

Plain git diff compares the working tree with the index, so it shows edits that are not yet staged. git diff --cached (or --staged) compares the index with HEAD, which is what the next commit will contain. git diff HEAD compares the working tree with HEAD, so it covers both staged and unstaged changes.

Follow-up: What does git diff --cached show on a brand-new repository with no commits? HEAD doesn't exist yet, so it shows all staged changes.

QHow do git diff A..B and git diff A...B differ?

A..B means the same as git diff A B: it compares the two tips directly. A...B compares B with the merge base of A and B, so you see only the changes made on B's side since the branches diverged. For git diff these are endpoint comparisons, not the commit ranges they mean in git log.

Follow-up: How else can you get the A...B result? Use git diff --merge-base A B, which is equivalent to git diff $(git merge-base A B) B.

QHow would you use git diff in a script to detect changes?

Use --exit-code, which returns 1 when there are differences and 0 when there are none. Use --quiet to get the same exit status without any output. For hooks, --check reports whitespace errors or conflict markers and exits non-zero when it finds them.

Follow-up: Can you combine --check with --exit-code? No. The docs say --check is not compatible with --exit-code, so run them as separate commands.

  • git-add
  • git-status
  • git-show
  • git-log
  • git-difftool
  • git-range-diff
  • git-apply
  • git-merge-base

Source: Git - git-diff Documentation. Checked 2026-11-02.