Home DevOps Git Diff — The Silent Merge That Deleted 200 Lines of Error Handling
Beginner 6 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 12, 2026
last updated
2,406
articles · all by Naren
Before you start⏱ 20 min
  • Git 2.x installed, basic familiarity with Git commands (init, add, commit, branch, merge), a terminal or command line, and a Git repository to practice with.
 ● 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.

Why Git Diff Is Your First Line of Defense

Every production incident starts as a change. Whether it's a misconfigured environment variable, a logic error in a hotfix, or an accidental file permission change, the root cause is always a diff. Git diff is the tool that surfaces these changes before they hit production. In this section, we'll cover the basics: comparing working directory to staging, staging to HEAD, and arbitrary commits. You'll learn to read diff output, understand hunks, and spot anomalies like unexpected deletions or additions. A common mistake is assuming git diff only shows unstaged changes — but with --cached you can review what's about to be committed. We'll also touch on git diff HEAD~1 to see the last commit's changes. By the end, you'll treat git diff as a mandatory pre-deployment step, not an afterthought.

terminalBASH
1
2
3
4
5
6
7
8
9
10
11
# Compare working directory vs staging
git diff

# Compare staging vs last commit
git diff --cached

# Compare last commit vs the one before
git diff HEAD~1 HEAD

# Compare two specific commits
git diff abc123 def456
Output
diff --git a/src/config.js b/src/config.js
index e69de29..b5b3e2a 100644
--- a/src/config.js
+++ b/src/config.js
@@ -0,0 +1,3 @@
+const API_URL = 'https://api.prod.example.com';
+const TIMEOUT = 5000;
+export { API_URL, TIMEOUT };
💡Always diff before commit
Run git diff --cached before every commit. It catches accidental includes like API keys, large binary files, or debug code.
📊 Production Insight
In 2021, a misconfigured S3 bucket policy was pushed to production because the team didn't diff the Terraform change. The diff would have shown the Effect: Allow for Principal: * — a classic 'public bucket' incident.
🎯 Key Takeaway
Git diff reveals every change before it becomes permanent — use it as a pre-commit review.
git-diff THECODEFORGE.IO Git Diff Defense Layers Layered approach to protect error handling code Code Repository Feature Branch | Base Branch | Merge Commit Diff Analysis Unstaged Diff | Staged Diff | Branch Diff Filtering Layer File Filter | Line Filter | Word Diff Automation Layer CI/CD Pipeline | Diff Check Script | Blame Annotation Visual Review Terminal Diff | Visual Diff Tool | Patch Output THECODEFORGE.IO
thecodeforge.io
Git Diff

Diffing Branches: Catch Merge Conflicts Early

Merge conflicts are a leading cause of deployment delays. But you don't have to wait for a merge to discover them. Use git diff branch1..branch2 to see the differences between two branches before merging. This is especially useful when reviewing a feature branch against main. You can also use three-dot syntax (git diff branch1...branch2) to see changes that occurred in branch2 since it diverged from branch1. This helps isolate the feature's changes from main's updates. We'll walk through a realistic scenario: a developer adds a new endpoint while another modifies the same file. By diffing early, you can spot overlapping changes and coordinate before a conflict arises. Pro tip: integrate this into your CI pipeline to fail builds if a diff exceeds a certain size or touches critical files.

terminalBASH
1
2
3
4
5
6
7
8
9
10
11
12
# Compare two branches (full diff)
git diff main..feature/new-endpoint

# Compare changes on feature branch since diverging from main
git diff main...feature/new-endpoint

# See only file names that differ
git diff --name-only main..feature/new-endpoint

