Reviewing Commit History Before a Merge Instead of Just the Diff
AI generated
git
HEAD
Git · Code Review · Commit History
Reviewing Commit History Before a Merge
instead of relying only on the final diff

A pull request's default view almost always shows just the aggregated diff between the target branch and the last commit. That hides how a change actually came together, whether it landed in one clean, atomic commit or only reached its final shape after several rounds of fixes. This article shows how git log, git show, git range-diff and a targeted interactive rebase let you actually review commit history before a merge, instead of trusting the final result alone.

11 min read Git Code Review

1. Why the final diff falls short during review

A pull request's default view usually shows only the aggregated diff between the target branch and the last commit of the feature branch. Anyone reviewing only that view sees the outcome, not how it got there. Whether a line was written in one clean, atomic commit or only reached its final form after three rounds of fixes disappears completely inside the aggregated diff.

On larger changes especially, the final diff often blends several unrelated concerns together: a refactor, a bug fix and a new feature all show up as one confusing mass of changes. A review that walks through the individual commits instead can judge each intent in isolation and assess risk far more precisely than a glance at the final result alone.

Another downside of a pure diff review: bugs that were introduced in an early commit and fixed again in a later one stay invisible in the final diff but show up clearly in the full history. That matters, because such patterns often reveal that the author had to test an idea live in the code, which can point to insufficient planning or an unclear grasp of the requirements.

2. git log as the starting point for a history overview

Before diving into individual commits, a quick overview of the history's structure is worth the time. Combining --oneline, --graph and --stat shows at a glance how many commits a feature branch contains, how they branch relative to each other, and which files each one touches, all without displaying a single diff hunk yet.

Commit count alone is already a useful signal: ten small, clearly scoped commits are usually faster and easier to review precisely than three enormous commits that each touch hundreds of lines across several modules at once. Skimming the commit list before the deeper review also lets you decide deliberately where to start the closer inspection.


# Compact overview of every commit in a feature branch
git log --oneline --graph --stat main..feature/checkout-redesign

# Commit messages and touched files only, without diff content
git log --stat --no-patch main..feature/checkout-redesign

3. Inspecting individual commits in isolation

For the actual content review, git show is the central tool: it prints commit message, author, date and the full diff of a single commit in isolation, without blending in context from neighboring commits. That lets you walk commit by commit through the history, judging each step as a self-contained unit.

Alternatively, git log -p delivers the same information for an entire range of commits at once, which is useful when the whole series should be worked through from start to finish in one sitting. Combined with --reverse, commits appear in chronological rather than reverse order, matching how the change actually came into being and making it easier to follow along.


# Inspect a single commit in isolation, including its diff
git show a1b2c3d

# Walk the entire commit series chronologically, commit by commit
git log -p --reverse main..feature/checkout-redesign

4. Reading merge commits and first-parent history correctly

Once a feature branch itself contains merges from the target branch, the picture changes: a plain git log then also shows foreign commits from the target branch that have nothing to do with the actual change. The --first-parent option narrows the history down to the commits that genuinely belong to the feature branch and hides the merged-in commits from the target branch.

For merge commits themselves, git show displays only a summary without a diff by default, since a merge commit technically has two parents. The -m option shows the diff of a merge commit against each parent separately, which helps clarify what concrete changes a merge actually introduces relative to the main line.


# Only the feature branch's own commits, without merged-in foreign commits
git log --first-parent --oneline main..feature/checkout-redesign

# Show the diff of a merge commit against each parent separately
git show -m 9f8e7d6

5. Distinguishing two-dot and three-dot diffs on feature branches

A common stumbling block during manual review is confusing git diff main..feature with git diff main...feature. The two-dot form compares the current state of main directly with the current state of feature, while the three-dot form instead uses the common ancestor of both branches, the so-called merge base, as its starting point and shows only the changes that happened in the feature branch since that point.

If main has moved on since the feature branch was created, the two forms produce noticeably different results: the two-dot form mixes foreign changes from main into the review, while the three-dot form shows exclusively the changes relevant to the review. For a clean review, the three-dot form is therefore almost always the right choice.


# Blends changes from main into the feature changes
git diff main..feature/checkout-redesign

# Shows only what happened on the feature branch since it diverged
git diff main...feature/checkout-redesign

6. Comparing commits across rebase boundaries with git range-diff

If a feature branch gets rebased again during an ongoing review, for instance to address review comments, every subsequent commit gets a new commit ID, even if the actual content stays identical. A plain repeated diff then appears to show everything as new, even though only a few lines may have actually changed content-wise.

git range-diff solves exactly this problem: it compares two versions of a commit series by content, automatically matches corresponding commits to each other, and shows which commits stayed unchanged, which changed content-wise, and which were added or removed. For reviewers who need to re-check a reworked version of a branch, that saves considerable time compared to walking through every commit again from scratch.


# Compare the old and new version of the commit series,
# after a rebase onto a more recent state of main
git range-diff main old-feature-branch@{1} feature/checkout-redesign

7. Cleaning up history with interactive rebase before review

A review gets noticeably easier once the history has already been cleaned up beforehand. Interactive rebase with git rebase -i lets authors merge pure fixup commits like fixup! or squash! into their original commit before the branch is even submitted for review.

