Skip to content

name-rev Git Command Guide

The git name-rev command finds symbolic names suitable for human digestion for revisions given in any format parsable by git rev-parse. It helps make commit references more meaningful by associating them with branch names, tags, or relative positions.

Terminal window
git name-rev [--tags] [--refs=<pattern>] [--exclude=<pattern>]
[--all] [--annotate-stdin] [<commit-ish>...]
OptionDescription
--tagsOnly use tags to name commits (no branches)
--refs=<pattern>Only use refs matching shell pattern
--exclude=<pattern>Exclude refs matching pattern
--no-refsClear previous ref patterns
OptionDescription
--allList all commits with their names
--annotate-stdinRead commits from stdin, write to stdout
ParameterDescription
<commit-ish>...Commits to find names for
Branch names: master, develop, feature-x
Tag names: v1.0.0, v2.1.0
Relative refs: master~5, develop^2
Undefined: <commit-hash> (undefined)
1. Find closest named reference (branch/tag)
2. Calculate relative distance
3. Generate human-readable name
4. Format: ref~distance or ref^distance
Terminal window
# Find name for specific commit
git name-rev abc123def456
# Output: abc123def456 master~5
# Find name for HEAD
git name-rev HEAD
# Output: HEAD master
Terminal window
# Name several commits
git name-rev HEAD~3 HEAD~5 HEAD~10
# Output:
# HEAD~3 master~3
# HEAD~5 master~5
# HEAD~10 master~10
Terminal window
# Use only tags for naming
git name-rev --tags abc123def456
# Output: abc123def456 v1.0.0~5
Terminal window
# Use only release branches
git name-rev --refs="release/*" abc123def456
# Use only feature branches
git name-rev --refs="feature/*" abc123def456
# Exclude certain refs
git name-rev --exclude="temp/*" abc123def456
Terminal window
# Annotate all commits in range
git log --oneline main..feature | \
while read hash msg; do
name=$(git name-rev --name-only "$hash")
echo "$hash $name: $msg"
done
# Process commit stream
git rev-list --all | head -20 | \
xargs git name-rev | \
while read hash name; do
echo "Commit $hash is known as $name"
done
Terminal window
# Annotate commits from stdin
echo "abc123def456" | git name-rev --annotate-stdin
# Process multiple commits
cat commit-list.txt | git name-rev --annotate-stdin
# Combine with other tools
git rev-parse HEAD~10..HEAD | git name-rev --annotate-stdin
Terminal window
# Analyze all commits with names
git name-rev --all | head -20
# Find commits without names
git name-rev --all | grep "undefined" | head -10
# Count named vs unnamed commits
total=$(git rev-list --all | wc -l)
named=$(git name-rev --all | grep -v "undefined" | wc -l)
unnamed=$((total - named))
echo "Total commits: $total"
echo "Named commits: $named"
echo "Unnamed commits: $unnamed"
Terminal window
# Compare branch relationships
analyze_branches() {
local branch1="$1"
local branch2="$2"
echo "=== Branch Analysis: $branch1 vs $branch2 ==="
# Find common ancestor
base=$(git merge-base "$branch1" "$branch2")
base_name=$(git name-rev --name-only "$base")
echo "Common ancestor: $base ($base_name)"
# Show branch tips
tip1=$(git rev-parse "$branch1")
tip1_name=$(git name-rev --name-only "$tip1")
tip2=$(git rev-parse "$branch2")
tip2_name=$(git name-rev --name-only "$tip2")
echo "$branch1 tip: $tip1 ($tip1_name)"
echo "$branch2 tip: $tip2 ($tip2_name)"
# Show divergence
ahead1=$(git rev-list --count "$base..$branch1")
ahead2=$(git rev-list --count "$base..$branch2")
echo "$branch1 ahead by: $ahead1 commits"
echo "$branch2 ahead by: $ahead2 commits"
}
analyze_branches "main" "develop"
#!/bin/bash
# Enhanced commit log with names
enhanced_log() {
local count="${1:-10}"
git log --oneline -"$count" | \
while read hash msg; do
name=$(git name-rev --name-only "$hash")
printf "%s %-15s %s\n" "$hash" "($name)" "$msg"
done
}
# Usage
enhanced_log 20
Terminal window
# Track commits relative to releases
track_releases() {
echo "=== Release Tracking ==="
# Get all tags
git tag --sort=-version:refname | head -5 | \
while read tag; do
hash=$(git rev-parse "$tag")
echo "Tag: $tag ($hash)"
# Show recent commits relative to this tag
git log --oneline "$tag~5..$tag" | \
while read commit_hash msg; do
name=$(git name-rev --name-only "$commit_hash")
echo " $commit_hash ($name): $msg"
done
echo ""
done
}
track_releases
Terminal window
# Use in CI for better commit identification
ci_commit_info() {
local commit_hash="$1"
echo "=== CI Commit Information ==="
echo "Commit: $commit_hash"
# Get symbolic name
symbolic_name=$(git name-rev --name-only "$commit_hash")
echo "Symbolic name: $symbolic_name"
# Determine branch/tag context
if [[ "$symbolic_name" == *"tags/"* ]]; then
echo "Context: Tag release"
tag_name=$(echo "$symbolic_name" | sed 's/.*tags\///')
echo "Tag: $tag_name"
elif [[ "$symbolic_name" == *"remotes/"* ]]; then
echo "Context: Remote branch"
branch=$(echo "$symbolic_name" | sed 's/.*remotes\///')
echo "Remote branch: $branch"
else
echo "Context: Local branch"
echo "Branch: $symbolic_name"
fi
# Get commit details
author=$(git show --no-patch --format="%an <%ae>" "$commit_hash")
date=$(git show --no-patch --format="%ad" "$commit_hash")
echo "Author: $author"
echo "Date: $date"
}
# Usage in CI
ci_commit_info "$CI_COMMIT_SHA"
Terminal window
# Monitor repository naming health
monitor_naming_health() {
echo "=== Repository Naming Health ==="
# Check for unnamed commits
unnamed_count=$(git name-rev --all | grep -c "undefined")
total_count=$(git rev-list --all | wc -l)
echo "Total commits: $total_count"
echo "Unnamed commits: $unnamed_count"
if [ "$unnamed_count" -gt 0 ]; then
percentage=$((unnamed_count * 100 / total_count))
echo "Unnamed percentage: $percentage%"
if [ "$percentage" -gt 50 ]; then
echo "⚠ Warning: High percentage of unnamed commits"
echo "Consider creating more branches/tags for better traceability"
fi
fi
# Check branch/tag coverage
branch_count=$(git branch -r | wc -l)
tag_count=$(git tag | wc -l)
echo "Remote branches: $branch_count"
echo "Tags: $tag_count"
# Show most recent unnamed commits
if [ "$unnamed_count" -gt 0 ]; then
echo ""
echo "Recent unnamed commits:"
git log --oneline --all | head -20 | \
while read hash msg; do
name=$(git name-rev --name-only "$hash")
if [[ "$name" == *"undefined"* ]]; then
echo " $hash: $msg"
fi
done
fi
}
monitor_naming_health
Terminal window
# Configure reference patterns
git config name-rev.refs "refs/heads/*" # Only branches
git config name-rev.refs "refs/tags/*" # Only tags
git config name-rev.refs "refs/remotes/*" # Remote branches
# Multiple patterns
git config --add name-rev.refs "refs/heads/main"
git config --add name-rev.refs "refs/tags/v*"
# Exclude patterns
git config name-rev.exclude "refs/heads/temp/*"
git config name-rev.exclude "refs/heads/old/*"
Terminal window
# Optimize for large repositories
git config name-rev.compute "auto" # Auto-compute names
git config name-rev.compute "always" # Always compute
git config name-rev.compute "never" # Never compute
# Cache name resolutions
export GIT_NAME_REV_CACHE="${HOME}/.git-name-rev-cache"
# Parallel processing for large repos
name_rev_parallel() {
local commits_file="$1"
# Process in parallel batches
split -l 100 "$commits_file" batch-
for batch in batch-*; do
git name-rev --annotate-stdin < "$batch" &
done
wait
# Combine results
cat batch-*
rm batch-*
}
# Usage
git rev-list --all > all-commits.txt
name_rev_parallel all-commits.txt
Terminal window
# Check why commit has no name
debug_undefined_commit() {
local commit="$1"
echo "Debugging undefined commit: $commit"
# Check if commit exists
if ! git cat-file -t "$commit" >/dev/null 2>&1; then
echo "Error: Commit does not exist"
return 1
fi
# Check available refs
echo "Available branches:"
git branch --contains "$commit"
echo "Available tags:"
git tag --contains "$commit"
# Check if commit is reachable
if git merge-base --is-ancestor "$commit" HEAD 2>/dev/null; then
echo "Commit is reachable from HEAD"
else
echo "Commit is not reachable from HEAD"
fi
# Manual name resolution
echo "Manual resolution:"
# Find closest branch
git branch -r --contains "$commit" | head -1
# Find closest tag
git describe --tags --contains "$commit" 2>/dev/null || echo "No containing tag"
}
debug_undefined_commit "abc123def456"
Terminal window
# Profile name resolution
time git name-rev HEAD~1000
# Check repository size impact
echo "Repository refs: $(find .git/refs -type f | wc -l)"
echo "Packed refs: $(wc -l .git/packed-refs 2>/dev/null || echo 0)"
# Optimize ref storage
git pack-refs --all
git for-each-ref --format="%(refname)" | git name-rev --stdin
Terminal window
# Debug pattern matching
test_patterns() {
local commit="$1"
echo "Testing patterns for commit: $commit"
# Test different patterns
for pattern in "refs/heads/*" "refs/tags/*" "refs/remotes/*"; do
result=$(git name-rev --refs="$pattern" "$commit" 2>/dev/null | grep -v "^$commit")
if [ -n "$result" ]; then
echo "Pattern $pattern: $result"
else
echo "Pattern $pattern: no match"
fi
done
}
test_patterns "abc123def456"
Terminal window
# Debug stdin processing
debug_stdin_processing() {
echo "Testing stdin processing..."
# Test with sample input
echo -e "HEAD\nHEAD~5\nabc123def456" | \
while read commit; do
echo "Processing: $commit"
name=$(git name-rev --name-only "$commit" 2>/dev/null || echo "error")
echo "Result: $name"
done
}
debug_stdin_processing
#!/bin/bash
# Analyze historical commits with names
commit_archaeology() {
local target_commit="$1"
echo "=== Commit Archaeology: $target_commit ==="
# Get symbolic name
symbolic_name=$(git name-rev --name-only "$target_commit")
echo "Symbolic name: $symbolic_name"
# Find related branches
echo "Related branches:"
git branch -r --contains "$target_commit" | sed 's/^/ /'
# Find related tags
echo "Related tags:"
git tag --contains "$target_commit" | sed 's/^/ /'
# Show commit context
echo "Commit context:"
git show --no-patch --format="Author: %an <%ae>%nDate: %ad%nSubject: %s" "$target_commit"
# Show position in branches
echo "Position in branches:"
git branch -r | while read branch; do
if git merge-base --is-ancestor "$target_commit" "$branch" 2>/dev/null; then
distance=$(git rev-list --count "$target_commit..$branch")
echo " $branch: $distance commits ahead"
fi
done
}
# Usage
commit_archaeology "abc123def456"
Terminal window
# Create visual representation of repository
visualize_repository() {
echo "=== Repository Visualization ==="
# Get recent commits with names
git log --oneline -20 | \
while read hash msg; do
name=$(git name-rev --name-only "$hash")
# Create simple ASCII visualization
if [[ "$name" == *"master"* ]] || [[ "$name" == *"main"* ]]; then
marker="*"
elif [[ "$name" == *"tags/"* ]]; then
marker="T"
else
marker="o"
fi
printf "%s %s %-15s %s\n" "$marker" "$hash" "($name)" "$msg"
done
echo ""
echo "Legend: * main/master, T tag, o other branch"
}
visualize_repository
Terminal window
# Generate release notes with commit names
generate_release_notes() {
local from_tag="$1"
local to_tag="${2:-HEAD}"
echo "=== Release Notes: $from_tag to $to_tag ==="
# Get commits between tags
git log --oneline "$from_tag..$to_tag" | \
while read hash msg; do
name=$(git name-rev --name-only "$hash")
category=$(categorize_commit "$msg")
echo " $category: $msg ($name)"
done | \
sort | \
awk -F: '
{ category[$1] = category[$1] $2 ": " $3 "\n" }
END {
for (cat in category) {
print "## " cat
print category[cat]
}
}
'
}
categorize_commit() {
local msg="$1"
if [[ "$msg" =~ ^feat: ]]; then
echo "Features"
elif [[ "$msg" =~ ^fix: ]]; then
echo "Bug Fixes"
elif [[ "$msg" =~ ^docs: ]]; then
echo "Documentation"
elif [[ "$msg" =~ ^refactor: ]]; then
echo "Refactoring"
else
echo "Other"
fi
}
# Usage
generate_release_notes "v1.0.0" "v2.0.0"

