Skip to content

revisions Git Command Guide

The git revisions documentation explains how to specify commits, trees, blobs, and ranges using Git’s extended SHA-1 syntax. This comprehensive guide covers all revision specification methods used throughout Git commands.

Terminal window
# Full SHA-1 hash
dae86e1950b1277e545cee180551750029cfe735
# Abbreviated SHA-1 (first 4+ characters)
dae86e
# Full refs
refs/heads/main
refs/tags/v1.0
refs/remotes/origin/main
# Symbolic refs
HEAD
ORIG_HEAD
MERGE_HEAD
CHERRY_PICK_HEAD
Terminal window
# Local branches
main
develop
feature/user-auth
# Remote tracking branches
origin/main
upstream/develop
# Tags
v1.0
v2.1.3
stable
# Tag with branch
v1.0^{} # Annotated tag object
v1.0^{tree} # Tree of lightweight tag
Terminal window
# Parent references
HEAD^ # First parent of HEAD
HEAD^2 # Second parent of HEAD
HEAD^^ # Grandparent (HEAD^1^1)
# Tilde notation
HEAD~ # First parent (same as HEAD^)
HEAD~2 # Grandparent
HEAD~3 # Great-grandparent
# Complex relatives
HEAD^2~3 # Third parent of second parent of HEAD
main~5^2 # Second parent of 5th ancestor of main
Terminal window
# Current reflog positions
HEAD@{0} # Current HEAD
HEAD@{1} # Previous HEAD position
main@{2} # Two steps ago for main branch
# Time-based reflog
HEAD@{yesterday}
HEAD@{1.day.ago}
HEAD@{2.weeks.ago}
HEAD@{3.months.ago}
# Date specifications
HEAD@{2023-01-01}
HEAD@{2023-01-01 12:00:00}
# Relative time
HEAD@{5.minutes.ago}
HEAD@{2.hours.ago}
Terminal window
# Two-dot range (symmetric difference)
A..B # Commits in B but not in A
main..feature # Commits in feature since main
# Three-dot range (symmetric)
A...B # Commits in A or B but not both
main...feature # Commits unique to each branch
# Multiple points
A B ^C # Commits reachable from A or B but not C
--not A # Exclude commits reachable from A
Terminal window
# Special refs
HEAD # Current branch tip
ORIG_HEAD # HEAD before merge/reset
MERGE_HEAD # Merge commit being merged
CHERRY_PICK_HEAD # Commit being cherry-picked
REVERT_HEAD # Commit being reverted
# Empty tree
4b825dc642cb6eb9a060e54bf8d69288fbee4904 # Empty tree SHA-1
Terminal window
# Tree objects
HEAD: # Root tree of HEAD
HEAD:src # src directory in HEAD
main:docs/README.md # Specific file tree
# Blob objects
HEAD:README.md # README.md content in HEAD
v1.0:LICENSE # LICENSE content in v1.0
main^{blob} # Blob pointed to by main
# Tree from commit
abc123^{tree} # Tree object of commit abc123
Terminal window
# Complex exclusions
--not main --not develop # Exclude main and develop branches
# Branch-specific ranges
origin/main..origin/feature # Remote branch range
# Tag-based ranges
v1.0..v2.0 # Between tags
v1.0...v2.0 # Symmetric tag range
# Mixed references
HEAD origin/main ^origin/develop # Complex inclusion/exclusion
Terminal window
# Since/until dates
--since="2 weeks ago"
--until="2023-01-01"
--after="1 month ago"
--before="yesterday"
# Author/date filters (with commands)
--author="John Doe"
--committer="jane@example.com"
--grep="bug fix"
Terminal window
# File-specific history
-- src/main.c # Commits affecting src/main.c
--follow -- README.md # Follow renames
# Directory filtering
-- src/ # Commits affecting src directory
-- Documentation/ # Documentation changes
Resolution Steps:
1. Check for exact SHA-1 match
2. Check local refs (heads, tags, remotes)
3. Check reflog references
4. Check abbreviated SHA-1
5. Check ancestry references (^ ~)
6. Apply range operators (.. ...)
7. Resolve special symbols
When Multiple Matches Exist:
├── Prefer local branches over remote
├── Prefer tags over branches
├── Use full SHA-1 for uniqueness
├── Use ^0 for commit vs tag disambiguation
└── Use ^{type} for object type specification
Terminal window
Common Resolution Errors:
├── 'ambiguous argument' - Multiple possible matches
├── 'unknown revision' - No matches found
├── 'bad revision' - Invalid syntax
├── 'needed a single revision' - Range where single rev expected
Resolution Strategies:
├── Use longer SHA-1 abbreviations
├── Specify full ref paths
├── Use ^{type} disambiguators
├── Check reflog with git reflog
Terminal window
# Show specific commit
git show abc123
# Show commit relative to HEAD
git show HEAD~3
# Show merge commit parents
git show HEAD^1 # First parent
git show HEAD^2 # Second parent
Terminal window
# Compare branches
git log main..feature
# Show commits since tag
git log v1.0..HEAD
# Cherry-pick from another branch
git cherry-pick feature~2
Terminal window
# Recover from mistake
git reset --hard HEAD@{1} # Go back one step
# See what changed yesterday
git diff HEAD@{yesterday}
# Find when bug was introduced
git bisect start HEAD@{1.week.ago} HEAD
Terminal window
# Find commits in feature but not in main or develop
git log --oneline feature ^main ^develop
# Show merge commits
git log --merges main..feature
# Find commits by author in range
git log --author="John" main..feature
Terminal window
# Show file at specific revision
git show v1.0:README.md
# Compare file versions
git diff HEAD:src/main.c HEAD~1:src/main.c
# Extract old version
git show HEAD~5:Makefile > old-makefile
Terminal window
# Configure abbreviation length
git config core.abbrev 7
# Configure reflog
git config core.logAllRefUpdates true
git config gc.reflogExpire 90.days
# Configure date formats
git config log.date relative
# Configure default refs
git config core.defaultRef HEAD

