Home DevOps Git Diff — The Silent Merge That Deleted 200 Lines of Error Handling
Beginner 3 min · July 07, 2026
Git Diff: Spot Changes Before They Become Incidents

Git Diff — The Silent Merge That Deleted 200 Lines of Error Handling

A team merged a branch that looked clean in the PR diff, but 200 lines of error handling had been silently deleted.

N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.

Follow
Production
production tested
July 07, 2026
last updated
214
articles · all by Naren
Before you start⏱ 20 min
  • Basic programming fundamentals (variables, functions, control flow)
  • No prior devops experience needed — we start from first principles
 ● Production Incident
Quick Answer

git diff shows unstaged changes. git diff --staged shows staged changes. git diff main..feature compares two branches. git diff --word-diff highlights word-level changes.

✦ Definition~90s read
What is Git Diff?

git diff shows the differences between two states of your repository — working tree vs index, index vs HEAD, or any two commits. It is Git's primary tool for code review and change inspection.

Git diff is like a highlighter pen for your code.
Plain-English First

Git diff is like a highlighter pen for your code. It marks every line that was added (green) and every line that was deleted (red) between any two snapshots. Without it, reviewing changes would mean reading the entire file and trying to remember what it looked like before. Git diff is the foundation of every code review, every merge, and every debugging session.

⚙ Browser compatibility
Latest versions — ✓ supported
ChromeFirefoxSafariEdge

Git diff is the single most important command for code review and debugging. Every PR diff is powered by git diff. Every merge conflict shows you a three-way diff. Every bisect session uses diff to verify the fix. Yet most developers only use the default git diff output, missing powerful flags that catch silent regressions. This guide covers the essential diff modes, the incident where a PR diff hid 200 deleted lines, and how to use diff to prevent invisible changes from reaching production.

Diff Modes and When to Use Each

git diff (no arguments) shows unstaged changes — working tree vs index. git diff --staged (or --cached) shows staged changes (index vs HEAD). git diff <commit> shows changes since a specific commit. git diff <commit1>..<commit2> compares any two arbitrary commits.

For branches: git diff main..feature shows what's in feature that isn't in main. git diff main...feature (three dots) shows changes since the common ancestor — this is what GitHub PRs use.

Use --stat for a summary of changed files and line counts. Use --name-only to list only file paths. Use --name-status to include what type of change (A=added, D=deleted, M=modified, R=renamed).

01_diff_modes.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Basic diffs
git diff                          # working tree vs index
git diff --staged                # index vs HEAD (staged changes)
git diff HEAD                    # working tree vs last commit

# Compare branches
git diff main..feature           # what feature has that main doesn't
git diff main...feature          # since common ancestor (like GitHub PR)

# Summary views
git diff --stat main...feature   # changed files with line counts
git diff --name-only main..HEAD  # just file paths
git diff --name-status main..HEAD  # A/D/M per file

# Compare specific file
git diff main...feature -- src/app.py
Output
app.py | 10 ++++++++--
utils.py | 5 +----
README.md | 2 ++
3 files changed, 13 insertions(+), 4 deletions(-)
A src/new_feature.py
M src/app.py
D src/old_utils.py
Use `--diff-filter` to Focus on Specific Change Types
git diff --diff-filter=D shows only deleted files. --diff-filter=AM shows only added and modified. This is invaluable for catching accidental deletions in large PRs. Chain with --name-only for a quick checklist.
Production Insight
On PR templates, require the author to paste the output of git diff --stat main...HEAD and git diff --name-status main...HEAD. This forces awareness of every changed file and the type of change. On CI, add a step that runs git diff --diff-filter=D --name-only main...HEAD and warns if any non-trivial files were deleted (e.g., exclude lock files and generated code). Some teams enforce that deletions require explicit justification in the PR description.
Key Takeaway
Use git diff --stat and --name-status for a bird's-eye view. --diff-filter narrows to specific change types. Always expand all collapsed sections in GitHub's PR diff viewer before approving.

Word Diff, Ignore Whitespace, and Catching Invisible Changes

git diff --word-diff highlights changes at the word level instead of line level. This is ideal for documentation, prose, or when a single line has both additions and deletions. --word-diff-regex= allows custom word boundaries.

git diff -w ignores whitespace changes (spaces, tabs, indentation). --ignore-space-change ignores whitespace-only changes but still shows whitespace that affects meaning. --ignore-blank-lines ignores empty line changes.

git diff --check checks for whitespace errors (trailing spaces, spaces before tabs) and lists them. git diff --color-moved highlights moved blocks of code with different colors — crucial for refactors where code is reorganized.

02_advanced_diff.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Word-level diff (not line-level)
git diff --word-diff

# Ignore whitespace changes
git diff -w                       # ignore all whitespace
git diff --ignore-space-change    # ignore whitespace-only changes

# Check for whitespace errors
git diff --check

# Show moved code blocks in different color
git diff --color-moved

