← Back to Blog
·8 min read

How to Read a Git Diff

The unified diff format looks cryptic at first glance, but once you know what each part means, reading code changes becomes fast and intuitive. Here's a complete breakdown.

The anatomy of a git diff

Run git diff HEAD~1on any repository and you'll see something like this:

diff --git a/src/api.ts b/src/api.ts
index 3a1b2c4..7d8e9f0 100644
--- a/src/api.ts
+++ b/src/api.ts
@@ -12,7 +12,9 @@ async function fetchUser(id: string) {
   const response = await fetch(`/api/users/${id}`);
-  return response.json();
+  if (!response.ok) {
+    throw new Error(`HTTP ${response.status}`);
+  }
+  return response.json();
 }

Let's break this down line by line.

The header lines

The first two lines identify what's being compared:

  • diff --git a/src/api.ts b/src/api.ts — Git's internal comparison command. The a/ prefix is the original version, b/ is the new version.
  • index 3a1b2c4..7d8e9f0 100644 — The SHA-1 object hashes of both file versions in Git's object database, plus the Unix file permissions (100644 = regular file, 100755 = executable).
  • --- a/src/api.ts and +++ b/src/api.ts — The file names for the original (---) and new (+++) versions. For new files, the original is /dev/null; for deleted files, the new version is /dev/null.

The hunk header: @@ -12,7 +12,9 @@

This is the most important and most misread part of a diff. The format is:

@@ -<original_start>,<original_count> +<new_start>,<new_count> @@
  • -12,7 — In the original file, this hunk starts at line 12 and spans 7 lines.
  • +12,9 — In the new file, this hunk starts at line 12 and spans 9 lines (2 more because we added 3 lines and removed 1).

The numbers after the comma are the total line counts shown in the hunk — including context lines. If the count is 1, the comma and count are often omitted: @@ -12 +12,4 @@. Anything after the final @@ is optional context — Git often shows the function or class name that contains the change, which is extremely helpful for navigating large diffs.

The diff lines themselves

  • Lines starting with a space are context lines — they appear in both the original and new files unchanged. By default Git shows 3 context lines around each change. Increase this with git diff -U10.
  • Lines starting with -(red in colored output) were in the original and are gone in the new version. They've been deleted or replaced.
  • Lines starting with + (green in colored output) are new additions. They exist in the new version but not the original.

A modification shows as a deletion followed by an addition. The line wasn't edited in place — from the diff's perspective, the old version was deleted and the new version was added. Many diff viewers (GitHub, GitLab) apply additional word-level highlighting within the +/- lines to show exactly which words changed.

Useful git diff flags

  • git diff --stat — Summary view: files changed, insertions, deletions. Great for getting an overview of a large PR.
  • git diff --word-diff — Shows word-level changes inline rather than full line replacements. Useful when lines have small text changes.
  • git diff --ignore-whitespace — Ignores whitespace-only changes. Essential when comparing code that was reformatted.
  • git diff main...feature-branch — Shows all changes on the feature branch relative to where it diverged from main. This is the canonical way to see what a PR contains.
  • git diff --cached — Shows staged changes (what will be committed if you run git commit).

Reading merge conflict markers

When a merge conflict occurs, Git inserts markers directly into the conflicted file:

<<<<<<< HEAD
  return response.json();
=======
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  return response.json();
>>>>>>> feature/error-handling
  • Everything between <<<<<<< HEAD and ======= is your current branch's version.
  • Everything between ======= and >>>>>>> feature/... is the incoming branch's version.
  • To resolve: edit the file to contain the correct final version, delete all three marker lines, then git add and commit.