How to Read Git Diff Output
A complete guide to understanding unified diff format, hunk headers, change markers, and the most useful git diff options.
What Git Diff Shows
When you run git diff, Git compares two versions of your files and produces output describing exactly what changed. This output follows the unified diff format, a standardized way of representing text differences that has been used in Unix systems since the 1980s. Understanding this format is essential for every developer who uses version control, reviews pull requests, or debugs code changes.
A diff does not show complete files. Instead, it shows only the parts that changed, along with a few lines of surrounding context to help you understand where the change occurred. This makes diffs compact and easy to scan, even when the underlying files are thousands of lines long.
The Unified Diff Format
The unified diff format was introduced as an improvement over the older "context diff" format. It is more compact because it interleaves removed and added lines rather than showing them in separate blocks. Every major code hosting platform (GitHub, GitLab, Bitbucket) and every modern code review tool displays diffs in this format.
A complete unified diff consists of several parts: the file header, one or more hunks, and the change lines within each hunk. Let us examine each part in detail.
Understanding the File Header
Every diff begins with a header that identifies which files are being compared. The header uses --- and +++ prefixes to distinguish the old and new versions:
diff --git a/src/utils.js b/src/utils.js
index 3a4b5c6..7d8e9f0 100644
--- a/src/utils.js
+++ b/src/utils.jsThe first line shows that Git is comparing a/src/utils.js (the old version, typically from the last commit) with b/src/utils.js (the new version, typically your working directory changes). The a/ and b/ prefixes are conventions that Git adds to distinguish the two sides.
The index line shows the abbreviated SHA hashes of the file blobs and the file mode (100644 means a regular file with standard permissions). The --- line marks the old file and +++ marks the new file.
Reading Hunk Headers
Each changed region in the file is called a "hunk." Hunks are introduced by headers enclosed in @@ markers:
@@ -15,7 +15,9 @@ function processData(input) {This header contains two critical pieces of information:
-15,7refers to the old file: this hunk starts at line 15 and spans 7 lines. The minus sign indicates this is the "before" state.+15,9refers to the new file: this hunk starts at line 15 and spans 9 lines. The plus sign indicates this is the "after" state.
The text after the second @@ (in this case, function processData(input) {) is a context hint showing the nearest enclosing function or class definition. This is not part of the diff format itself but a Git-specific enhancement that helps you quickly locate where in the code the change occurs.
The Meaning of +, -, and Context Lines
Within each hunk, every line is prefixed with one of three characters:
+(plus): This line was added in the new version. It appears only in the modified file.-(minus): This line was removed from the old version. It no longer exists in the modified file.- (space): This line is unchanged. It exists in both versions and is shown for context. By default, Git shows 3 context lines above and below each change.
Here is an example showing all three types:
@@ -10,6 +10,8 @@ const config = {
timeout: 5000,
retries: 3,
- baseURL: "http://localhost:3000",
+ baseURL: process.env.API_URL,
+ headers: {
+ "Content-Type": "application/json",
+ },
debug: false,
};Reading this hunk: the timeout and retries lines are unchanged context. The hardcoded baseURL was removed and replaced with an environment variable. Two new lines were added to include a headers object. The debug line is unchanged context below the change.
Common Git Diff Options
Git provides many flags that modify how diffs are displayed. Here are the most useful ones:
--stat: Summary Statistics
The --stat flag shows a high-level summary instead of the full diff. It lists each changed file with a bar chart showing the number of insertions and deletions:
$ git diff --stat
src/config.ts | 12 ++++++------
src/api/client.ts| 45 ++++++++++++++++++++++++++++++++++-----------
tests/api.test.ts| 8 ++++++++
3 files changed, 42 insertions(+), 17 deletions(-)This is extremely useful for quickly understanding the scope of a change before diving into the details.
--name-only: Just the Filenames
When you only need to know which files changed, --name-only prints just the file paths with no diff content.
--word-diff and --color-words
Standard line-based diffs can be hard to read when only a single word on a line changed. The --word-diff flag shows changes at the word level, wrapping removed words in [-...-] and added words in {+...+}. The --color-words flag does the same but uses terminal colors instead of markers, producing a cleaner display.
Git Diff Variants
The behavior of git diff changes depending on what you are comparing:
git diff: Compares the working directory with the staging area (index). Shows unstaged changes.git diff --staged(or--cached): Compares the staging area with the last commit (HEAD). Shows what will be included in your next commit.git diff HEAD: Compares the working directory with the last commit. Shows all changes, whether staged or not.
Diffing Specific Files and Commits
You can narrow the diff to specific files by appending paths: git diff -- src/config.ts. To compare between two specific commits, provide their hashes: git diff abc123 def456. You can also compare branches: git diff main..feature-branch.
For comparing a file at different points in history, use: git diff HEAD~3 -- src/utils.js to see how a file has changed over the last three commits.
Interactive Example: Try It Yourself
The best way to internalize the diff format is to practice reading them. Paste any two text blocks into our Diff Checker tool and observe the output. Try switching between line, word, and character granularity to see how the same changes are represented at different levels of detail.
For JSON-specific diffs where formatting changes should be ignored, switch to our JSON diff mode which compares the data structure rather than the text representation. Learn more about this approach in our JSON Diff guide.
Tips for Reading Diffs Effectively
After years of reviewing pull requests, experienced developers develop strategies for reading diffs efficiently:
- Start with
--statto understand the scope before reading the full diff. - Read context lines first to understand where in the code the change is happening, then read the actual changes.
- Use word-level diffs for configuration files, documentation, and prose where line-level diffs are too coarse.
- Pay attention to the hunk headers since the function name hint tells you which function is being modified without scrolling up.
- Watch for whitespace-only changes which can obscure real modifications. Use
-wto ignore whitespace.
Further Reading
- Git diff documentation
Official Git reference for the diff command and its options.
- Pro Git — Viewing Changes
The Pro Git book chapter on tracking and viewing file changes.
- GNU diffutils documentation
Documentation for the GNU diff, diff3, sdiff, and cmp utilities.