What’s the difference between git name-rev and git describe?

Section titled “What’s the difference between git name-rev and git describe?”

git name-rev finds symbolic names for any commit; git describe finds the most recent tag reachable from a commit, with distance information.

How do I find the symbolic name for a specific commit?

Section titled “How do I find the symbolic name for a specific commit?”

Use git name-rev to get the symbolic name, or git name-rev —name-only for just the name part.

Can name-rev work with abbreviated hashes?

Section titled “Can name-rev work with abbreviated hashes?”

Yes, git name-rev accepts any commit reference that git rev-parse can understand, including abbreviated hashes.

Limits name resolution to tags only, ignoring branches. Useful when you want tag-based naming instead of branch-based.

Pipe git log output to git name-rev —annotate-stdin, or use git log with custom formatting that includes symbolic names.

Can name-rev resolve names for merge commits?

Section titled “Can name-rev resolve names for merge commits?”

Yes, merge commits get names based on their relationship to named refs, showing which branch they belong to.

What’s the performance impact of name-rev —all?

Section titled “What’s the performance impact of name-rev —all?”

Can be slow on large repositories as it processes all commits. Use selectively or combine with other filtering options.

Use —name-only for clean output, check exit codes, and handle “undefined” results for commits without symbolic names.

Yes, includes remote branches in name resolution. Use —refs=“refs/remotes/*” to limit to remote refs only.

What’s the relationship between name-rev and git symbolic-ref?

Section titled “What’s the relationship between name-rev and git symbolic-ref?”

git symbolic-ref shows the target of symbolic refs; git name-rev finds human-readable names for commit objects.

How do I customize name-rev reference patterns?

Section titled “How do I customize name-rev reference patterns?”

Use —refs and —exclude options to control which references are considered for name resolution.

Yes, operates on refs and commit objects regardless of working directory presence.

What’s the difference between name-rev and branch —contains?

Section titled “What’s the difference between name-rev and branch —contains?”

git name-rev finds the closest named reference; git branch —contains shows all branches containing a commit.

How do I troubleshoot “undefined” name-rev results?

Section titled “How do I troubleshoot “undefined” name-rev results?”

Check if commit is reachable from named refs, verify repository has branches/tags, and ensure refs are properly packed.

  1. Commit Reference Enhancement: Make commit hashes more human-readable by associating them with branch/tag names
  2. Repository Analysis: Analyze commit relationships and branch structures with meaningful names
  3. Debugging Support: Aid in debugging by providing context for commit references
  4. Historical Research: Study repository evolution with named commit references
  5. CI/CD Integration: Provide meaningful commit identification in automated pipelines
  6. Documentation Generation: Create human-readable commit references for documentation and reports