Compare Two Versions of a Series with git range-diff
After a rebase, "did that just reorder my commits or quietly change one?" is the question git range-diff answers. It compares an older and newer version of a patch series, flags reordered, added, removed and edited commits, and lets you inspect exactly what changed in the patch itself. The examples use Git 2.43.0 from the installed git-man package. Allow about fifteen minutes.
The route
Jump straight to the step you need, or tick off Done means at the end.
- You need: a Git repository containing both versions of the series, or a remote-tracking branch plus a reflog entry for the earlier one.
- Read-only. The command only compares commits, it does not rebase, merge, reset or otherwise modify the repository. No elevated privileges needed.
1. Check the installed command
Confirm the version and the command's spelling before copying a longer example:
$ git --version
git version 2.43.0
$ git range-diff -h
The command is git range-diff, with a space. Its normal input is two ranges; in the two-range form, the first range is the older version and the second is the newer one.
Checkpoint
You should have the expected Git binary and a repository in which the revisions named below all resolve.
2. Compare two versions of a branch
The most useful case is a topic branch that was just rebased: the upstream branch is the current base, HEAD@{1} is the previous local position, and HEAD is where you are now:
$ git range-diff @{u} @{1} HEAD
Read this three-revision form as "compare the commits after this base in revision one with the commits after the same base in revision two". The base does not have to be the exact branch point, any common commit that makes both ranges meaningful will do.
For two explicitly named ranges, use the double-dot notation twice:
$ git range-diff OLD_BASE..old-topic NEW_BASE..new-topic
Replace every uppercase placeholder with a revision that exists in your repository. Check the names without changing anything:
$ git rev-parse --verify OLD_BASE
$ git rev-parse --verify old-topic
$ git rev-parse --verify NEW_BASE
$ git rev-parse --verify new-topic
A failed verification is a revision or spelling problem. Do not "fix" it by guessing a nearby branch, find the intended base first.
3. Read the matching markers
Each output row places an old commit on the left and a new commit on the right. The marker between them tells you what Git found:
-: -------- > 1: 0ddba11 Add a new check
1: c0debee = 2: cab005e Keep the parser change
2: f00dbal ! 3: decafe1 Tighten the error text
3: bedead < -------- -: Remove the obsolete test
=means the corresponding patches match closely.!means both commits correspond, but their author information, message or patch differs. The indented diff below shows exactly what.>marks a commit present only in the second, newer range.<marks a commit present only in the first, older range.
Rows appear in the order of the second range, with an unmatched commit inserted after its ancestors have been shown. This compares patch identity, it is not a claim that commit object IDs stay stable after a rebase, they never do.
On a terminal, Git colours additions, deletions, matches and changed patches differently. If a pager opens, use the usual pager controls, then press q to get back to the shell.
4. Inspect an edited patch
For a ! row, look below it for the diff of the two patches: the outer markers describe how the patch changed, the inner diff shows the old and new content. This surfaces a reworded commit message or a small fix without you comparing the entire branch by eye.
To make copied output easier to read in a log or issue, turn off terminal colour:
$ git range-diff --no-color OLD_BASE..old-topic NEW_BASE..new-topic
By default, range-diff keeps the original diff colours and adds outer red and green markers for the diff of diffs. Reach for the colour-disabled form above only when that nesting gets in the way, it is usually the clearer choice for a plain-text capture.
5. Limit the comparison to a path
If a large series touches several areas, add -- followed by one or more paths:
$ git range-diff OLD_BASE..old-topic NEW_BASE..new-topic -- src/parser.c tests/parser
This narrows the ranges to those paths, which can make a review easier to focus, but it can also hide changes elsewhere in the same commits. Run the unfiltered comparison before declaring two series equivalent.
6. Handle poor or surprising matches
Git pairs commits by comparing author information, commit messages and patches, solving a cost-based assignment across both ranges, so a commit's visual position is not the matching rule. A large rewrite can therefore show up as a deletion plus an addition instead of an edit.
The default creation factor is 60 percent. If a substantial edit gets reported as a complete rewrite, try a larger value:
$ git range-diff --creation-factor=80 OLD_BASE..old-topic NEW_BASE..new-topic
Tip
If unrelated commits are getting paired, try a smaller value instead. Treat the result as review evidence, not an automated decision, always open the commits in question with git show before accepting a surprising match. Large ranges can also take noticeable time, since Git is comparing many pairs of patches and solving an assignment problem, so narrow the range or a path when investigating something specific.
7. Keep the output out of scripts
Range-diff output is human-readable porcelain. Its layout and wording can change between Git versions, and it is not meant to be machine-readable, so do not parse its rows in a deployment check or treat them as a stable interchange format. For automation, pick a command and format built for that purpose, such as a suitable git log format, and test it against the Git versions you actually support.
Options such as --stat are accepted as regular diff options, but the installed manual warns that some combinations produce output that is not useful in range-diff's context. Keep the default view for review unless you have checked the result on your own repository.
Done means
- Version and revisions checked. The installed Git and every range boundary resolve.
- Compared in the right order. Older range first, newer range second.
- Markers understood. You can tell matching, changed, added and removed commits apart.
- Surprises inspected. Any odd pairing checked with
git showbefore trusting it. - Kept for humans. Colour-disabled form used for plain text, range-diff output kept out of scripts.
- Repository untouched. No history or working-tree state changed by the comparison.