@@ · review craft @@

Re-reviewing a PR after changes: the interdiff problem

Othman Shareef · July 2, 2026 · 6 min read

First reviews get all the attention; second reviews get the waste. You reviewed Tuesday, left eight comments, the author pushed “fixes”, and now you’re staring at the full 2,000-line diff again trying to reconstruct which parts you’ve already vouched for. A 278-point Hacker News thread titled “Some of us like interdiff code review” exists because this, not the first pass, is where review time quietly dies.

Why re-review is uniquely wasteful

The first review amortizes: you build the mental model once and spend it on the whole diff. The re-review should cost only the delta: the author changed forty lines in response to comments. But if you can’t isolate those forty lines reliably, you pay the full model-building cost again, on code you’ve already read. Multiply by two or three rounds per PR and the second-review tax exceeds the first review. This is also where rubber-stamping sneaks in: “probably just the fixes” is the path of least resistance when the tooling can’t show you the fixes.

Where GitHub’s answer falls short

GitHub does offer “changes since your last review”, and when authors append normal commits, it mostly works. The gaps: it anchors to commits, so a rebase or squash force-push orphans the comparison; outdated comment threads detach from their lines; and there’s no view of how the shape of the change evolved (did the author restructure, or patch?). The thread above is full of Gerrit refugees for a reason: patchset-to-patchset comparison was a first-class feature there.

The workflow fix: fixups now, squash later

  • During review rounds: authors append (git commit --fixup per addressed comment), never rewrite. Reviewers get an honest per-round delta.
  • Before merge: git rebase -i --autosquash folds the fixups away; clean history and reviewable rounds both survive.
  • When a rebase mid-review is unavoidable (conflicts with main): git range-diff old-base..old-head new-base..new-head compares the two versions as patches, surviving the rewrite. It is the closest thing Git ships to a true interdiff. (docs)

The tooling fix: scope the diff to what’s new

The general capability underneath all of this is scoping the diff to a span of commits: “show me only what changed in the commits since my review.” It’s the feature we leaned into with Pyor (ours, free for individuals): pick any commit range in a PR and the diff re-scopes to exactly that span, with review threads kept anchored, including threads on lines that later changed, which get pinned to the version they were written against instead of vanishing as “outdated.” The re-review becomes what it always should have been: read the delta, check it against your comments, approve with confidence.

Close the loop explicitly

However you get the delta, end the round crisply: re-resolve the threads that were addressed, say what you re-checked, and approve mentioning the rounds (“round 2 reviewed: migration fix verified”). Re-review is the part of the process most likely to be invisible work; making it visible is how it stays funded.

Frequently asked questions

What is an interdiff?

A diff between two versions of the same change (version 1 of a PR vs. version 2 after review feedback) rather than between the change and its base branch. It answers the re-reviewer’s actual question: what did the author modify since I last looked?

Why does force-pushing break re-review on GitHub?

GitHub’s “changes since your last review” compares commits. When the author rebases or squashes and force-pushes, the old commits no longer exist, so the comparison loses its anchor, and the reviewer is often left re-reading the whole diff. git range-diff compares the two commit ranges as patches, which survives the rewrite.

Should authors avoid force-pushing during review?

During an active review round, yes: append fixup commits so reviewers can see deltas, then squash before merge. Clean history and reviewable rounds aren’t in conflict; they just happen at different moments.

← All posts