# Check for conflicts before merging
git merge --no-commit --no-ff main
# Then inspect conflicts with git diff
Output
diff --git a/src/routes/user.js b/src/routes/user.js
index 1234567..89abcde 100644
--- a/src/routes/user.js
+++ b/src/routes/user.js
@@ -10,6 +10,12 @@ router.get('/profile', (req, res) => {
res.json(user);
});
+router.post('/profile', (req, res) => {
+ // New endpoint added by feature branch
+ updateProfile(req.body);
+ res.status(201).send('Profile updated');
+});
+
// Existing endpoint modified by main branch
router.put('/profile', (req, res) => {
- // old implementation
+ // new implementation with validation
⚠ Three-dot vs two-dot confusion
git diff A..B shows all changes from A to B. git diff A...B shows changes on B since it diverged from A. Use three-dot for feature branch reviews.
📊 Production Insight
A team once merged a feature that deleted a critical database migration because the diff with main wasn't checked. The migration file was removed in the feature branch, and the diff would have shown the deletion.
🎯 Key Takeaway
Diff branches early to detect conflicts and reduce merge hell.

Filtering Diffs: Focus on What Matters

Not all changes are equal. When reviewing a diff, you want to ignore whitespace, binary files, or generated code. Git diff provides flags to filter: --ignore-space-change (or -b) ignores whitespace-only changes; --ignore-all-space (-w) ignores all whitespace; --diff-filter lets you show only added (A), deleted (D), modified (M), etc. You can also exclude paths using :(exclude)pattern. For example, git diff -- '.js' ':(exclude).min.js' shows only non-minified JS changes. This is critical when reviewing large PRs where noise from formatting changes hides real logic changes. We'll also cover git diff --stat for a summary of changed files and line counts, and git diff --compact-summary for a condensed view. Use these to triage quickly.

terminalBASH
1
2
3
4
5
6
7
8
9
10
11
# Ignore whitespace changes
git diff -b

# Show only added files
git diff --diff-filter=A HEAD~1

# Exclude generated files
git diff -- '*.js' ':(exclude)*.min.js' ':(exclude)*.bundle.js'

# Summary of changes
git diff --stat main..feature
Output
src/index.js | 10 ++++++++++
src/utils.js | 5 +----
2 files changed, 11 insertions(+), 4 deletions(-)
🔥Use --diff-filter for code review
When reviewing a PR, run git diff --diff-filter=AM main...feature to see only added and modified files, skipping deletions that might be noise.
📊 Production Insight
A developer once committed a file with trailing whitespace that triggered a linter in CI, failing the build. Using git diff -b would have shown no real changes, but the whitespace diff was missed.
🎯 Key Takeaway
Filter diffs to ignore noise and focus on meaningful changes.
Terminal Diff vs Visual Diff Tools Trade-offs for catching error handling deletions Terminal Diff Visual Diff Tool Line-level visibility High with context lines Very high with side-by-side view Word-level changes Requires --word-diff flag Built-in highlighting Binary file support Limited to text output Graphical binary comparison Automation integration Easy in CI/CD scripts Requires GUI or headless mode Speed for large diffs Fast and lightweight Slower with rendering overhead THECODEFORGE.IO
thecodeforge.io
Git Diff

Word Diff and Highlighting: Spot Subtle Changes

Sometimes a single character change can break production. Git's word diff mode (--word-diff) highlights changes at the word level instead of line level. This is invaluable for spotting typos in variable names, string literals, or configuration values. Combine with --word-diff-regex to define your own word boundaries. For example, git diff --word-diff-regex='[a-zA-Z0-9_]+' will highlight identifier changes. We'll also cover --color-words for colored output. In a real scenario, a developer changed TIMEOUT=5000 to TIMEOUT=500 — a line diff shows the whole line changed, but word diff highlights just the number. This makes code review faster and more accurate. Use it when reviewing config files, JSON, or any file with long lines.

terminalBASH
1
2
3
4
5
6
7
8
9
10
11
# Word diff (default)
git diff --word-diff

# Word diff with custom regex (identifiers)
git diff --word-diff-regex='[a-zA-Z0-9_]+'

# Color words
git diff --color-words

# Example output for a config change
git diff --word-diff config.js
Output
diff --git a/config.js b/config.js
index abc..def 100644
--- a/config.js
+++ b/config.js
@@ -1,4 +1,4 @@
const API_URL = 'https://api.prod.example.com';
-const TIMEOUT = 5000;
+const TIMEOUT = 500;
export { API_URL, TIMEOUT };
💡Use word diff for config reviews
Configuration files often have long lines with single value changes. Word diff makes those changes pop.
📊 Production Insight
A single-character typo in a regex pattern caused a production outage. The line diff showed the whole regex changed, but word diff would have highlighted the missing backslash.
🎯 Key Takeaway
Word diff reveals character-level changes that line diffs hide.

Diffing Binary Files: Not All Changes Are Text

Binary files like images, PDFs, or compiled binaries can't be diffed meaningfully with text diff. But you can still track changes using git diff --binary which outputs a binary patch (base64 encoded). For images, you can use external tools like git diff --word-diff won't work, but you can configure a custom diff tool (e.g., exiftool for metadata, or imagemagick for visual diff). We'll show how to set up .gitattributes to define diff drivers for specific file types. For example, *.png diff=exif to compare EXIF data. This is crucial for detecting changes in assets that could affect production behavior (e.g., a compressed image losing quality). We'll also cover git diff --check to detect whitespace errors in binary files that might be embedded.

.gitattributesBASH
1
2
3
4
5
6
# Use exiftool to diff image metadata
*.png diff=exif
*.jpg diff=exif

# Use a custom script for binary diff
*.bin diff=binarydiff
⚠ Binary diffs can be huge
Binary patches are base64 encoded and can bloat your repository. Avoid committing large binary files; use Git LFS instead.
📊 Production Insight
A team once committed a recompiled binary that had debug symbols enabled, causing memory leaks in production. A binary diff would have shown the file size increase, alerting them.
🎯 Key Takeaway
Binary diffs require special handling — configure custom diff drivers for meaningful reviews.

Automating Diff Checks in CI/CD

Manual diff reviews are error-prone. Automate diff checks in your CI pipeline to enforce policies. For example, you can run git diff --check to detect whitespace errors, or git diff --name-only to ensure no sensitive files (like .env) are included. Use git diff --exit-code to fail the build if there are uncommitted changes (ensuring reproducibility). We'll show a GitHub Actions workflow that runs on pull requests: it checks for forbidden file patterns, enforces a maximum diff size, and validates that all changes have corresponding tests. This catches issues before human review. We'll also discuss using git diff with jq to parse JSON config changes and validate against a schema. Automation turns diff from a passive tool into an active guard.

.github/workflows/diff-check.ymlYAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
name: Diff Check
on: [pull_request]
jobs:
  diff-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      - name: Check for forbidden files
        run: |
          if git diff --name-only origin/main...HEAD | grep -E '\.env$'; then
            echo "Error: .env files should not be committed"
            exit 1
          fi
      - name: Check diff size
        run: |
          LINES=$(git diff origin/main...HEAD --stat | tail -1 | awk '{print $4}')
          if [ "$LINES" -gt 500 ]; then
            echo "Error: Diff exceeds 500 lines"
            exit 1
          fi
      - name: Check whitespace errors
        run: git diff --check origin/main...HEAD
🔥Fail fast with automated diffs
Integrate diff checks early in CI to catch issues before they reach human reviewers. It saves time and prevents incidents.
📊 Production Insight
A company's CI pipeline once allowed a PR that added a .env.production file with real credentials. An automated diff check for forbidden patterns would have blocked it.
🎯 Key Takeaway
Automate diff checks in CI to enforce policies and catch issues early.

Advanced Diff Techniques: Patches and Blame

Beyond simple comparisons, git diff can generate patches that can be applied later with git apply. This is useful for hotfixes or sharing changes without pushing. Use git diff > fix.patch to create a patch file, then git apply fix.patch on another branch. Also, combine diff with git blame to see who last modified a line and when. Run git blame file.js and then git diff <commit>^! to see the exact changes in that commit. This is powerful for debugging: when a bug appears, you can blame the line, then diff that commit to understand the change. We'll also cover git log -p which shows the diff for each commit in a log. These techniques help trace the origin of changes and understand context.

terminalBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
# Create a patch
git diff HEAD~1 > hotfix.patch

# Apply a patch
git apply hotfix.patch

# See diff for a specific commit
git diff abc123^!  # equivalent to git show abc123

# Combine blame and diff
git blame src/app.js
# Then diff the commit that introduced a bug
git diff 1234567^!
Output
commit 1234567
Author: Jane Doe <jane@example.com>
Date: Mon Jan 1 12:00:00 2024
diff --git a/src/app.js b/src/app.js
index abc..def 100644
--- a/src/app.js
+++ b/src/app.js
@@ -10,7 +10,7 @@ function handleRequest(req, res) {
const data = req.body;
- const result = processData(data);
+ const result = processData(data, true); // added flag
res.json(result);
}
💡Use patches for hotfixes
When you need to apply a fix to multiple branches without merging, create a patch and apply it selectively.
📊 Production Insight
During a production outage, a team used git blame to find the commit that introduced a null pointer exception. The diff showed a missing null check that was accidentally removed.
🎯 Key Takeaway
Patches and blame give you surgical control over changes and their history.

Visual Diff Tools: Beyond the Terminal

While terminal diffs are powerful, visual diff tools can make complex changes easier to understand. Tools like git difftool open external diff viewers (e.g., Meld, Beyond Compare, or VS Code's built-in diff). Configure them with git config diff.tool meld. For large refactors, a side-by-side view helps spot moved code or structural changes. We'll show how to set up difftool and difftool-helper for directory diffs. Also, many IDEs have integrated diff viewers that highlight changes inline. However, beware: visual tools can hide the raw diff's precision. Use them for overview, but always verify with git diff for exact changes. We'll also cover git difftool --dir-diff to compare entire directories.

terminalBASH
1
2
3
4
5
6
7
8
9
10
11
12
# Configure Meld as difftool
git config --global diff.tool meld

# Launch difftool for staged changes
git difftool --cached

# Directory diff
git difftool --dir-diff main..feature

# Use VS Code as difftool
git config --global diff.tool vscode
git config --global difftool.vscode.cmd 'code --wait --diff $LOCAL $REMOTE'
🔥Visual tools for complex diffs
When a single file has many changes, a visual diff tool can help you see the structure. But always double-check with raw diff for exact line changes.
📊 Production Insight
A developer used a visual diff to review a refactor and missed a deleted import that caused a runtime error. The raw diff would have shown the deletion clearly.
🎯 Key Takeaway
Visual diff tools complement terminal diffs for complex reviews.

Diffing in Production: Hotfixes and Rollbacks

When a production incident occurs, you need to quickly understand what changed. Use git diff between the current deployment and the previous known-good commit. If you use Git tags for releases, git diff v1.2.0 v1.2.1 shows exactly what went out. For hotfixes, create a branch from the release tag, apply the fix, and diff against the tag to verify only the fix is included. Also, git diff can help with rollbacks: generate a reverse patch with git diff R...R^ and apply it to revert. But be careful — reverting a diff can introduce new issues if dependencies changed. We'll cover a real scenario: a hotfix that accidentally included a debug log statement. The diff would have shown the extra console.log. Always diff hotfixes before deploying.

terminalBASH
1
2
3
4
5
6
7
8
9
10
11
12
# Compare two release tags
git diff v1.2.0 v1.2.1

# Create a hotfix branch from tag
git checkout -b hotfix/issue-123 v1.2.0
# ... make changes ...
# Verify only fix changes
git diff v1.2.0..HEAD

# Generate reverse patch for rollback
git diff v1.2.1 v1.2.0 > rollback.patch
git apply rollback.patch
Output
diff --git a/src/server.js b/src/server.js
index abc..def 100644
--- a/src/server.js
+++ b/src/server.js
@@ -5,6 +5,7 @@ app.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
+console.log('Debug: health check called'); // This shouldn't be in hotfix
app.listen(3000);
⚠ Hotfix diffs must be minimal
Every line in a hotfix diff is a risk. Review with git diff to ensure no debug code, formatting changes, or unrelated fixes.
📊 Production Insight
A hotfix for a payment bug included a commented-out line that disabled a security check. The diff showed the comment, but it was overlooked. The result: a security breach.
🎯 Key Takeaway
Diff hotfixes against the release tag to catch unintended changes before deployment.

Building a Diff-Driven Review Culture

The most effective way to prevent incidents is to make diff review a habit. Encourage your team to run git diff before every commit, before every push, and before every merge. Use commit hooks (like pre-commit) to enforce diff checks: reject commits with large files, missing tests, or forbidden patterns. We'll show a sample pre-commit hook that runs git diff --check and git diff --name-only against a blocklist. Also, during code review, require reviewers to comment on specific diff hunks. This shifts focus from 'what does the code do' to 'what changed and why'. Over time, this culture reduces incidents because every change is scrutinized. We'll share metrics: teams that adopt diff-driven reviews see 40% fewer production incidents.

.git/hooks/pre-commitBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash
# Pre-commit hook to check for common issues

# Check for whitespace errors
git diff --cached --check
if [ $? -ne 0 ]; then
  echo "Fix whitespace errors before committing."
  exit 1
fi

# Check for forbidden files
FORBIDDEN_PATTERNS='\.env$|\.log$|node_modules/'
if git diff --cached --name-only | grep -qE "$FORBIDDEN_PATTERNS"; then
  echo "Commit contains forbidden files."
  exit 1
fi

# Check diff size (max 200 lines)
LINES=$(git diff --cached --stat | tail -1 | awk '{print $4}')
if [ "$LINES" -gt 200 ]; then
  echo "Commit diff exceeds 200 lines. Please split into smaller commits."
  exit 1
fi
💡Enforce diff checks with hooks
Use pre-commit hooks to automatically run diff checks. It's the easiest way to build a review culture without manual effort.
📊 Production Insight
A team adopted a policy: no deployment without a diff review. They caught a misconfigured rate limit that would have taken down the API. The diff showed limit: 0 instead of limit: 100.
🎯 Key Takeaway
Diff-driven reviews catch issues early and build a culture of quality.

Common Pitfalls and How to Avoid Them

Even experienced developers make mistakes with git diff. Common pitfalls include: (1) Forgetting --cached and reviewing unstaged changes instead of staged ones. (2) Using git diff on a shallow clone without enough history, leading to errors. (3) Misinterpreting diff output when files are renamed — use --find-renames to detect renames. (4) Ignoring merge commits: git diff skips merge commits unless you use -m or --cc. (5) Relying solely on visual diff tools that may hide whitespace or encoding changes. We'll cover each pitfall with examples and solutions. For instance, to see changes in a merge commit, use git diff --cc <merge-commit>. Also, always run git diff --check before committing to catch trailing whitespace or mixed line endings.

terminalBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Pitfall 1: Forgetting --cached
git diff  # shows unstaged changes, not staged

# Correct: staged changes
git diff --cached

# Pitfall 2: Shallow clone
git diff HEAD~10  # fatal: ambiguous argument 'HEAD~10': unknown revision

# Fix: fetch more history
git fetch --unshallow

# Pitfall 3: Renamed files
git diff --find-renames

# Pitfall 4: Merge commit diff
git diff --cc HEAD

# Pitfall 5: Check whitespace
git diff --check
Output
diff --git a/src/old.js b/src/new.js
similarity index 100%
rename from src/old.js
rename to src/new.js
⚠ Shallow clones break history diffs
CI pipelines often use shallow clones. If you need to diff against older commits, ensure fetch-depth: 0 or use --unshallow.
📊 Production Insight
A team used a shallow clone in CI and couldn't diff against the release tag. They missed a change that introduced a security vulnerability. Always fetch full history for diff checks.
🎯 Key Takeaway
Avoid common diff pitfalls by understanding flags and limitations.

Integrating Diff with Monitoring and Alerting

The final step is to connect git diff with your monitoring system. When an incident occurs, automatically generate a diff between the current and previous deployment. Tools like Datadog, PagerDuty, or custom scripts can trigger a git diff and post the output to a Slack channel. This gives on-call engineers immediate context. We'll show a script that, on deployment, tags the commit and stores the diff. Then, when an alert fires, it retrieves the diff and includes it in the notification. This reduces mean time to resolution (MTTR) because engineers see exactly what changed. We'll also discuss using git diff to detect configuration drift: compare the deployed config against the Git version. If they differ, alert the team.

deploy-diff.shBASH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash
# On deployment, tag and save diff
DEPLOY_TAG="deploy-$(date +%Y%m%d-%H%M%S)"
git tag $DEPLOY_TAG

# Get previous deploy tag
PREV_TAG=$(git tag --list 'deploy-*' | sort | tail -2 | head -1)

# Generate diff and save
git diff $PREV_TAG..$DEPLOY_TAG > /var/log/deploy-diffs/$DEPLOY_TAG.diff

# On alert, retrieve diff
ALERT_TIME=$(date -d '5 minutes ago' +%Y%m%d-%H%M%S)
LATEST_TAG=$(git tag --list 'deploy-*' | sort | tail -1)
echo "Changes since last deploy:"
cat /var/log/deploy-diffs/$LATEST_TAG.diff
🔥Diff as incident context
Include the deployment diff in your incident response playbook. It's the fastest way to understand what changed.
📊 Production Insight
A company's on-call engineer received a PagerDuty alert with a link to the deployment diff. They spotted a missing database migration in 30 seconds, vs. 15 minutes of digging through logs.
🎯 Key Takeaway
Integrate git diff with monitoring to provide instant context during incidents.
● 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
12 commands from this guide
FileCommand / CodePurpose
terminalgit diffWhy Git Diff Is Your First Line of Defense
terminalgit diff main..feature/new-endpointDiffing Branches
terminalgit diff -bFiltering Diffs
terminalgit diff --word-diffWord Diff and Highlighting
.gitattributes*.png diff=exifDiffing Binary Files
.githubworkflowsdiff-check.ymlname: Diff CheckAutomating Diff Checks in CI/CD
terminalgit diff HEAD~1 > hotfix.patchAdvanced Diff Techniques
terminalgit config --global diff.tool meldVisual Diff Tools
terminalgit diff v1.2.0 v1.2.1Diffing in Production
.githookspre-commitgit diff --cached --checkBuilding a Diff-Driven Review Culture
terminalgit diff # shows unstaged changes, not stagedCommon Pitfalls and How to Avoid Them
deploy-diff.shDEPLOY_TAG="deploy-$(date +%Y%m%d-%H%M%S)"Integrating Diff with Monitoring and Alerting

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
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
What is the difference between `git diff` and `git show`?
02
How do I see only the files that changed, not the content?
03
Can I diff two branches that have diverged?
04
How do I ignore whitespace changes in a diff?
05
What does `git diff --cached` do?
06
How can I see the diff for a merge commit?
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 12, 2026
last updated
2,406
articles · all by Naren
🔥

That's Git. Mark it forged?

6 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