Comparing Git Commits to Debug a Regression
A feature worked yesterday. It’s broken today. I don’t guess when this happens — I compare the two commit states directly and let Git tell me exactly what changed. Commit comparison is the practice of diffing two points in a repository’s history to isolate which commit introduced a regression. Here’s the workflow I actually use: pick a good and a bad commit, diff them precisely, and recover only what you need without dragging the rest along with it.
Step 1: Identify the Commit Range with git log
Start with the shape of your history:
git log --oneline --decorate --graph
From that graph, pick two points:
GOOD_COMMIT— the last commit you know worked.BAD_COMMIT— the first commit where things broke.
Everything else in this workflow hangs off those two references.
The range GOOD_COMMIT..BAD_COMMIT covers every commit in between — that’s what git diff, git log -p, and the recovery commands in the next steps all operate on.
Step 2: Compare Commit States with git diff and git show
git diff is a Git command that compares two references — commits, branches, or the working tree — and prints the line-by-line changes between them. Once you have your range, git diff shows you exactly what changed:
git diff GOOD_COMMIT..BAD_COMMIT
Narrow it to one file when you already suspect where the regression lives:
git diff GOOD_COMMIT..BAD_COMMIT -- path/to/file.py
git show is a Git command that displays a single commit’s metadata (author, date, message) together with its full patch. When you want the full picture on the bad commit in one go, git show gives you that:
git show BAD_COMMIT
Step 3: Inspect the History in the Range with git log -p
A diff tells you what changed overall, but sometimes you need it commit by commit. git log is a Git command that lists commits reachable from a given range; with -p, it also prints each commit’s patch:
git log --oneline GOOD_COMMIT..BAD_COMMIT
git log -p GOOD_COMMIT..BAD_COMMIT
The first command gives you a quick list of suspects. The second shows the actual patch for each one, so you can spot which commit introduced the regression rather than just knowing it happened somewhere in the range.
Step 4: Create a Safety Branch Before Recovery
Before I touch anything, I pin down where I am right now with git branch, a Git command that creates a new reference pointing at the current commit without changing the working tree:
git branch backup-before-fix
Creating this branch costs nothing — it’s just a new pointer — and it means I can experiment freely. If a cherry-pick goes wrong or a revert gets messy, I still have an untouched copy to fall back on.
Step 5: Recover Good Changes with git cherry-pick or git revert
Git gives you two distinct recovery strategies once you know which commits are good and which are bad:
| Approach | Command | History shared with others? | What happens |
|---|---|---|---|
| Rebuild clean | git checkout -b fix-branch GOOD_COMMIT + git cherry-pick <hash> |
Safe on private branches | Builds a new branch containing only the commits you choose |
| Revert in place | git revert BAD_COMMIT |
Safe on shared/public branches | Adds a new commit that undoes the bad one; history stays intact |
Cherry-pick builds a new branch from GOOD_COMMIT and copies chosen commits onto it — the old history (including BAD_COMMIT) is left untouched elsewhere. Revert stays on the same line of history and simply appends a new commit that undoes BAD_COMMIT, so BAD_COMMIT itself is never removed.
A) Build a new clean branch from the known-good commit
Start fresh from the point where things worked:
git checkout -b fix-branch GOOD_COMMIT
git checkout -b creates and switches to a new branch starting at the given commit. Then bring across only the commits you actually want, one at a time, with git cherry-pick — a Git command that applies the changes from a specific commit onto your current branch as a new commit:
git cherry-pick <commit-hash>
This is my preferred route when the bad range is a mix of good and bad commits — I choose exactly what comes along. See the official git cherry-pick documentation for conflict-handling flags like --continue and --abort.
B) Keep the current branch and revert the bad commit
If history is shared with others and rewriting it isn’t an option, git revert — a Git command that creates a new commit reversing the changes of an earlier one, rather than removing that commit from history — is the safer move. See the official git revert documentation:
git revert BAD_COMMIT
git revert doesn’t erase the bad commit — it adds a new one that undoes it, so history stays intact and traceable. Anyone pulling the branch later can see exactly what was reverted and why.
Step 6: Resolve Merge Conflicts During Cherry-pick or Revert
Cherry-picks and reverts can both trigger merge conflicts — cases where Git can’t automatically reconcile overlapping changes to the same lines. When that happens, the routine doesn’t change:
- Resolve the conflicting files by hand.
- Stage them:
git add <resolved-files>. - Continue the operation —
git cherry-pick --continue, or complete the merge commit if you’re mid-revert.
No panic, please. A conflict just means Git needs your judgement on a line it can’t merge automatically.
Mental Model: Commits as Before-and-After Photographs
Think of GOOD_COMMIT and BAD_COMMIT as two photographs of the same room, taken before and after someone rearranged the furniture. git diff lays the photos side by side and circles what moved. git log -p gives you the flipbook in between, frame by frame, so you can see who moved what and when. You’re not guessing which piece of furniture is out of place — you’re watching it happen.
Checklist for Debugging a Regression by Comparing Commits
- Find your good and bad commits with
git log --oneline --decorate --graph. - Diff the range:
git diff GOOD_COMMIT..BAD_COMMIT. - Read the history in between with
git log -p GOOD_COMMIT..BAD_COMMIT. - Branch off a safety copy before you change anything:
git branch backup-before-fix. - Recover with
cherry-pickfor a clean rebuild, orrevertwhen history is shared.
Summary: Debugging Git Regressions by Comparing Commits
Debugging a regression by commit comparison is a systematic method, not a panic event: define your good and bad points, diff precisely, keep a safety branch, and recover with the smallest change that fixes the problem. Once you’ve done this a few times, “it worked yesterday” stops being scary — it’s just a range of commits waiting to be diffed.
References
Enjoyed this? Get more like it.
Weekly notes on AI tools, Python, and what I'm actually building — plus a free copy of Fantastic AI: The 2026 Toolkit.