Best Practices for Revision Specification:

Section titled “Best Practices for Revision Specification:”
Terminal window
# Use descriptive names
git checkout feature/user-authentication # Good
git checkout abc123 # Less clear
# Prefer relative refs for scripts
HEAD~1 # Better than hardcoded SHA-1
main^ # Clear parent relationship
# Use ranges consistently
main..feature # Commits in feature since main
feature..main # Usually empty
# Validate before using
git rev-parse --verify <ref> # Check existence
git rev-parse --disambiguate=<prefix> # Test disambiguation
Terminal window
# Safe revision handling in scripts
validate_revision() {
local rev="$1"
if ! git rev-parse --verify "$rev" >/dev/null 2>&1; then
echo "Invalid revision: $rev"
exit 1
fi
# Get canonical form
canonical=$(git rev-parse "$rev")
echo "Using revision: $canonical"
}
# Usage
validate_revision "HEAD~2"
Terminal window
# Log with complex ranges
git log --oneline main...feature --author="John" --since="1 week ago"
# Show with reflog
git show HEAD@{2.days.ago}
# Bisect with ranges
git bisect start HEAD HEAD~20
git bisect run test-script.sh
Terminal window
# Diff with complex refs
git diff HEAD~5 HEAD -- src/
# Merge with specific base
git merge --no-ff -m "Merge feature" feature main
# Cherry-pick range
git cherry-pick main..feature
Terminal window
# Create branch from complex ref
git checkout -b new-branch HEAD~10
# Reset to reflog position
git reset --hard main@{yesterday}
# Rebase onto specific commit
git rebase --onto v2.0 v1.5 feature-branch
Terminal window
# Resolve ambiguous refs
git rev-parse --disambiguate=abc # See possible matches
# Use full SHA-1
git show abc123def456 # Instead of short abc123
# Specify ref type
git show v1.0^{commit} # Force commit interpretation
git show v1.0^{tag} # Force tag interpretation
Terminal window
# Fix syntax errors
HEAD^^^ # Invalid - use HEAD~3
HEAD^1^2 # Invalid - use HEAD~2^2 or HEAD^^2
main..feature..bugfix # Invalid - use main feature ^bugfix
# Correct syntax
HEAD~3 # Third ancestor
HEAD^2~2 # Second ancestor of second parent
main feature ^bugfix # Commits in main/feature but not bugfix
Terminal window
# Fix reflog problems
git reflog expire --expire=now --all # Clear reflog
git reflog --all # Check current reflog
# Recover lost refs
git fsck --unreachable | grep commit # Find dangling commits
git update-ref refs/heads/recovered <commit-sha>
Terminal window
# Fix time reference problems
git reflog --since="1 week ago" # Check available entries
# Use specific dates
HEAD@{2023-01-01 10:00:00} # Specific timestamp
# Check timezone settings
git config log.date
date # Verify system time
Terminal window
# Debug path resolution
git ls-tree HEAD # See tree structure
git ls-tree HEAD:src # Check subdirectory
# Fix path issues
git show HEAD:src/main.c # Correct path
git show HEAD:src/main.c # Wrong - no leading slash needed
Terminal window
# Debug range issues
git rev-list main..feature | head -5 # Check range contents
git rev-list --count main..feature # Count commits in range
# Verify branch relationships
git log --oneline --graph --decorate main feature # Visualize
git merge-base main feature # Find common ancestor
Terminal window
# Release preparation with revision ranges
prepare_release() {
local version="$1"
local base_tag="${2:-v$(($version-1))}"
echo "Preparing release $version from $base_tag"
# Check commits since last release
commit_count=$(git rev-list --count "$base_tag..HEAD")
echo "Commits since $base_tag: $commit_count"
# Check for breaking changes
breaking_changes=$(git log --grep="BREAKING" --oneline "$base_tag..HEAD")
if [ -n "$breaking_changes" ]; then
echo "⚠ Breaking changes detected:"
echo "$breaking_changes"
fi
# Generate changelog
echo "# Release $version" > CHANGELOG.md
echo "" >> CHANGELOG.md
git log --pretty=format:"* %s (%h)" "$base_tag..HEAD" >> CHANGELOG.md
# Create release tag
git tag -a "v$version" -m "Release version $version"
}
prepare_release "2.1.0" "v2.0.0"
Terminal window
# Code review with revision ranges
code_review_session() {
local branch="$1"
local base="${2:-main}"
echo "Code review session for $branch vs $base"
# Get commit range
commits=$(git rev-list "$base..$branch")
echo "Commits to review: $(echo "$commits" | wc -l)"
# Show summary
git log --oneline --stat "$base..$branch" | head -30
# Check for large commits
for commit in $commits; do
changes=$(git show --stat "$commit" | tail -1 | awk '{print $4+$6}')
if [ "$changes" -gt 500 ]; then
echo "⚠ Large commit: $(git show -s --format='%h %s' "$commit")"
fi
done
# Interactive review
echo "Starting interactive review..."
git log --patch --reverse "$base..$branch" | less
}
code_review_session "feature/new-api" "develop"
Terminal window
# Analyze repository structure with revisions
analyze_repository() {
echo "Repository Analysis Report"
echo "========================="
# Branch analysis
echo "Branch Information:"
git branch -v | while read -r branch commit message; do
branch=${branch#* }
echo " $branch: $commit - $message"
done
# Tag analysis
echo -e "\nTag Information:"
git tag | while read -r tag; do
commit=$(git rev-parse "$tag")
date=$(git show -s --format=%ci "$tag")
echo " $tag: $commit ($date)"
done
# Reflog summary
echo -e "\nReflog Activity:"
git reflog --all --pretty=format:"%gd %gs %s" | head -10
# Revision statistics
total_commits=$(git rev-list --all --count)
total_refs=$(git rev-parse --all | wc -l)
echo -e "\nStatistics:"
echo " Total commits: $total_commits"
echo " Total refs: $total_refs"
}
analyze_repository
Terminal window
# Test different revisions automatically
automated_revision_testing() {
local test_script="$1"
local revisions="${2:-HEAD~5..HEAD}"
echo "Testing revisions: $revisions"
# Get commits to test
commits=$(git rev-list "$revisions")
failed_tests=0
for commit in $commits; do
echo "Testing commit: $(git log --oneline -1 "$commit")"
# Checkout commit
if git checkout -q "$commit"; then
# Run tests
if ! $test_script; then
echo "✗ Tests failed for $commit"
failed_tests=$((failed_tests + 1))
else
echo "✓ Tests passed for $commit"
fi
else
echo "✗ Could not checkout $commit"
failed_tests=$((failed_tests + 1))
fi
done
# Return to original state
git checkout -q -
if [ "$failed_tests" -gt 0 ]; then
echo "$failed_tests commits failed testing"
return 1
else
echo "✓ All tested commits passed"
return 0
fi
}
automated_revision_testing "npm test" "main..feature"
# Pre-commit hook with revision validation
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
# Validate commits using revision specifications
# Check if commit is on main branch
current_branch=$(git rev-parse --abbrev-ref HEAD)
if [ "$current_branch" = "main" ]; then
echo "Direct commits to main branch not allowed"
echo "Please create a feature branch"
exit 1
fi
# Check commit message format
commit_msg=$(git show -s --format=%s HEAD)
if [[ ! "$commit_msg" =~ ^(feat|fix|docs|style|refactor|test|chore): ]]; then
echo "Commit message must follow conventional format:"
echo " type(scope): description"
echo ""
echo "Valid types: feat, fix, docs, style, refactor, test, chore"
exit 1
fi
# Check file size limits
git diff --cached --name-only | while read -r file; do
size=$(git ls-files --stage "$file" | awk '{print $3}')
if [ "$size" -gt 1048576 ]; then # 1MB
echo "File too large: $file ($(numfmt --to=iec $size))"
exit 1
fi
done
echo "✓ Pre-commit validation passed"
EOF
chmod +x .git/hooks/pre-commit
Terminal window
# Automated bisect with revision ranges
automated_bisect() {
local good_rev="$1"
local bad_rev="${2:-HEAD}"
local test_command="$3"
echo "Starting automated bisect"
echo "Good revision: $good_rev"
echo "Bad revision: $bad_rev"
# Start bisect
git bisect start "$bad_rev" "$good_rev"
# Run bisect
git bisect run bash -c "$test_command"
# Show result
if git bisect log | grep -q "first bad commit"; then
bad_commit=$(git bisect view --format=%H | head -1)
echo "Found bad commit: $(git show -s --format='%h %s' "$bad_commit")"
else
echo "Bisect completed without finding bad commit"
fi
# Clean up
git bisect reset
}
automated_bisect "v1.0" "HEAD" "make test"
Terminal window
# Validate repository migration with revision checking
validate_migration() {
local source_repo="$1"
local target_repo="$2"
echo "Validating repository migration"
# Compare commit counts
source_commits=$(GIT_DIR="$source_repo" git rev-list --all --count)
target_commits=$(GIT_DIR="$target_repo" git rev-list --all --count)
echo "Source commits: $source_commits"
echo "Target commits: $target_commits"
if [ "$source_commits" != "$target_commits" ]; then
echo "⚠ Commit count mismatch"
return 1
fi
# Compare refs
source_refs=$(GIT_DIR="$source_repo" git rev-parse --all | sort)
target_refs=$(GIT_DIR="$target_repo" git rev-parse --all | sort)
if ! diff <(echo "$source_refs") <(echo "$target_refs") >/dev/null; then
echo "⚠ Reference mismatch"
diff <(echo "$source_refs") <(echo "$target_refs")
return 1
fi
# Compare recent commits
source_recent=$(GIT_DIR="$source_repo" git log --oneline -5)
target_recent=$(GIT_DIR="$target_repo" git log --oneline -5)
if [ "$source_recent" != "$target_recent" ]; then
echo "⚠ Recent commit mismatch"
return 1
fi
echo "✓ Migration validation passed"
return 0
}
validate_migration "/path/to/source.git" "/path/to/target.git"

What are the different ways to specify a commit?

Section titled “What are the different ways to specify a commit?”

SHA-1 hash (full or abbreviated), branch names, tag names, HEAD, relative refs (^ ~), reflog (@{ }), and symbolic refs.

Use A..B for commits in B but not A, or A…B for symmetric difference. Multiple refs with ^ for complex ranges.

What’s the difference between HEAD^ and HEAD~?

Section titled “What’s the difference between HEAD^ and HEAD~?”

HEAD^ is the first parent, HEAD~ is equivalent but allows chaining (HEAD~2). For merge commits, HEAD^2 is the second parent.

Use HEAD@{yesterday} or HEAD@{1.day.ago}. Requires reflog to be enabled.

Use reflog syntax like HEAD@{2023-01-01} or relative dates. For commit dates, use —since/—until with commands.

How do I specify a file at a specific revision?

Section titled “How do I specify a file at a specific revision?”

Use revision:path syntax like HEAD:README.md or v1.0:src/main.c.

What’s the difference between .. and …?

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

A..B shows commits reachable from B but not A. A…B shows commits reachable from A or B but not both (symmetric difference).

How do I find the common ancestor of two branches?

Section titled “How do I find the common ancestor of two branches?”

Use git merge-base branch1 branch2 to find the common ancestor commit.

Can I use wildcards in revision specifications?

Section titled “Can I use wildcards in revision specifications?”

Limited support through reflog and some commands. Generally use explicit refs or SHA-1s.

Use —all ^excluded-branch or —branches ^excluded-branch.

ORIG_HEAD stores the previous HEAD position before operations like merge, reset, or rebase that change HEAD.

Can I reference commits by author or message?

Section titled “Can I reference commits by author or message?”

Use —author, —grep, etc. with commands like git log, but not directly in revision specs.

How do I disambiguate between refs with same name?

Section titled “How do I disambiguate between refs with same name?”

Use full ref paths (refs/heads/main) or ^{type} syntax (main^{commit}).

What’s the safest way to specify revisions in scripts?

Section titled “What’s the safest way to specify revisions in scripts?”

Use git rev-parse to validate and canonicalize revisions before use. Prefer symbolic refs over hardcoded SHA-1s.

Can revision specs include time-based filtering?

Section titled “Can revision specs include time-based filtering?”

Time filtering is done by commands (—since, —until), not in the revision spec itself.

Use the special SHA-1 4b825dc642cb6eb9a060e54bf8d69288fbee4904 for the empty tree.

What’s the difference between lightweight and annotated tags?

Section titled “What’s the difference between lightweight and annotated tags?”

Annotated tags are objects with metadata; use tag^{} to get the commit, tag^{tree} for the tree.

Limited support. Generally use explicit refs or SHA-1s in configuration.

  1. Script Automation: Reliable revision parsing and validation in Git scripts
  2. Complex Range Queries: Advanced commit range specifications for analysis
  3. Reflog Navigation: Time-based reference access using reflog
  4. Cross-Reference Operations: Working with multiple branches and tags
  5. Repository Analysis: Comprehensive repository structure examination
  6. Automated Workflows: Revision-based automation in CI/CD and hooks