Pre-commit review
Before committing, run git diff --staged to make sure only the intended hunks are staged.
Git · Command
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
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.
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>…]| Name | Type | Description |
|---|---|---|
<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 / --staged | flag | Compares the index with a commit, which is HEAD if you don't name one. It shows what the next commit would contain. |
--merge-base | flag | Uses 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-index | flag | Compares two paths on the filesystem, even if they are not tracked by Git. |
<path>… | pathspec | Restricts the diff to the named files, or to every file under the named directories. |
<blob> <blob> | object names | Compares the raw contents of two blob objects. |
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.
#!/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 --checkThe script shows the main comparison modes and then uses --quiet and --check as automated checks.
Before committing, run git diff --staged to make sure only the intended hunks are staged.
Run git diff --stat main...feature to see which files a feature branch changed relative to where it forked from main.
After code generation or formatting, a CI job runs git diff --exit-code and fails if any files changed.
Use git diff --no-index old.json new.json to get Git's diff output for two arbitrary files.
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.
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.
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-addgit-statusgit-showgit-loggit-difftoolgit-range-diffgit-applygit-merge-base