rev-list Git Command Guide
The git rev-list command lists commit objects in reverse chronological order by following parent links from given commits. It performs set operations on commits and is the underlying engine for many Git commands that work with commit ranges and history traversal.
git rev-list Syntax:
Section titled “git rev-list Syntax:”git rev-list [<options>] <commit>... [--] [<path>...]Commit Limiting Options:
Section titled “Commit Limiting Options:”| Option | Description |
|---|---|
-n <number>, --max-count=<number> | Limit number of commits |
--skip=<number> | Skip number of commits |
--since=<date>, --after=<date> | Show commits after date |
--until=<date>, --before=<date> | Show commits before date |
--author=<pattern> | Limit to commits by author |
--committer=<pattern> | Limit to commits by committer |
--grep=<pattern> | Search commit messages |
History Traversal Options:
Section titled “History Traversal Options:”| Option | Description |
|---|---|
--parents | Include parent information |
--children | Include children information |
--left-right | Mark which side of symmetric diff |
--cherry-mark | Mark equivalent commits |
--cherry-pick | Omit equivalent commits |
--boundary | Output excluded boundary commits |
Output Formatting Options:
Section titled “Output Formatting Options:”| Option | Description |
|---|---|
--oneline | Abbreviated commit info |
--pretty=<format> | Pretty print with format |
--header | Show commit headers |
--timestamp | Show raw timestamp |
--relative-date | Show relative dates |
Path and Content Filtering:
Section titled “Path and Content Filtering:”| Option | Description |
|---|---|
-- <path>... | Show only commits affecting paths |
--follow | Follow file renames |
--no-merges | Don’t show merge commits |
--merges | Show only merge commits |
--first-parent | Follow only first parent |
--not <commit> | Exclude commits reachable from |
Advanced Analysis Options:
Section titled “Advanced Analysis Options:”| Option | Description |
|---|---|
--bisect | Mark commits for bisection |
--bisect-vars | Show bisect variables |
--walk-reflogs | Walk reflog instead of commit history |
--count | Count commits instead of listing |
--graph | Show commit graph |
Parameters:
Section titled “Parameters:”| Parameter | Description |
|---|---|
<commit>... | Starting commits for traversal |
<path>... | Limit to commits affecting paths |
Understanding Commit Ranges:
Section titled “Understanding Commit Ranges:”Set Operations with Commits:
Section titled “Set Operations with Commits:”Commit Range Syntax:├── A..B: Commits in B but not in A├── ^A B: Commits reachable from B but not A├── A...B: Symmetric difference (in A or B but not both)├── --not A: Exclude commits reachable from A└── --all: Include all references
Visual Representation: A---B---C---D (main) \ E---F (feature)
main..feature: E, F feature..main: (empty) main...feature: C, D, E, FCommit Graph Traversal:
Section titled “Commit Graph Traversal:”Traversal Rules:├── Start from given commits├── Follow parent pointers backwards├── Include all reachable commits├── Exclude commits marked with ^├── Apply filters and limits└── Output in reverse chronological order
Example Traversal: git rev-list A B ^C = (commits reachable from A OR B) MINUS (commits reachable from C)Path-Based Filtering:
Section titled “Path-Based Filtering:”Path Filtering:├── Only commits that modify specified paths├── Follows file renames with --follow├── Can combine with commit ranges├── Useful for file-specific history└── More efficient than git log -- <path>Basic Rev-List Operations:
Section titled “Basic Rev-List Operations:”Simple Commit Listing:
Section titled “Simple Commit Listing:”# List recent commitsgit rev-list HEAD
# List commits with limitgit rev-list -n 10 HEAD
# List commits with parentsgit rev-list --parents HEAD
# Count commitsgit rev-list --count HEADCommit Range Operations:
Section titled “Commit Range Operations:”# Commits in feature but not maingit rev-list main..feature
# Symmetric differencegit rev-list main...feature
# Exclude specific commitsgit rev-list HEAD ^abc123
# Multiple starting pointsgit rev-list branch1 branch2 ^mainTime-Based Filtering:
Section titled “Time-Based Filtering:”# Commits from last weekgit rev-list --since="1 week ago" HEAD
# Commits before specific dategit rev-list --until="2023-01-01" HEAD
# Commits between datesgit rev-list --since="2023-01-01" --until="2023-12-31" HEADAuthor and Message Filtering:
Section titled “Author and Message Filtering:”# Commits by specific authorgit rev-list --author="John Doe" HEAD
# Commits matching message patterngit rev-list --grep="fix" HEAD
# Combine filtersgit rev-list --author="Jane" --grep="bug" --since="1 month ago" HEADAdvanced Commit Analysis:
Section titled “Advanced Commit Analysis:”Path-Specific History:
Section titled “Path-Specific History:”# Commits affecting specific filegit rev-list HEAD -- file.txt
# Commits affecting directorygit rev-list HEAD -- src/
# Multiple pathsgit rev-list HEAD -- file1.txt file2.txt
# Follow renamesgit rev-list --follow HEAD -- renamed-file.txtMerge and Branch Analysis:
Section titled “Merge and Branch Analysis:”# Only merge commitsgit rev-list --merges HEAD
# Exclude merge commitsgit rev-list --no-merges HEAD
# First parent only (ignore merged branches)git rev-list --first-parent HEAD
# Show merge boundariesgit rev-list --boundary main..featureGraph and Relationship Analysis:
Section titled “Graph and Relationship Analysis:”# Show commit relationshipsgit rev-list --parents HEAD
# Show left-right marks for rangesgit rev-list --left-right main...feature
# Cherry pick analysisgit rev-list --cherry-mark upstream...topic
# Count commits between pointsgit rev-list --count main..featureBisect Support:
Section titled “Bisect Support:”# Mark commits for bisectiongit rev-list --bisect bad-commit..good-commit
# Show bisect variablesgit rev-list --bisect-vars bad-commit..good-commit
# Custom bisect rangegit rev-list --bisect HEAD~100..HEADConfiguration and Best Practices:
Section titled “Configuration and Best Practices:”Git Configuration for Rev-List:
Section titled “Git Configuration for Rev-List:”# Configure default behaviorgit config log.date relative # Relative datesgit config log.follow true # Follow renamesgit config log.showSignature false # Hide signatures
# Configure pretty formatsgit config pretty.oneline "%h %s" # One line formatgit config pretty.short "%h %s%n%b" # Short format
# Configure search patternsgit config grep.patternType fixed # Exact matchesgit config grep.extendedRegexp true # Extended regexEfficient Rev-List Usage:
Section titled “Efficient Rev-List Usage:”# Use --count for counting onlygit rev-list --count HEAD # Faster than wc -l
# Limit output for large reposgit rev-list -n 100 HEAD
# Use --since for time rangesgit rev-list --since="1 month ago" HEAD
# Combine filters efficientlygit rev-list --author="John" --grep="fix" --since="1 week ago" HEADPerformance Optimization:
Section titled “Performance Optimization:”# Avoid full history traversalgit rev-list -n 50 HEAD # Limit results
# Use specific starting pointsgit rev-list v1.0..HEAD # Limited range
# Cache results when possiblegit rev-list --all | head -1000 > commit-cache.txt
# Use --no-walk for specific commitsgit rev-list --no-walk HEAD~10..HEADIntegration with Development Workflows:
Section titled “Integration with Development Workflows:”Release Management:
Section titled “Release Management:”#!/bin/bash# Analyze commits for release
release_commit_analysis() { local previous_tag="$1" local current_head="${2:-HEAD}"
echo "Analyzing commits since $previous_tag"
# Count total commits total_commits=$(git rev-list --count "$previous_tag..$current_head") echo "Total commits: $total_commits"
# Count merge commits merge_commits=$(git rev-list --merges --count "$previous_tag..$current_head") echo "Merge commits: $merge_commits"
# Count authors authors=$(git rev-list --pretty="%an" "$previous_tag..$current_head" | grep "^[^commit]" | sort | uniq -c | sort -nr) echo "Top contributors:" echo "$authors" | head -5
# Check for large commits echo "Commits with many changes:" git rev-list --pretty="%h %s" "$previous_tag..$current_head" | while read -r line; do hash=$(echo "$line" | cut -d' ' -f1) changes=$(git show --stat "$hash" | tail -1 | awk '{print $4+$6}') if [ "$changes" -gt 100 ]; then echo "$line ($changes changes)" fi done}
release_commit_analysis "v1.0.0"Code Review Automation:
Section titled “Code Review Automation:”# Analyze commits for code reviewcode_review_analysis() { local branch="$1" local base="${2:-main}"
echo "Code review analysis for $branch vs $base"
# Find new commits commits=$(git rev-list "$base..$branch")
echo "Commits to review: $(echo "$commits" | wc -l)"
# Check for WIP commits wip_commits=$(echo "$commits" | xargs git show --pretty="%s" | grep -i "wip\|work\|temp" | wc -l) if [ "$wip_commits" -gt 0 ]; then echo "⚠ Found $wip_commits WIP commits" fi
# Check commit message quality empty_messages=$(echo "$commits" | xargs git show --pretty="%s" | grep -E "^.{0,10}$" | wc -l) if [ "$empty_messages" -gt 0 ]; then echo "⚠ Found $empty_messages commits with short messages" fi
# Check for large commits for commit in $commits; do files=$(git show --name-only "$commit" | tail -n +2 | wc -l) if [ "$files" -gt 20 ]; then echo "⚠ Large commit $commit affects $files files" fi done
echo "Analysis complete"}
code_review_analysis "feature/new-ui" "develop"Repository Statistics:
Section titled “Repository Statistics:”# Generate repository statisticsrepo_statistics() { echo "Repository Statistics"
# Total commits total_commits=$(git rev-list --all --count) echo "Total commits: $total_commits"
# Commits by author echo "Commits by author:" git rev-list --all --pretty="%an" | grep "^[^commit]" | sort | uniq -c | sort -nr | head -10
# Commits by month echo "Commits by month:" git rev-list --all --pretty="%ai" | cut -d- -f1-2 | sort | uniq -c | sort -nr | head -12
# Most active periods echo "Most active days:" git rev-list --all --pretty="%ai" | cut -d' ' -f1 | sort | uniq -c | sort -nr | head -10
# Branch analysis echo "Branch commit counts:" for branch in $(git branch --all | sed 's|* ||' | sed 's|remotes/||'); do if git rev-parse --verify "$branch" >/dev/null 2>&1; then count=$(git rev-list --count "$branch" 2>/dev/null || echo "0") echo "$branch: $count commits" fi done | sort -t: -k2 -nr | head -10}
repo_statisticsTroubleshooting Common Issues:
Section titled “Troubleshooting Common Issues:”Performance Issues with Large Repositories:
Section titled “Performance Issues with Large Repositories:”# Optimize for large reposlarge_repo_revlist() { echo "Optimizing rev-list for large repository"
# Use --no-walk for specific commits git rev-list --no-walk HEAD~1000..HEAD
# Limit output git rev-list -n 100 HEAD
# Use since/until for time ranges git rev-list --since="1 month ago" HEAD
# Avoid full history traversal git rev-list --all | head -10000}
large_repo_revlistMemory Issues:
Section titled “Memory Issues:”# Handle memory constraintsmemory_efficient_revlist() { echo "Memory-efficient rev-list operations"
# Process in batches git rev-list HEAD | xargs -n 1000 echo
# Use streaming output git rev-list --oneline HEAD | head -10000
# Avoid storing large result sets git rev-list --count HEAD # Just count
# Use --no-objects for commit-only info git rev-list --no-objects HEAD}
memory_efficient_revlistComplex Range Issues:
Section titled “Complex Range Issues:”# Debug complex commit rangesdebug_commit_ranges() { local range="$1"
echo "Debugging range: $range"
# Show what the range includes echo "Included commits:" git rev-list "$range" | head -10
# Show boundaries echo "Boundary commits:" git rev-list --boundary "$range"
# Show left-right marks echo "Left-right marks:" git rev-list --left-right "$range" | head -10
# Verify range syntax if ! git rev-list "$range" >/dev/null 2>&1; then echo "Invalid range syntax" exit 1 fi}
debug_commit_ranges "main..feature"Path Filtering Issues:
Section titled “Path Filtering Issues:”# Handle path filtering problemsdebug_path_filtering() { local path="$1"
echo "Debugging path filtering for: $path"
# Check if path exists in history if ! git log --oneline -- "$path" | head -1 >/dev/null 2>&1; then echo "Path not found in repository history" return 1 fi
# Show commits affecting path git rev-list HEAD -- "$path" | head -10
# Check for renames git rev-list --follow HEAD -- "$path" | head -10
# Compare with git log log_count=$(git log --oneline -- "$path" | wc -l) revlist_count=$(git rev-list HEAD -- "$path" | wc -l)
echo "git log count: $log_count" echo "rev-list count: $revlist_count"}
debug_path_filtering "src/main.js"Author and Date Filtering Issues:
Section titled “Author and Date Filtering Issues:”# Debug filtering problemsdebug_filtering() { local filter="$1" local value="$2"
echo "Debugging $filter filtering for: $value"
case "$filter" in "author") git rev-list --pretty="%an <%ae>" HEAD | grep -i "$value" | head -5 ;; "grep") git rev-list --grep="$value" HEAD | head -5 ;; "since") git rev-list --since="$value" HEAD | head -5 ;; "until") git rev-list --until="$value" HEAD | head -5 ;; esac}
debug_filtering "author" "john"Real-World Usage Examples:
Section titled “Real-World Usage Examples:”Security Audit and Compliance:
Section titled “Security Audit and Compliance:”#!/bin/bash# Security audit using rev-list
security_audit() { local since="${1:-1.month.ago}" local audit_file="security-audit-$(date +%Y%m%d).txt"
echo "Security Audit Report - $(date)" > "$audit_file" echo "Period: $since" >> "$audit_file" echo "=================================" >> "$audit_file"
# Find commits by external contributors echo "External contributor commits:" >> "$audit_file" git rev-list --since="$since" --pretty="%h %an <%ae> %s" HEAD | grep -v "@company.com" | head -20 >> "$audit_file"
# Find large commits (potential security issues) echo -e "\nLarge commits (potential review needed):" >> "$audit_file" git rev-list --since="$since" HEAD | while read -r commit; do changes=$(git show --stat "$commit" | tail -1 | awk '{print $4+$6}') if [ "$changes" -gt 500 ]; then echo "$commit: $changes changes" >> "$audit_file" fi done
# Check for sensitive file modifications echo -e "\nSensitive file modifications:" >> "$audit_file" git rev-list --since="$since" HEAD -- "*.key" "*.pem" "*.secret" | while read -r commit; do echo "Sensitive files in: $commit" >> "$audit_file" git show --name-only "$commit" | grep -E "\.(key|pem|secret)$" >> "$audit_file" done
# Check for emergency commits echo -e "\nEmergency/hotfix commits:" >> "$audit_file" git rev-list --since="$since" --grep="emergency\|hotfix\|urgent" HEAD | xargs git show --oneline >> "$audit_file"
echo "Security audit complete: $audit_file"}
security_audit "2 weeks ago"Development Velocity Metrics:
Section titled “Development Velocity Metrics:”# Calculate development velocitydevelopment_velocity() { local period="${1:-1.month}" local output_file="velocity-report-$(date +%Y%m%d).json"
echo "Calculating development velocity for $period"
# Total commits total_commits=$(git rev-list --since="$period" --count HEAD)
# Commits by author authors=$(git rev-list --since="$period" --pretty="%an" HEAD | grep "^[^commit]" | sort | uniq -c | sort -nr | awk '{print "{\"author\":\""$2"\",\"commits\":"$1"}"}' | paste -sd, -)
# Daily activity daily_activity=$(git rev-list --since="$period" --pretty="%ai" HEAD | cut -d' ' -f1 | sort | uniq -c | awk '{print "{\"date\":\""$2"\",\"commits\":"$1"}"}' | paste -sd, -)
# Branch activity branch_commits=$(for branch in $(git branch --all | sed 's|* ||'); do if git rev-parse --verify "$branch" >/dev/null 2>&1; then count=$(git rev-list --since="$period" --count "$branch" 2>/dev/null || echo 0) echo "{\"branch\":\"$branch\",\"commits\":$count}" fi done | paste -sd, -)
# Generate JSON report cat > "$output_file" << EOF{ "period": "$period", "total_commits": $total_commits, "authors": [$authors], "daily_activity": [$daily_activity], "branch_activity": [$branch_commits], "generated_at": "$(date -Iseconds)"}EOF
echo "Velocity report generated: $output_file" echo "Total commits in period: $total_commits"}
development_velocity "3 months ago"Automated Testing Integration:
Section titled “Automated Testing Integration:”# Integrate with CI/CD testingci_commit_testing() { local base_commit="$1" local head_commit="${2:-HEAD}"
echo "Testing commits from $base_commit to $head_commit"
# Get commits to test commits_to_test=$(git rev-list "$base_commit..$head_commit")
echo "Found $(echo "$commits_to_test" | wc -l) commits to test"
# Test each commit individually failed_commits=() for commit in $commits_to_test; do echo "Testing commit $commit..."
# Checkout commit if git checkout -q "$commit"; then # Run tests if ! run-tests.sh; then echo "✗ Tests failed for $commit" failed_commits+=("$commit") else echo "✓ Tests passed for $commit" fi else echo "✗ Could not checkout $commit" failed_commits+=("$commit") fi done
# Return to original commit git checkout -q "$head_commit"
# Report results if [ ${#failed_commits[@]} -eq 0 ]; then echo "✓ All commits passed testing" return 0 else echo "✗ Failed commits: ${failed_commits[*]}" return 1 fi}
ci_commit_testing "main" "feature/new-feature"Repository Archaeology:
Section titled “Repository Archaeology:”# Analyze repository history patternsrepository_archaeology() { echo "Repository Archaeology Report" echo "============================"
# Find oldest commit oldest=$(git rev-list --reverse HEAD | head -1) oldest_date=$(git show -s --format=%ci "$oldest") echo "Oldest commit: $oldest ($oldest_date)"
# Find largest commits echo -e "\nLargest commits by changes:" git rev-list HEAD | while read -r commit; do stats=$(git show --stat "$commit" | tail -1) insertions=$(echo "$stats" | awk '{print $4}' | tr -d '+') deletions=$(echo "$stats" | awk '{print $6}' | tr -d '-') total=$((insertions + deletions)) echo "$commit: $total changes (+$insertions -$deletions)" done | sort -t: -k2 -nr | head -10
# Find most active periods echo -e "\nMost active months:" git rev-list --pretty="%ai" HEAD | cut -d- -f1-2 | sort | uniq -c | sort -nr | head -12
# Find commit message patterns echo -e "\nCommon commit message patterns:" git rev-list --pretty="%s" HEAD | head -1000 | sed 's/^[[:space:]]*//' | cut -d' ' -f1 | tr '[:upper:]' '[:lower:]' | sort | uniq -c | sort -nr | head -20
# Find authors over time echo -e "\nAuthor activity timeline:" git rev-list --pretty="%ai %an" HEAD | awk '{print $1, $2, $3}' | sort | uniq -c | sort -k2,3 | head -20}
repository_archaeologyConflict Analysis and Prevention:
Section titled “Conflict Analysis and Prevention:”# Analyze potential merge conflictsconflict_analysis() { local branch1="$1" local branch2="$2"
echo "Analyzing potential conflicts between $branch1 and $branch2"
# Find commits unique to each branch branch1_commits=$(git rev-list "$branch2..$branch1") branch2_commits=$(git rev-list "$branch1..$branch2")
echo "$branch1 unique commits: $(echo "$branch1_commits" | wc -l)" echo "$branch2 unique commits: $(echo "$branch2_commits" | wc -l)"
# Find files modified by both branches branch1_files=$(echo "$branch1_commits" | xargs git show --name-only | grep "^[^commit]" | sort | uniq) branch2_files=$(echo "$branch2_commits" | xargs git show --name-only | grep "^[^commit]" | sort | uniq)
# Find overlapping files overlapping_files=$(comm -12 <(echo "$branch1_files") <(echo "$branch2_files"))
if [ -n "$overlapping_files" ]; then echo -e "\nPotentially conflicting files:" echo "$overlapping_files"
# Analyze conflict risk high_risk_files=$(echo "$overlapping_files" | while read -r file; do branch1_changes=$(echo "$branch1_commits" | xargs git show -- "$file" 2>/dev/null | wc -l) branch2_changes=$(echo "$branch2_commits" | xargs git show -- "$file" 2>/dev/null | wc -l) echo "$file: $branch1_changes vs $branch2_changes changes" done | sort -t: -k2 -nr | head -5)
echo -e "\nHigh-risk files:" echo "$high_risk_files" else echo "No overlapping file modifications found" fi}
conflict_analysis "main" "feature/large-refactor"What’s the difference between rev-list and log?
Section titled “What’s the difference between rev-list and log?”git rev-list outputs raw commit hashes for scripting, while git log provides formatted, human-readable output. rev-list is faster and more suitable for automation.
How do I get commits between two branches?
Section titled “How do I get commits between two branches?”Use git rev-list branch1..branch2 for commits in branch2 but not branch1. Use branch1…branch2 for symmetric difference (commits in either but not both).
Can I limit rev-list output?
Section titled “Can I limit rev-list output?”Yes, use -n
How do I count commits with rev-list?
Section titled “How do I count commits with rev-list?”Use —count option: git rev-list —count
What’s the difference between A..B and A…B?
Section titled “What’s the difference between A..B and A…B?”A..B shows commits reachable from B but not A. A…B shows commits reachable from either A or B but not both (symmetric difference).
How do I find commits affecting specific files?
Section titled “How do I find commits affecting specific files?”Use git rev-list
Can rev-list show merge commits only?
Section titled “Can rev-list show merge commits only?”Yes, use —merges to show only merge commits, or —no-merges to exclude them.
How do I use rev-list with dates?
Section titled “How do I use rev-list with dates?”Use —since=“1 week ago” for commits after date, —until=“2023-01-01” for commits before date.
What’s —first-parent for?
Section titled “What’s —first-parent for?”—first-parent follows only the first parent of merge commits, ignoring merged branch history. Useful for understanding mainline development.
Can rev-list help with bisect?
Section titled “Can rev-list help with bisect?”Yes, —bisect marks commits for bisection, —bisect-vars shows variables for bisect automation.
How do I find commits by author?
Section titled “How do I find commits by author?”Use —author="
What’s the —boundary option for?
Section titled “What’s the —boundary option for?”—boundary outputs the commits at the boundary of the range that would normally be excluded, useful for understanding range limits.
Can rev-list search commit messages?
Section titled “Can rev-list search commit messages?”Yes, use —grep="
How do I get relative dates with rev-list?
Section titled “How do I get relative dates with rev-list?”Use —pretty=“%ar %h %s” for relative dates, or configure log.relativeDate.
What’s —cherry-mark for?
Section titled “What’s —cherry-mark for?”—cherry-mark marks commits that are equivalent (same diff) between two branches, useful for identifying duplicate work.
Can rev-list work with reflogs?
Section titled “Can rev-list work with reflogs?”Yes, —walk-reflogs walks reflog entries instead of commit history, useful for analyzing reference changes.
How do I get commit parents with rev-list?
Section titled “How do I get commit parents with rev-list?”Use —parents to include parent commit hashes in output.
What’s the —left-right option?
Section titled “What’s the —left-right option?”—left-right marks commits with < (left side) or > (right side) for ranges, showing which branch they belong to.
Can rev-list be used for performance monitoring?
Section titled “Can rev-list be used for performance monitoring?”Yes, combine with time commands to benchmark repository operations and identify performance bottlenecks.
Applications of the git rev-list command
Section titled “Applications of the git rev-list command”- Commit Range Analysis: Analyze differences between branches and commits
- Repository Statistics: Generate metrics about development activity and patterns
- Automated Testing: Test specific commit ranges in CI/CD pipelines
- Code Review Automation: Analyze commits for review requirements and quality checks
- Security Auditing: Identify suspicious commits and track changes to sensitive files
- Development Velocity: Measure team productivity and contribution patterns