Skip to content
WANCODE
BM

Understanding Git Diffs

17 minIntermediateHTML · CSS · JS

In this lesson

  • Read the parts of a unified diff: file headers, hunk headers, +, - and context lines
  • Review a diff against its stated purpose and spot behaviour that was removed
  • Fix a regression while keeping the parts of the change you want

The big idea

A diff is the most honest description of a change. The commit message says what the author meant; the - lines show what was actually taken away.

Agent tools edit files directly, and the safest way to see what they did is the diff: git diff for unstaged changes, git diff --staged for staged ones, git show for the last commit. Here's a commit an assistant made with the message "Refactor checkout: support more discount codes, tidy naming".

Diff
diff --git a/script.js b/script.js
--- a/script.js
+++ b/script.js
@@ -1,23 +1,23 @@
+const DISCOUNTS = { RAYA10: 0.1, MERDEKA20: 0.2 };
+
 const cart = [
   { name: "Kopi O", price: 2.5, qty: 2 },
   { name: "Nasi lemak", price: 6, qty: 1 },
   { name: "Kuih lapis", price: 1.5, qty: 3 },
 ];

-function getTotal(items) {
-  return items.reduce((sum, item) => sum + item.price * item.qty, 0);
+function cartTotal(items) {
+  return items.reduce((sum, item) => sum + item.price, 0);
 }

 function applyDiscount(total, code) {
-  if (code === "RAYA10") {
-    return total * 0.9;
-  }
-  return total;
+  const rate = DISCOUNTS[code.toUpperCase()];
+  return total - total * rate;
 }

 function render() {
   const code = document.querySelector("#code").value;
-  const subtotal = getTotal(cart);
+  const subtotal = cartTotal(cart);
   document.querySelector("#subtotal").textContent = "RM " + subtotal.toFixed(2);
   document.querySelector("#total").textContent = "RM " + applyDiscount(subtotal, code).toFixed(2);
 }

How to read it

--- a/ +++ b/
The file before (a/) and after (b/) the change. Git also prints an index … line with file hashes, left out here.
@@ -1,23 +1,23 @@
A hunk header: this chunk covers 23 lines starting at line 1 in the old file, and 23 lines starting at line 1 in the new one.
- line
Removed: this existed before and is gone now.
+ line
Added: this is new.
line (space)
Context: unchanged, shown so you can see where the change sits.

Review the diff against the message

  • For each - line, ask: where did this behaviour go? If it isn't in a + line somewhere, it was deleted. * item.qty vanished in a "naming" change.
  • Check the new cases. The old applyDiscount returned total for unknown codes. The new one computes total - total * undefined. What's that?
  • Look for new assumptions. code.toUpperCase() assumes code is always a string. The old code didn't.
  • Mixed changes are a smell. A rename and a logic change in the same line are easy to approve by accident. Ask for separate commits.

What the assistant wrote

JavaScript
function applyDiscount(total, code) {
  const rate = DISCOUNTS[code.toUpperCase()];
  return total - total * rate;
}

What a reviewer ships

JavaScript
function applyDiscount(total, code) {
  const key = (code || "").trim().toUpperCase();
  const rate = DISCOUNTS[key];
  if (rate === undefined) return total;
  return total - total * rate;
}

Keep the improvement (a lookup table, case-insensitive codes) and restore the old guarantee (unknown or missing code → no discount).

In a real repo you don't have to accept a change all-or-nothing: git add -p lets you stage it hunk by hunk, and git restore -p <file> lets you discard individual hunks of unstaged changes. Read every hunk either way.

script.js is the code after the assistant's commit. The preview shows RM NaN before you've even typed a code. Using the diff above, fix applyDiscount so that:

- RAYA10 (any letter case) gives 10% off and MERDEKA20 gives 20% off — keep the new feature, - an unknown code, an empty string or a missing code returns the total unchanged — the old guarantee.

Checks

  • Known codes still work (any case)
  • Unknown, empty or missing codes give no discount
  • The page no longer shows NaN

Check understanding

Q1

In a diff, what does a line starting with a space mean?

Q2

A commit message says "rename variables, no behaviour change". What's the most reliable way to check that claim?

What you learned

  • ---/+++ name the files, @@ headers locate each hunk, -/+/space mark removed/added/context lines.
  • For every removed line, find where its behaviour went.
  • Check the commit message's claims against the diff — and ask for separate commits for renames and logic.
Preview

Console output from your code appears here.

Step 1 of 2