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 --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 anindex …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.qtyvanished in a "naming" change. - Check the new cases. The old
applyDiscountreturnedtotalfor unknown codes. The new one computestotal - total * undefined. What's that? - Look for new assumptions.
code.toUpperCase()assumescodeis 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
function applyDiscount(total, code) {
const rate = DISCOUNTS[code.toUpperCase()];
return total - total * rate;
}What a reviewer ships
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
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.
Console output from your code appears here.