git commit --fixup immediately links a fixup commit to its matching original commit, and git rebase -i --autosquash then sorts and merges those fixes into the right place automatically. The result is a history in which every remaining commit genuinely represents a self-contained, review-worthy idea, instead of exposing a sequence of trial and error.


# Link a fixup commit directly to an earlier commit
git commit --fixup a1b2c3d

# Before review: automatically sort and merge fixup commits into place
git rebase -i --autosquash main

8. Commit-by-commit review in practice: a checklist

A short checklist helps make the process systematic: does each commit make sense and stand on its own? Does the code remain in a working state after every single commit, so a later bisection with git bisect does not fail on a broken intermediate step? Does the commit message explain the why of the change, not just the obvious what?

It is also worth asking whether related changes are actually bundled into one commit or unnecessarily scattered across several. Both extremes, a single giant commit for everything and excessive splitting into dozens of micro commits, make review harder, just for opposite reasons.

9. Log formats, aliases and editor integration for efficient review

Recurring log invocations can be saved permanently as a git alias, so a short command like git lg delivers the familiar, carefully configured output instead of retyping long option chains every time. Custom format strings via --pretty=format: also let you display exactly the information relevant to a given review, such as author, relative date and commit message on a single line.

Most modern IDEs, including PhpStorm, additionally offer a graphical commit history with clickable individual commits, side-by-side diffs and blame integration, which makes switching between the command line and visual inspection easier. For deeper reviews, though, the command line often remains the faster choice, because range expressions like three-dot diffs or range-diff can be expressed there more precisely and repeatably.


# Save a permanent alias for a clear log output
git config --global alias.lg "log --graph --oneline --decorate --abbrev-commit"

# Custom format: short hash, relative date, author, subject line
git log --pretty=format:"%h %ad %an %s" --date=relative main..feature/checkout-redesign
Criterion Plain final-diff review Commit-by-commit review Recommendation
Detects mixed concerns No, everything looks like one change Yes, each commit judged separately Commit by commit
Shows fixes for temporary bugs No, invisible in the final result Yes, visible in the history Commit by commit
Effort on a small, atomic branch Very low Somewhat higher, but manageable Either works
Effort on a large, unstructured branch Low, but not very informative High, but surfaces real risk Commit by commit
Useful for later git bisect runs No effect on the history Direct, since every commit was checked Commit by commit

Mironsoft

Git workflows, branching strategies, and CI hooks

Chaotic Git history and unclear branching rules across the team?

We set up clean Git workflows, clarify branching strategies for the team, and automate quality checks via Git hooks and CI pipelines so the history stays traceable.

Workflow Audit

Review the existing branching strategy and merge practice for weak spots.

Hook Automation

Set up pre-commit and pre-push hooks for linting, tests, and commit conventions.

Team Training

Teach rebase, cherry-pick, and conflict resolution hands-on across the team.

10. Summary

Commit History Review

Core problem

A pull request's final diff shows only the outcome, not the path there, hiding possible intermediate bugs.

Key tools

git log with --graph and --stat for the overview, git show for individual commits, git range-diff after rebases.

Before the review

Clean up history with interactive rebase and --autosquash so each commit is a self-contained idea.

Rule of thumb

Use a three-dot diff against the merge base instead of a two-dot diff to exclude foreign changes from the comparison.

11. FAQ: Commit History Review

1Why isn't a pull request's final diff enough for a thorough review?
The final diff shows only the aggregated result of all commits. Bugs that were introduced and fixed again along the way, mixed concerns, and the actual order in which a change was built all stay invisible.
2Which command works best for a first overview of a feature branch?
git log --oneline --graph --stat against the target branch gives a compact overview of the number, structure and touched files of the commits before the actual content review begins.
3How do I view a single commit in isolation, without context from neighboring commits?
git show followed by the commit hash displays exactly one commit, including message, metadata and full diff, independent of the surrounding commits.
4What is the difference between git diff main..feature and git diff main...feature?
The two-dot form compares the current states directly, mixing in changes that landed on main since the branches diverged. The three-dot form uses the common merge base and shows only the actual feature changes.
5What is git range-diff useful for during review?
git range-diff compares two versions of the same commit series by content, even if a rebase changed every commit ID. It shows which commits stayed the same, changed, were added, or were removed.
6How can I sort out fixup commits cleanly before review?
git commit --fixup links a fixup commit directly to an earlier commit. git rebase -i --autosquash then automatically sorts and merges those fixes into the right place.
7Why does --first-parent matter for feature branches with merged-in changes?
Without --first-parent, the log output also shows foreign commits merged in from the target branch. --first-parent narrows the history down to the commits that actually belong to the feature branch.
8How do I see the diff of a merge commit?
git show shows no diff for a merge commit by default. The -m option shows the diff against each parent separately.
9How can I tell whether a feature branch is structured in a review-friendly way?
Each commit should stand on its own, leave the code in a working state, and carry a commit message that explains the why of the change, not just the obvious what.
10Is a custom git alias worth setting up for reviewing history?
Yes, an alias like git lg with --graph, --oneline and --decorate saves noticeable typing on frequent use and keeps the log output consistent and familiar across every review.