Skip to content

range-diff Git Command Guide

The git range-diff command compares two commit ranges (ignoring merge commits) and shows the differences between two versions of a patch series or branch. It finds corresponding commits between ranges and displays how patches have changed.

Terminal window
git range-diff [--color=[<when>]] [--no-color] [<diff-options>]
[--no-dual-color] [--creation-factor=<factor>]
[--left-only | --right-only] [--diff-merges=<format>]
[--remerge-diff]
( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> )
[[--] <path>...]
OptionDescription
--color[=<when>]Colorize output (auto/always/never)
--no-colorDisable color output
--no-dual-colorUse single color for differences
OptionDescription
--creation-factor=<factor>Adjust sensitivity for matching commits (default 60)
--left-onlyShow only commits from left range
--right-onlyShow only commits from right range
OptionDescription
--diff-merges=<format>Control merge commit diff format
--remerge-diffUse remerge-diff format for range differences
<diff-options>Standard diff options (—stat, —patch, etc.)
ParameterDescription
<range1> <range2>Two commit ranges to compare
<rev1>...<rev2>Symmetric difference notation
<base> <rev1> <rev2>Base commit and two revisions
<path>...Limit comparison to specific paths
Range Diff Algorithm:
1. Identify commits in both ranges (excluding merges)
2. Find corresponding commits between ranges
3. Match commits based on patch similarity
4. Display changes between matched commits
5. Show unmatched commits as additions/removals
Matching Criteria:
├── Author information similarity
├── Commit message similarity
├── Patch content similarity
├── Creation factor threshold (default 60%)
Range Specification Methods:
├── git range-diff main..feature main..updated-feature
├── git range-diff feature...updated-feature
├── git range-diff main feature updated-feature
Visual Representation:
main ─── A ─── B ─── C (original)
└───── A' ── B' (updated)
Range diff shows: A→A', B→B', C removed
Range Diff Output:
1: abc123 ! 1: def456 Subject of first commit
@@ Metadata and diff changes @@
... patch differences ...
2: ghi789 = 2: jkl012 Subject of second commit
(commit unchanged)
3: mno345 > 3: pqr678 Subject of third commit
(commit added in second range)
Terminal window
# Compare original vs rebased branch
git range-diff main..feature main..feature-rebased
# Compare before and after rebase
git range-diff HEAD~5..HEAD HEAD~3..HEAD
# Compare two feature branches
git range-diff feature-v1..feature-v2
Terminal window
# Symmetric difference notation
git range-diff feature...feature-updated
# Compare with base commit
git range-diff main feature-v1 feature-v2
# Compare specific ranges
git range-diff v1.0..v1.1 v1.0..v2.0
Terminal window
# Compare changes to specific file
git range-diff main..feature -- src/main.js
# Compare directory changes
git range-diff main..feature -- src/
# Multiple paths
git range-diff main..feature -- file1.txt file2.txt
Terminal window
# Review rebase changes
review_rebase() {
local original_branch="$1"
local rebased_branch="$2"
local base="${3:-main}"
echo "Reviewing rebase: $original_branch -> $rebased_branch"
# Show range diff
git range-diff "$base..$original_branch" "$base..$rebased_branch"
# Check for problematic changes
if git range-diff --creation-factor=80 "$base..$original_branch" "$base..$rebased_branch" | grep -q "!"; then
echo "⚠ Significant changes detected in rebase"
else
echo "✓ Rebase appears clean"
fi
}
review_rebase "feature" "feature-rebased"
Terminal window
# Track patch series changes
patch_series_diff() {
local series_v1="$1"
local series_v2="$2"
echo "Comparing patch series versions"
# Full diff with statistics
git range-diff --stat "$series_v1" "$series_v2"
# Show only changed commits
git range-diff --no-dual-color "$series_v1" "$series_v2" | grep "^[0-9]:.*!"
# Count changes
changes=$(git range-diff "$series_v1" "$series_v2" | grep -c "!")
echo "Commits with changes: $changes"
}
patch_series_diff "v1.0..v1.5" "v2.0..v2.5"
Terminal window
# Analyze merge conflicts between ranges
analyze_merge_conflicts() {
local range1="$1"
local range2="$2"
echo "Analyzing potential merge conflicts"
# Find overlapping changes
git range-diff --diff-merges=first-parent "$range1" "$range2" |
while read -r line; do
if [[ "$line" =~ ^[0-9]:\ [[:xdigit:]]+\ !\ [0-9]:\ [[:xdigit:]]+ ]]; then
commit1=$(echo "$line" | awk '{print $2}')
commit2=$(echo "$line" | awk '{print $5}')
# Check for file conflicts
files1=$(git show --name-only "$commit1" | tail -n +2)
files2=$(git show --name-only "$commit2" | tail -n +2)
conflicts=$(comm -12 <(echo "$files1" | sort) <(echo "$files2" | sort))
if [ -n "$conflicts" ]; then
echo "Potential conflict in: $conflicts"
fi
fi
done
}
analyze_merge_conflicts "branch1..branch2" "branch1..branch3"
Terminal window
# Test range differences
test_range_differences() {
local base_range="$1"
local test_range="$2"
echo "Testing range differences"
# Get changed commits
changed_commits=$(git range-diff "$base_range" "$test_range" | grep "!" | wc -l)
if [ "$changed_commits" -gt 0 ]; then
echo "Found $changed_commits changed commits - running tests"
# Test the changes
if run-tests.sh; then
echo "✓ Tests passed for changed commits"
else
echo "✗ Tests failed for changed commits"
# Show which commits changed
git range-diff "$base_range" "$test_range" | grep "!"
exit 1
fi
else
echo "No commit changes detected"
fi
}
test_range_differences "main..feature" "main..feature-updated"
Terminal window
# Configure range-diff behavior
git config range-diff.creation-factor 60 # Similarity threshold
git config range-diff.color always # Color output
git config diff.colorMoved zebra # Moved code highlighting
# Configure diff options
git config diff.algorithm histogram # Better diff algorithm
git config diff.wsErrorHighlight all # Highlight whitespace errors
Terminal window
# Use appropriate creation factors
git range-diff --creation-factor=80 range1 range2 # Strict matching
git range-diff --creation-factor=40 range1 range2 # Loose matching
# Combine with diff options
git range-diff --stat --no-merges range1 range2 # Statistics only
git range-diff --patch --word-diff range1 range2 # Detailed patches
# Focus on specific changes
git range-diff --grep="bug" range1 range2 # Search messages
git range-diff --author="John" range1 range2 # Filter by author
Terminal window
# Optimize for large ranges
git range-diff --no-color range1 range2 # Disable colors
git range-diff --quiet range1 range2 # Reduce output
# Use efficient algorithms
git range-diff --diff-algorithm=histogram range1 range2
# Limit comparison scope
git range-diff --relative range1 range2 # Relative paths
#!/bin/bash
# Enhanced PR review with range-diff
pr_review_enhancement() {
local pr_branch="$1"
local base_branch="${2:-main}"
echo "Enhanced PR review for $pr_branch"
# Get PR range
pr_range="$base_branch..$pr_branch"
# Compare with previous version if available
if git rev-parse --verify "${pr_branch}-old" >/dev/null 2>&1; then
echo "Comparing with previous version:"
git range-diff "${pr_branch}-old" "$pr_branch"
# Save current as old for next comparison
git branch -f "${pr_branch}-old" "$pr_branch"
else
echo "First review of this PR"
git branch "${pr_branch}-old" "$pr_branch"
fi
# Analyze commit quality
echo "Commit analysis:"
git range-diff --stat "$pr_range"
# Check for WIP commits
wip_count=$(git log --oneline "$pr_range" | grep -i "wip\|work\|temp" | wc -l)
if [ "$wip_count" -gt 0 ]; then
echo "⚠ Found $wip_count WIP commits"
fi
# Check commit message quality
short_messages=$(git log --oneline "$pr_range" | awk 'length($2) < 10' | wc -l)
if [ "$short_messages" -gt 0 ]; then
echo "⚠ Found $short_messages commits with short messages"
fi
}
pr_review_enhancement "feature/new-ui"
Terminal window
# Ensure rebase quality with range-diff
rebase_quality_assurance() {
local original_head="$1"
local rebased_head="${2:-HEAD}"
echo "Rebase quality assurance"
# Compare original vs rebased
range_diff_output=$(git range-diff "$original_head" "$rebased_head" 2>/dev/null)
# Check for unexpected changes
unexpected_changes=$(echo "$range_diff_output" | grep -c "!")
if [ "$unexpected_changes" -gt 0 ]; then
echo "⚠ Rebase introduced $unexpected_changes unexpected changes"
# Show the changes
echo "$range_diff_output" | grep "!"
# Ask for confirmation
read -p "Continue with rebase? (y/N): " confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
echo "Rebase cancelled"
exit 1
fi
else
echo "✓ Rebase appears clean"
fi
# Run tests on rebased code
if ! run-tests.sh; then
echo "✗ Tests failed after rebase"
exit 1
fi
echo "✓ Rebase quality assurance passed"
}
rebase_quality_assurance "HEAD~5" "HEAD"
Terminal window
# Maintain patch series with range-diff
patch_series_maintenance() {
local series_branch="$1"
local upstream_branch="${2:-main}"
echo "Patch series maintenance for $series_branch"
# Get current series
current_series=$(git rev-list "$upstream_branch..$series_branch")
# Check for conflicts with upstream
if git range-diff "$upstream_branch..$series_branch" "$upstream_branch..$series_branch" --quiet 2>/dev/null; then
echo "✓ Series is up to date"
else
echo "⚠ Series may need updating"
# Show what changed
git range-diff --stat "$upstream_branch..$series_branch" "$upstream_branch..$series_branch"
fi
# Check series health
series_length=$(echo "$current_series" | wc -l)
echo "Series length: $series_length commits"
# Check for merge commits
merge_commits=$(git rev-list --merges "$upstream_branch..$series_branch" | wc -l)
if [ "$merge_commits" -gt 0 ]; then
echo "⚠ Found $merge_commits merge commits in series"
fi
# Validate commit messages
echo "Commit message validation:"
git log --oneline "$upstream_branch..$series_branch" | while read -r line; do
commit_msg=$(echo "$line" | cut -d' ' -f2-)
if [ ${#commit_msg} -lt 10 ]; then
echo "⚠ Short commit message: $line"
fi
done
}
patch_series_maintenance "patch-series-v3"
Terminal window
# Debug commit matching issues
debug_commit_matching() {
local range1="$1"
local range2="$2"
echo "Debugging commit matching between ranges"
# Try different creation factors
for factor in 40 60 80; do
echo "Creation factor $factor:"
git range-diff --creation-factor="$factor" "$range1" "$range2" | head -10
echo "---"
done
# Check commit similarity manually
echo "Manual similarity check:"
commits1=$(git rev-list "$range1")
commits2=$(git rev-list "$range2")
for commit1 in $commits1; do
msg1=$(git show -s --format=%s "$commit1")
for commit2 in $commits2; do
msg2=$(git show -s --format=%s "$commit2")
if [ "$msg1" = "$msg2" ]; then
echo "Exact match: $commit1 <-> $commit2"
fi
done
done
}
debug_commit_matching "main..feature-v1" "main..feature-v2"
Terminal window
# Optimize large range comparisons
optimize_large_ranges() {
local range1="$1"
local range2="$2"
echo "Optimizing large range comparison"
# Check range sizes
size1=$(git rev-list --count "$range1")
size2=$(git rev-list --count "$range2")
echo "Range sizes: $size1 vs $size2"
if [ "$size1" -gt 100 ] || [ "$size2" -gt 100 ]; then
echo "Large ranges detected - using optimizations"
# Use higher creation factor for speed
git range-diff --creation-factor=80 --no-color --quiet "$range1" "$range2"
# Or limit to recent commits
recent_range1=$(git rev-list -n 50 "$range1" | tail -1)
recent_range2=$(git rev-list -n 50 "$range2" | tail -1)
if [ -n "$recent_range1" ] && [ -n "$recent_range2" ]; then
git range-diff "${recent_range1}..${range1}" "${recent_range2}..${range2}"
fi
else
git range-diff "$range1" "$range2"
fi
}
optimize_large_ranges "main..feature" "main..feature-updated"
Terminal window
# Understand range-diff output
interpret_range_diff() {
local range1="$1"
local range2="$2"
echo "Interpreting range-diff output"
echo "Legend:"
echo " 1: abc123 ! 2: def456 - Commit changed between ranges"
echo " 1: abc123 = 2: def456 - Commit unchanged"
echo " 1: abc123 > 2: def456 - Commit added in second range"
echo " 1: abc123 < 2: def456 - Commit removed from second range"
echo ""
git range-diff "$range1" "$range2"
}
interpret_range_diff "v1.0..v1.1" "v1.0..v1.2"
Terminal window
# Handle merge commits in range-diff
handle_merge_commits() {
local range1="$1"
local range2="$2"
echo "Handling merge commits in range-diff"
# Show merge commits
echo "Merge commits in range1:"
git rev-list --merges "$range1"
echo "Merge commits in range2:"
git rev-list --merges "$range2"
# Compare with different merge formats
echo "Using first-parent:"
git range-diff --diff-merges=first-parent "$range1" "$range2" | head -10
echo "Using separate:"
git range-diff --diff-merges=separate "$range1" "$range2" | head -10
# Filter out merges
echo "Excluding merges:"
git range-diff --no-merges "$range1" "$range2" | head -10
}
handle_merge_commits "main..feature" "main..feature-merged"
Terminal window
# Debug path-specific range-diff issues
debug_path_issues() {
local range1="$1"
local range2="$2"
local path="$3"
echo "Debugging path-specific issues: $path"
# Check if path exists in ranges
echo "Files in range1:"
git rev-list "$range1" | xargs git show --name-only | grep "^$path" | sort | uniq
echo "Files in range2:"
git rev-list "$range2" | xargs git show --name-only | grep "^$path" | sort | uniq
# Compare specific path
git range-diff "$range1" "$range2" -- "$path"
# Check for renames
git range-diff --follow "$range1" "$range2" -- "$path"
}
debug_path_issues "main..feature" "main..feature-updated" "src/main.js"
#!/bin/bash
# Code review workflow with range-diff
code_review_workflow() {
local feature_branch="$1"
local base_branch="${2:-main}"
local reviewer="${3:-}"
echo "Code review workflow for $feature_branch"
# Get range for review
review_range="$base_branch..$feature_branch"
# Show overview
echo "=== Review Overview ==="
commit_count=$(git rev-list --count "$review_range")
echo "Commits to review: $commit_count"
# Show range diff summary
echo -e "\n=== Range Diff Summary ==="
git range-diff --color=always --stat "$review_range" | head -20
# Check for concerning patterns
echo -e "\n=== Quality Checks ==="
# Large commits
large_commits=$(git rev-list "$review_range" | while read -r commit; do
changes=$(git show --stat "$commit" | tail -1 | awk '{print $4+$6}')
if [ "$changes" -gt 200 ]; then
echo "Large commit: $(git show -s --format='%h %s' "$commit") ($changes changes)"
fi
done)
if [ -n "$large_commits" ]; then
echo "⚠ Large commits detected:"
echo "$large_commits"
fi
# Commit message quality
poor_messages=$(git log --oneline "$review_range" | awk 'length($2) < 15' | wc -l)
if [ "$poor_messages" -gt 0 ]; then
echo "$poor_messages commits with short messages"
fi
# Generate review report
if [ -n "$reviewer" ]; then
report_file="review-report-$(date +%Y%m%d-%H%M%S).txt"
{
echo "Code Review Report"
echo "=================="
echo "Branch: $feature_branch"
echo "Base: $base_branch"
echo "Reviewer: $reviewer"
echo "Date: $(date)"
echo ""
git range-diff "$review_range"
} > "$report_file"
echo "✓ Review report generated: $report_file"
fi
}
code_review_workflow "feature/user-auth" "develop" "security-team"
Terminal window
# Validate rebase operations
rebase_validation() {
local original_ref="$1"
local rebased_ref="${2:-HEAD}"
echo "Rebase validation: $original_ref -> $rebased_ref"
# Store original range
original_range="$original_ref"
# Get rebased range
rebased_range="$rebased_ref"
# Compare ranges
echo "=== Range Comparison ==="
git range-diff "$original_range" "$rebased_range"
# Check for semantic changes
semantic_changes=$(git range-diff --creation-factor=90 "$original_range" "$rebased_range" | grep -c "!")
if [ "$semantic_changes" -gt 0 ]; then
echo "$semantic_changes commits have semantic changes"
# Show detailed changes
git range-diff --patch "$original_range" "$rebased_range" | head -50
# Ask for confirmation
read -p "Accept rebase with semantic changes? (y/N): " confirm
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
echo "Rebase validation failed"
exit 1
fi
else
echo "✓ Rebase appears to preserve semantics"
fi
# Run tests
echo "=== Testing Rebased Code ==="
if run-tests.sh; then
echo "✓ All tests pass"
else
echo "✗ Tests failed after rebase"
exit 1
fi
echo "✓ Rebase validation complete"
}
rebase_validation "HEAD~5" "HEAD"
Terminal window
# Version control for patch series
patch_series_versioning() {
local series_name="$1"
local new_version="$2"
echo "Creating patch series version: $series_name-$new_version"
# Tag current series
series_tag="$series_name-v$new_version"
git tag "$series_tag" HEAD
# Compare with previous version
prev_version=$((new_version - 1))
prev_tag="$series_name-v$prev_version"
if git rev-parse --verify "$prev_tag" >/dev/null 2>&1; then
echo "=== Changes from v$prev_version to v$new_version ==="
git range-diff "$prev_tag..$series_tag"
# Analyze changes
additions=$(git range-diff "$prev_tag..$series_tag" | grep -c ">")
deletions=$(git range-diff "$prev_tag..$series_tag" | grep -c "<")
modifications=$(git range-diff "$prev_tag..$series_tag" | grep -c "!")
echo "Summary: +$additions -$deletions ~$modifications patches"
else
echo "First version of series"
fi
# Archive series
series_file="$series_name-$new_version.patch"
git format-patch --stdout "$prev_tag..$series_tag" > "$series_file"
echo "✓ Series archived: $series_file"
# Update series documentation
echo "v$new_version - $(date)" >> "$series_name-series.txt"
git range-diff --no-color "$prev_tag..$series_tag" >> "$series_name-series.txt"
}
patch_series_versioning "network-stack" "3"
Terminal window
# Regression testing with range-diff
regression_testing() {
local baseline_range="$1"
local test_range="$2"
echo "Regression testing: $baseline_range vs $test_range"
# Get changed commits
changed_commits=$(git range-diff --creation-factor=70 "$baseline_range" "$test_range" | grep "!" | awk '{print $2}')
if [ -z "$changed_commits" ]; then
echo "No significant changes detected"
return 0
fi
echo "Found $(echo "$changed_commits" | wc -l) changed commits"
# Test each changed commit
failed_tests=0
for commit in $changed_commits; do
echo "Testing commit $commit..."
# Checkout and test
if git checkout -q "$commit" && run-regression-tests.sh; then
echo "$commit: tests pass"
else
echo "$commit: tests fail"
failed_tests=$((failed_tests + 1))
# Show what changed
git show --stat "$commit"
fi
done
# Return to original state
git checkout -q -
if [ "$failed_tests" -gt 0 ]; then
echo "$failed_tests commits failed regression tests"
exit 1
else
echo "✓ All changed commits pass regression tests"
fi
}
regression_testing "v1.0..v1.1" "v1.0..v1.2"
Terminal window
# Integration with code review platforms
code_review_platform() {
local pr_number="$1"
local repo_owner="$2"
local repo_name="$3"
echo "Code review platform integration for PR #$pr_number"
# Get PR information (mock - would use API)
base_sha="abc123"
head_sha="def456"
# Compare ranges
echo "=== PR #$pr_number Range Diff ==="
git range-diff "$base_sha..$head_sha"
# Generate review comments
review_comments=""
# Check for large commits
large_commits=$(git rev-list "$base_sha..$head_sha" | while read -r commit; do
lines=$(git show --stat "$commit" | tail -1 | awk '{print $4+$6}')
if [ "$lines" -gt 300 ]; then
review_comments="${review_comments}Large commit detected: $(git show -s --format='%h %s' "$commit") ($lines lines)\n"
fi
done)
# Check commit messages
poor_messages=$(git log --oneline "$base_sha..$head_sha" | awk 'length($2) < 20' | wc -l)
if [ "$poor_messages" -gt 0 ]; then
review_comments="${review_comments}$poor_messages commits have short messages\n"
fi
# Check for test files
test_files=$(git rev-list "$base_sha..$head_sha" | xargs git show --name-only | grep -c "test\|spec")
if [ "$test_files" -eq 0 ]; then
review_comments="${review_comments}No test files detected in changes\n"
fi
# Output review comments
if [ -n "$review_comments" ]; then
echo "=== Review Comments ==="
echo -e "$review_comments"
else
echo "✓ No automated review issues found"
fi
# Generate diff for review
git range-diff --no-color "$base_sha..$head_sha" > "pr-$pr_number-range-diff.txt"
echo "✓ Range diff saved: pr-$pr_number-range-diff.txt"
}
code_review_platform "123" "myorg" "myrepo"

What’s the difference between range-diff and regular diff?

Section titled “What’s the difference between range-diff and regular diff?”

range-diff compares entire commit ranges and finds corresponding commits between ranges; regular diff compares file contents or specific commits.

How do I compare two versions of a branch?

Section titled “How do I compare two versions of a branch?”

Use git range-diff main..feature-v1 main..feature-v2 to see how commits changed between versions.

It controls how similar commits must be to be considered matches (default 60%). Higher values require more similarity.

By default it ignores merges, but —diff-merges option controls how merge commits are handled in the comparison.

Use git range-diff range1 range2 | grep ”!” to show only commits that have different content between ranges.

What’s the difference between A..B and A…B syntax?

Section titled “What’s the difference between A..B and A…B syntax?”

Both work for range-diff, but A…B is symmetric difference (commits in A or B but not both), while A..B is asymmetric.

Yes, use git range-diff origin/main..feature origin/main..feature-updated to compare remote branches.

How do I limit range-diff to specific files?

Section titled “How do I limit range-diff to specific files?”

Add — path/to/file at the end: git range-diff range1 range2 — src/main.js

What if range-diff shows too many false matches?

Section titled “What if range-diff shows too many false matches?”

Increase the creation-factor: git range-diff —creation-factor=80 range1 range2

Yes, it finds corresponding commits regardless of order and shows reordering as part of the comparison.

Capture output with command substitution or redirect to file. Check exit code for success/failure.

What’s the impact of range-diff on performance?

Section titled “What’s the impact of range-diff on performance?”

It can be slow for large ranges as it compares patches. Use —no-color and limit ranges for better performance.

Yes, use —stat option: git range-diff —stat range1 range2

= means unchanged, ! means changed, > means added, < means removed, followed by commit subjects.

No, it works within a single repository. Use git diff for cross-repository comparisons.

What’s the —left-only and —right-only options for?

Section titled “What’s the —left-only and —right-only options for?”

—left-only shows only commits from the first range, —right-only shows only commits from the second range.

Run git range-diff in CI pipelines to validate changes, generate reports, or trigger additional testing.

Applications of the git range-diff command

Section titled “Applications of the git range-diff command”
  1. Rebase Validation: Verify that rebases preserve intended changes and don’t introduce regressions
  2. Patch Series Review: Track evolution of patch series and review changes between versions
  3. Code Review Enhancement: Provide reviewers with clear view of how commits changed
  4. Branch Comparison: Compare different implementations or versions of features
  5. Merge Quality Assurance: Ensure merges don’t introduce unexpected changes
  6. Regression Detection: Identify when commits change behavior between versions