How to see the changes between two commits without commits in-between?

Learn how to see the changes between two commits without commits in-between? with practical examples, diagrams, and best practices. Covers git, diff development techniques with visual explanations.

Comparing Two Git Commits Without Intermediate Changes

Hero image for How to see the changes between two commits without commits in-between?

Learn how to effectively use git diff to compare any two commits in your repository, ignoring all changes that occurred in between them.

When working with Git, it's common to need to compare the state of your codebase between two specific points in history. While git diff is a powerful command, its default behavior often compares the working directory to a commit, or two consecutive commits. This article focuses on how to get a clean diff between any two arbitrary commits, effectively ignoring all the changes introduced by commits that lie chronologically between them. This is particularly useful for understanding the net impact of a feature branch or comparing different versions of a release.

Understanding Git Diff Basics

Before diving into comparing non-consecutive commits, let's quickly review the fundamental ways git diff works. By default, git diff without any arguments shows changes in your working directory that are not yet staged. With one commit, it compares your working directory to that commit. With two commits, it typically compares the first commit to the second. The key is to specify the exact commits you want to compare, ensuring Git understands you're interested in the difference in content between those two snapshots, not the history of how one evolved into the other.

flowchart TD
    A[Commit A] --> B[Commit B]
    B --> C[Commit C]
    C --> D[Commit D]
    E[Commit E]

    subgraph "Standard Diff (A..D)"
        A -- "Changes from A to B" --> B
        B -- "Changes from B to C" --> C
        C -- "Changes from C to D" --> D
    end

    subgraph "Target Diff (A...D)"
        A -.-> D
    end

    style A fill:#f9f,stroke:#333,stroke-width:2px
    style D fill:#f9f,stroke:#333,stroke-width:2px
    style E fill:#ccc,stroke:#333,stroke-width:1px
    linkStyle 0 stroke-dasharray: 5 5
    linkStyle 1 stroke-dasharray: 5 5
    linkStyle 2 stroke-dasharray: 5 5
    linkStyle 3 stroke-width:3px,fill:none,stroke:red
    linkStyle 4 stroke-width:3px,fill:none,stroke:red

Visualizing the difference between a standard diff (A..D) and a direct diff (A...D) ignoring intermediate commits.

The Three-Dot (A...B) Syntax for Direct Comparison

The most straightforward and recommended way to compare two arbitrary commits, ignoring intermediate changes, is to use the three-dot ... syntax with git diff. This command compares the state of the tree at the second commit (B) with the common ancestor of both commits (A and B). However, if you want to compare the content of commit A directly to the content of commit B, you simply specify them directly without any range operators.

git diff <commit-A-hash> <commit-B-hash>

Directly comparing the content of two specific commits.

Let's break this down:

  • <commit-A-hash>: This is the identifier for the first commit you want to compare. This could be a full SHA-1 hash, a short hash, a branch name, a tag, or HEAD~N.
  • <commit-B-hash>: This is the identifier for the second commit you want to compare. Git will show you what changes are needed to transform the state of <commit-A-hash> into the state of <commit-B-hash>.

Practical Example

Imagine you have a main branch and a feature/new-ui branch. You want to see the net changes introduced by feature/new-ui since it diverged from main, but without merging it yet. You can find the merge base (the common ancestor) and then compare your feature branch head to that merge base. However, if you just want to compare the current state of main to the current state of feature/new-ui, regardless of their history, the direct comparison is what you need.

# First, identify the commits you want to compare
# Let's say 'main' is at commit 'abcdef1' and 'feature/new-ui' is at '1234567'

# To compare the current state of 'main' to the current state of 'feature/new-ui':
git diff main feature/new-ui

# Alternatively, using full or short hashes:
git diff abcdef1 1234567

# To see the diff in a specific file:
git diff main feature/new-ui -- path/to/your/file.js

Examples of using git diff to compare two branch heads or specific commit hashes.

Comparing with the Merge Base (Two-Dot vs. Three-Dot Revisited)

While the direct comparison (git diff <commit-A> <commit-B>) is what the question asks for, it's important to distinguish it from the git diff A...B (three-dot) syntax, which is also very useful for comparing branches. The three-dot syntax compares the second commit (B) with the merge base of A and B. This effectively shows you what changes B introduced since it diverged from A.

# Find the common ancestor (merge base) of two branches
MERGE_BASE=$(git merge-base main feature/new-ui)

# Compare the feature branch to its merge base with main
git diff $MERGE_BASE feature/new-ui

# This is equivalent to the three-dot syntax:
git diff main...feature/new-ui

Comparing a branch to its common ancestor with another branch using git merge-base or the three-dot syntax.

The choice between git diff <commit-A> <commit-B> and git diff A...B depends on your intent:

  • git diff <commit-A> <commit-B>: Shows the literal content difference between the two specified commits, ignoring any history. This is what you use when you want to see the 'final state' difference.
  • git diff A...B: Shows the changes introduced on branch B since it diverged from branch A. This is useful for code review or understanding the unique contributions of a feature branch.