# Show function context around changes
git diff --function-context

# Show staged changes with 10 lines of context
git diff --staged -U10
Output
[-old_function()-]{+new_function(param)+}
payment_handler.py:7: trailing whitespace.
payment_handler.py:12: space before tab in indent.
{"type": "Moved", "code": "def validate_payment():"}
Whitespace-Only Diffs Can Hide Functional Changes
If a file was reformatted (Prettier, Black, rustfmt), the diff shows every line as changed. Reviewers can't see the actual logic change. Always separate formatting PRs from logic PRs. Use .git-blame-ignore-revs to keep blame useful after formatting commits.
Production Insight
Configure git diff --color-moved as the default via git config diff.colorMoved zebra. This makes moved code blocks visually distinct from added/deleted code in your terminal. In CI, run git diff --check to fail builds on whitespace errors. For large PRs (>500 lines changed), require the reviewer to run git diff main...HEAD --stat and git diff main...HEAD --name-status before approving.
Key Takeaway
--word-diff for prose, -w to skip whitespace, --check for whitespace errors, --color-moved to track reorganized code. Separate formatting PRs from logic PRs to keep diffs readable.
● Production incidentPOST-MORTEMseverity: high

The PR That Looked Clean but Deleted 200 Lines

Symptom
A developer submitted a PR with 150 additions and 50 deletions. The diff looked clean — a reasonable refactor. The PR was approved and merged. Three days later, a production incident was traced to missing error handling in 5 files. The error handling had been deleted in the merge, but the diff hid the deletions because they were in a different part of the file than the additions.
Assumption
The team assumed that git diff (and GitHub's PR diff) shows ALL changes. They did not realize that the default diff context (3 lines) can hide deletions when the diff is large.
Root cause
1. The PR touched 15 files with 150 additions and 50 deletions spread across different sections of the files. 2. GitHub's default diff shows 3 lines of context around each change. 3. In one file (payment_handler.py), the developer deleted a try/except block (20 lines) and added a new function (30 lines) in a different section of the same file. 4. The diff showed the addition with 3 lines of context but did not highlight the deletion because it was in a separate hunk. 5. The reviewer saw 150 additions and scrolled through GitHub's diff, which collapsed similar changes. 6. The deletion was never reviewed because it was buried in a collapsed section of the diff. 7. The missing error handling caused unhandled exceptions in production.
Fix
1. Immediately: restored the deleted try/except blocks via git revert of the offending commits. 2. Used git diff --check to verify no whitespace errors (also checks for subtle issues). 3. Required git diff --stat at the top of every PR description to show the change summary. 4. Added a PR template instructing reviewers to expand all collapsed diff sections before approving. 5. Added a CI check: git diff --diff-filter=D --name-only main...HEAD lists deleted files; git diff main...HEAD --name-status shows the change type for every file. 6. Team rule: if a PR has more than 10 files changed, require a secondary reviewer to look at only the deletions.
Key lesson
  • Default git diff (3-line context) can hide important changes.
  • Always expand all sections in GitHub's PR diff viewer.
  • Use git diff --stat for a bird's-eye view of change types.
  • Pay special attention to files with both additions and deletions — they often contain hidden regressions.
Production debug guideUse this when production is on fire5 entries
Symptom · 01
See unstaged local changes
Fix
Run git diff — shows working tree vs index
Symptom · 02
See staged changes (what will commit)
Fix
Run git diff --staged (or --cached)
Symptom · 03
Compare two commits or branches
Fix
Run git diff <commit1>..<commit2> or git diff main..feature
Symptom · 04
Find only the names of changed files
Fix
Run git diff --name-only — no content, just file paths
Symptom · 05
Detect whitespace-only changes
Fix
Run git diff --ignore-space-change or -w to ignore all whitespace
⚙ Quick Reference
2 commands from this guide
FileCommand / CodePurpose
01_diff_modes.shgit diff # working tree vs indexDiff Modes and When to Use Each
02_advanced_diff.shgit diff --word-diffWord Diff, Ignore Whitespace, and Catching Invisible Changes

Key takeaways

1
Default git diff shows 3 lines of context
expand all sections or use -U<N> for more
2
Use --stat and --name-status for a change summary, not just content diff
3
--diff-filter=D catches accidental file deletions
4
--word-diff for prose, -w to skip whitespace noise
5
--color-moved reveals reorganized code that looks like add+delete
6
Require git diff --stat main...HEAD in PR descriptions
N
Naren Founder & Principal Engineer

20+ years shipping production infrastructure and CI/CD at scale. Lessons pulled from things that broke in production.

Follow
Verified
production tested
July 07, 2026
last updated
214
articles · all by Naren
🔥

That's Git. Mark it forged?

3 min read · try the examples if you haven't

Previous
Git Config and Aliases: Customize Your Git Experience
42 / 47 · Git
Next
Git Log: Master Commit History Search and Filtering