refs Git Command Guide
The git for-each-ref command iterates over all refs that match specified patterns and displays them according to custom formats, with powerful sorting and filtering capabilities.
git for-each-ref Syntax:
Section titled “git for-each-ref Syntax:”git for-each-ref [--count=<count>] [--shell|--perl|--python|--tcl] [(--sort=<key>)...] [--format=<format>] [--include-root-refs] [--points-at=<object>] [--merged[=<object>]] [--no-merged[=<object>]] [--contains[=<object>]] [--no-contains[=<object>]] [(--exclude=<pattern>)...] [--start-after=<marker>] [ --stdin | <pattern>... ]Output Control:
Section titled “Output Control:”| Option | Description |
|---|---|
--count=<count> | Limit number of refs shown |
| `—shell | —perl |
--format=<format> | Custom output format |
Sorting Options:
Section titled “Sorting Options:”| Option | Description |
|---|---|
--sort=<key> | Sort by specified key (multiple allowed) |
--start-after=<marker> | Start after specified ref |
Filtering Options:
Section titled “Filtering Options:”| Option | Description |
|---|---|
--points-at=<object> | Show refs pointing to object |
--merged[=<object>] | Show refs merged into object |
--no-merged[=<object>] | Show refs not merged into object |
--contains[=<object>] | Show refs containing object |
--no-contains[=<object>] | Show refs not containing object |
--exclude=<pattern> | Exclude refs matching pattern |
--include-root-refs | Include root refs |
Parameters:
Section titled “Parameters:”| Parameter | Description |
|---|---|
<pattern> | Ref patterns to match (fnmatch or literal) |
--stdin | Read patterns from stdin |
Understanding Reference Iteration:
Section titled “Understanding Reference Iteration:”Reference Types:
Section titled “Reference Types:”Git Reference Hierarchy:├── Branches: refs/heads/ (local branches)├── Remotes: refs/remotes/ (remote tracking branches)├── Tags: refs/tags/ (annotated and lightweight tags)├── Notes: refs/notes/ (note objects)├── Stash: refs/stash (stash entries)└── Special: HEAD, ORIG_HEAD, MERGE_HEAD, etc.
Reference Storage:├── Stored in .git/refs/ directory hierarchy├── Packed refs in .git/packed-refs file├── Symbolic refs point to other refs└── Reflogs track reference changesFormat Specifiers:
Section titled “Format Specifiers:”Common Format Fields:├── %(refname) # Full ref name├── %(refname:short) # Short ref name├── %(objectname) # Object SHA-1├── %(objectname:short) # Short object SHA-1├── %(authorname) # Author name├── %(authoremail) # Author email├── %(authordate) # Author date├── %(committername) # Committer name├── %(committerdate) # Committer date├── %(subject) # Commit subject├── %(body) # Commit body├── %(upstream) # Upstream branch├── %(upstream:short) # Short upstream name└── %(push) # Push destination
Advanced Fields:├── %(HEAD) # * if HEAD, else space├── %(color:<color>) # Color output├── %(align:<width>,<position>) # Text alignment├── %(if:<condition>)%<(then) # Conditional output└── %(symref) # Symbolic ref targetSorting Keys:
Section titled “Sorting Keys:”Available Sort Keys:├── refname # Lexical sort by ref name├── objectname # Sort by object SHA-1├── objectsize # Sort by object size├── authordate # Sort by author date├── commitdate # Sort by commit date├── version:refname # Version sort for tags├── -key # Reverse sort└── Multiple keys: --sort=key1 --sort=key2Basic Reference Operations:
Section titled “Basic Reference Operations:”List All References:
Section titled “List All References:”# List all refs with default formatgit for-each-ref
# List refs with short formatgit for-each-ref --format='%(refname:short)'
# List refs with object infogit for-each-ref --format='%(refname) %(objectname) %(subject)'Filter by Reference Type:
Section titled “Filter by Reference Type:”# List all branchesgit for-each-ref refs/heads/
# List all remote branchesgit for-each-ref refs/remotes/
# List all tagsgit for-each-ref refs/tags/
# List specific remotegit for-each-ref refs/remotes/origin/Custom Formatting:
Section titled “Custom Formatting:”# Branch status formatgit for-each-ref --format='%(HEAD) %(refname:short) %(upstream:short)' refs/heads/
# Tag informationgit for-each-ref --format='%(refname:short) %(authordate) %(subject)' refs/tags/
# Detailed commit infogit for-each-ref --format='%(refname:short): %(authorname) <%(authoremail)> %(committerdate:relative)' refs/heads/Advanced Reference Queries:
Section titled “Advanced Reference Queries:”Sorting and Limiting:
Section titled “Sorting and Limiting:”# Sort by author date (newest first)git for-each-ref --sort=-authordate --format='%(refname:short) %(authordate:relative)' refs/heads/
# Sort by version for tagsgit for-each-ref --sort=version:refname --format='%(refname:short)' refs/tags/
# Limit outputgit for-each-ref --count=10 --sort=-committerdate refs/heads/Filtering by Content:
Section titled “Filtering by Content:”# Branches containing specific commitgit for-each-ref --contains=<commit> --format='%(refname:short)' refs/heads/
# Branches not containing commitgit for-each-ref --no-contains=<commit> --format='%(refname:short)' refs/heads/
# Branches merged into maingit for-each-ref --merged=main --format='%(refname:short)' refs/heads/
# Branches not merged into maingit for-each-ref --no-merged=main --format='%(refname:short)' refs/heads/Pattern Matching:
Section titled “Pattern Matching:”# Match specific patternsgit for-each-ref 'refs/heads/feature/*'
# Exclude patternsgit for-each-ref --exclude='refs/heads/main' --exclude='refs/heads/develop' refs/heads/
# Complex patternsgit for-each-ref 'refs/heads/*' 'refs/tags/v1.*'Points-At Filtering:
Section titled “Points-At Filtering:”# Refs pointing to specific commitgit for-each-ref --points-at=<commit> --format='%(refname)'
# Branches pointing to HEADgit for-each-ref --points-at=HEAD refs/heads/
# Tags pointing to specific objectgit for-each-ref --points-at=<object> refs/tags/Configuration and Best Practices:
Section titled “Configuration and Best Practices:”Git Configuration for for-each-ref:
Section titled “Git Configuration for for-each-ref:”# Configure default formatsgit config pretty.for-each-ref "%(refname:short) %(subject)"
# Configure color outputgit config color.for-each-ref true
# Configure sorting preferencesgit config for-each-ref.sort -committerdateSafe Usage Practices:
Section titled “Safe Usage Practices:”# Always test formatsgit for-each-ref --format='%(refname)' | head -5
# Use count limits for large reposgit for-each-ref --count=50 refs/heads/
# Validate patternsgit for-each-ref 'refs/heads/*' 2>/dev/null || echo "Invalid pattern"
# Handle empty resultsif git for-each-ref --format='%(refname)' refs/heads/ | grep -q .; then echo "Branches found"else echo "No branches found"fiPerformance Optimization:
Section titled “Performance Optimization:”# Use specific patterns to limit scopegit for-each-ref refs/heads/feature/ # Faster than refs/heads/
# Avoid expensive fields in large repos# Use %(refname:short) instead of %(subject) for speed
# Cache results when possibleREFS_CACHE=$(git for-each-ref --format='%(refname)' refs/heads/)Integration with Development Workflows:
Section titled “Integration with Development Workflows:”Branch Management Dashboard:
Section titled “Branch Management Dashboard:”#!/bin/bash# Branch management dashboard
branch_dashboard() { echo "=== Branch Dashboard ===" echo ""
# Active branches with status echo "Active Branches:" git for-each-ref --sort=-committerdate --format='%(HEAD)%(color:green)%(refname:short)%(color:reset) %(color:blue)%(upstream:short)%(color:reset) %(authordate:relative) %(subject)' refs/heads/ | head -10 echo ""
# Stale branches echo "Potentially Stale Branches (>90 days):" git for-each-ref --sort=-committerdate --format='%(refname:short) %(committerdate:relative)' refs/heads/ | awk '$2 ~ /weeks?|months?|years?/ {print}' | head -5 echo ""
# Branch statistics total_branches=$(git for-each-ref refs/heads/ | wc -l) merged_branches=$(git for-each-ref --merged=HEAD refs/heads/ | grep -v HEAD | wc -l) echo "Statistics: $total_branches total branches, $merged_branches merged into HEAD"}
branch_dashboardTag Management:
Section titled “Tag Management:”# Tag management with for-each-reftag_management() { echo "=== Tag Management ==="
# Recent tags echo "Recent Tags:" git for-each-ref --sort=-version:refname --format='%(refname:short) %(authordate:relative) %(subject)' refs/tags/ | head -10 echo ""
# Annotated vs lightweight echo "Tag Types:" echo "Annotated tags:" git for-each-ref --format='%(refname:short)' refs/tags/ | while read -r tag; do if git cat-file -t "$tag" 2>/dev/null | grep -q tag; then echo " $tag" fi done
echo "Lightweight tags:" git for-each-ref --format='%(refname:short)' refs/tags/ | while read -r tag; do if ! git cat-file -t "$tag" 2>/dev/null | grep -q tag; then echo " $tag" fi done}
tag_managementRepository Health Check:
Section titled “Repository Health Check:”# Repository health check using refshealth_check() { echo "=== Repository Health Check ==="
# Check for broken refs echo "Checking for broken refs..." git for-each-ref --format='%(refname)' | while read -r ref; do if ! git rev-parse --verify "$ref" >/dev/null 2>&1; then echo "Broken ref: $ref" fi done
# Check reflog sizes echo "Reflog sizes:" git for-each-ref --format='%(refname)' | while read -r ref; do reflog_size=$(git reflog --format='%H' "$ref" 2>/dev/null | wc -l) if [ "$reflog_size" -gt 1000 ]; then echo "Large reflog: $ref ($reflog_size entries)" fi done
# Check for old branches echo "Old branches (>6 months):" git for-each-ref --sort=-committerdate --format='%(refname:short) %(committerdate:relative)' refs/heads/ | awk '$2 ~ /months?|years?/ {print}' | head -5
echo "Health check complete"}
health_checkTroubleshooting Common Issues:
Section titled “Troubleshooting Common Issues:”Format String Issues:
Section titled “Format String Issues:”# Debug format stringsgit for-each-ref --format='%(refname)' 2>&1 | head -5
# Escape special charactersgit for-each-ref --format='Branch: %(refname:short)' refs/heads/
# Handle missing fieldsgit for-each-ref --format='%(refname) %(upstream:-none)' refs/heads/
# Test conditional formattinggit for-each-ref --format='%(if)%(HEAD)%(then)* %(end)%(refname:short)' refs/heads/Pattern Matching Problems:
Section titled “Pattern Matching Problems:”# Debug pattern matchinggit for-each-ref 'refs/heads/*' | wc -l
# Use proper escapinggit for-each-ref 'refs/heads/feature/*'
# Check pattern syntaxgit for-each-ref --format='%(refname)' 'refs/heads/main' 2>/dev/null || echo "Pattern failed"Sorting Issues:
Section titled “Sorting Issues:”# Fix sorting problemsgit for-each-ref --sort=committerdate --format='%(refname)' refs/heads/
# Multiple sort keysgit for-each-ref --sort=authordate --sort=refname refs/heads/
# Reverse sortinggit for-each-ref --sort=-committerdate refs/heads/Performance Issues:
Section titled “Performance Issues:”# Optimize for large repositoriesgit for-each-ref --count=100 --sort=-committerdate refs/heads/
# Use specific patternsgit for-each-ref refs/heads/main refs/heads/develop # Faster than refs/heads/
# Avoid expensive operations# Use %(refname:short) instead of %(subject) for large reposFiltering Problems:
Section titled “Filtering Problems:”# Debug filteringgit for-each-ref --contains=HEAD --format='%(refname)' refs/heads/
# Check object existenceif git cat-file -t <object> >/dev/null 2>&1; then git for-each-ref --contains=<object> refs/heads/else echo "Object does not exist"fi
# Verify merge statusgit for-each-ref --merged=HEAD --format='%(refname)' refs/heads/Output Encoding Issues:
Section titled “Output Encoding Issues:”# Handle encoding problemsgit for-each-ref --format='%(refname)' | iconv -f utf-8 -t utf-8
# Use shell quoting for special charactersgit for-each-ref --shell --format='%(refname)' refs/heads/
# Handle non-ASCII charactersexport LC_ALL=C.UTF-8git for-each-ref --format='%(refname)' refs/heads/Real-World Usage Examples:
Section titled “Real-World Usage Examples:”Git Branch Cleanup Script:
Section titled “Git Branch Cleanup Script:”#!/bin/bash# Automated branch cleanup
cleanup_branches() { echo "=== Branch Cleanup Report ==="
# Find merged branches echo "Branches merged into main:" git for-each-ref --merged=main --format='%(refname:short)' refs/heads/ | grep -v main | while read -r branch; do echo " $branch (merged)" done
# Find old branches echo "Branches not updated in 6+ months:" six_months_ago=$(date -d '6 months ago' +%s) git for-each-ref --sort=-committerdate --format='%(refname:short) %(committerdate:unix)' refs/heads/ | while read -r branch date; do if [ "$date" -lt "$six_months_ago" ] && [ "$branch" != "main" ]; then echo " $branch (last updated $(date -d "@$date" '+%Y-%m-%d'))" fi done
# Interactive cleanup echo "" echo "Options:" echo "1. Delete merged branches" echo "2. Delete old branches" echo "3. Show detailed info" read -p "Choose action (1-3): " action
case "$action" in 1) git for-each-ref --merged=main --format='%(refname:short)' refs/heads/ | grep -v main | xargs -n1 git branch -d ;; 2) # Would need more sophisticated logic for safety echo "Manual review recommended for old branches" ;; 3) git for-each-ref --sort=-committerdate --format='%(refname:short) %(committerdate:relative) %(authorname)' refs/heads/ ;; esac}
cleanup_branchesRelease Branch Management:
Section titled “Release Branch Management:”# Release branch managementrelease_management() { echo "=== Release Branch Management ==="
# Current release branches echo "Active Release Branches:" git for-each-ref --sort=-version:refname --format='%(refname:short) %(authordate:relative)' 'refs/heads/release/*' | head -5
# Release tags echo "Release Tags:" git for-each-ref --sort=-version:refname --format='%(refname:short) %(authordate:relative) %(subject)' 'refs/tags/v*' | head -10
# Branches ready for release echo "Branches Ready for Release (merged into main):" git for-each-ref --merged=main --format='%(refname:short)' refs/heads/ | grep -E '(release|hotfix)' | while read -r branch; do # Check if already tagged branch_version=$(echo "$branch" | sed 's/.*release[-/]//;s/.*hotfix[-/]//') if ! git for-each-ref --format='%(refname)' "refs/tags/v$branch_version" | grep -q .; then echo " $branch -> ready for v$branch_version" fi done
# Release candidates echo "Release Candidates:" git for-each-ref --format='%(refname:short)' 'refs/heads/*rc*' | while read -r rc_branch; do commits=$(git rev-list --count main.."$rc_branch") echo " $rc_branch ($commits commits since main)" done}
release_managementRepository Statistics:
Section titled “Repository Statistics:”# Generate repository statisticsrepo_stats() { echo "=== Repository Statistics ==="
# Branch statistics total_branches=$(git for-each-ref refs/heads/ | wc -l) active_branches=$(git for-each-ref --sort=-committerdate --format='%(committerdate:unix)' refs/heads/ | head -20 | wc -l) echo "Branches: $total_branches total, $active_branches recently active"
# Tag statistics total_tags=$(git for-each-ref refs/tags/ | wc -l) recent_tags=$(git for-each-ref --sort=-authordate --format='%(authordate:unix)' refs/tags/ | awk 'BEGIN{count=0; six_months_ago=systime() - (6*30*24*3600)} $1 > six_months_ago {count++} END{print count}') echo "Tags: $total_tags total, $recent_tags in last 6 months"
# Remote statistics total_remotes=$(git for-each-ref refs/remotes/ | wc -l) remote_names=$(git for-each-ref --format='%(refname)' refs/remotes/ | sed 's|refs/remotes/||' | cut -d/ -f1 | sort | uniq | wc -l) echo "Remote branches: $total_branches across $remote_names remotes"
# Author statistics echo "Top Contributors (by commits):" git for-each-ref --format='%(authorname)' refs/heads/ | sort | uniq -c | sort -nr | head -5 | while read -r count author; do echo " $author: $count branches" done
# Branch age distribution echo "Branch Age Distribution:" git for-each-ref --format='%(committerdate:unix)' refs/heads/ | awk ' BEGIN { now = systime() print "Age ranges:" } { age_days = int((now - $1) / 86400) if (age_days <= 7) young++ else if (age_days <= 30) month++ else if (age_days <= 90) quarter++ else if (age_days <= 365) year++ else old++ } END { print " 0-7 days: " young print " 8-30 days: " month print " 31-90 days: " quarter print " 91-365 days: " year print " 1+ years: " old }'}
repo_statsCI/CD Pipeline Integration:
Section titled “CI/CD Pipeline Integration:”# CI/CD pipeline branch validationci_branch_validation() { local required_branches="main develop" local max_branch_age_days=90
echo "=== CI/CD Branch Validation ==="
# Check required branches exist for branch in $required_branches; do if git for-each-ref --format='%(refname)' "refs/heads/$branch" | grep -q .; then echo "✓ Required branch exists: $branch" else echo "✗ Missing required branch: $branch" return 1 fi done
# Check branch freshness stale_branches=$(git for-each-ref --format='%(refname:short) %(committerdate:unix)' refs/heads/ | awk -v max_age="$max_branch_age_days" ' BEGIN { now = systime() max_age_seconds = max_age * 24 * 3600 } { branch = $1 age_seconds = now - $2 if (age_seconds > max_age_seconds) { print branch } }')
if [ -n "$stale_branches" ]; then echo "⚠ Stale branches (>${max_branch_age_days} days):" echo "$stale_branches" else echo "✓ All branches are fresh" fi
# Check for feature branches that should be merged old_feature_branches=$(git for-each-ref --format='%(refname:short) %(committerdate:unix)' 'refs/heads/feature/*' | awk ' BEGIN { now = systime() month_ago = now - (30 * 24 * 3600) } $2 < month_ago { print $1 }')
if [ -n "$old_feature_branches" ]; then echo "⚠ Old feature branches (>30 days):" echo "$old_feature_branches" fi
echo "Branch validation complete"}
ci_branch_validationCustom Git Commands:
Section titled “Custom Git Commands:”# Custom git commands using for-each-refgit-branch-age() { # Show branch ages git for-each-ref --sort=-committerdate --format='%(refname:short) %(committerdate:relative)' refs/heads/}
git-tag-history() { # Show tag creation history git for-each-ref --sort=-authordate --format='%(refname:short) %(authordate) %(authorname)' refs/tags/}
git-merged-branches() { # Show branches merged into current git for-each-ref --merged=HEAD --format='%(refname:short)' refs/heads/ | grep -v "$(git rev-parse --abbrev-ref HEAD)"}
git-stale-branches() { # Show stale branches local days="${1:-30}" local cutoff_date=$(date -d "$days days ago" +%s)
git for-each-ref --format='%(refname:short) %(committerdate:unix)' refs/heads/ | while read -r branch date; do if [ "$date" -lt "$cutoff_date" ]; then echo "$branch (last updated $(date -d "@$date" '+%Y-%m-%d'))" fi done}
# Usage examplesgit branch-age | head -10git tag-history | head -5git merged-branchesgit stale-branches 60Repository Mirroring Validation:
Section titled “Repository Mirroring Validation:”# Validate repository mirroringvalidate_mirroring() { local primary_repo="$1" local mirror_repo="$2"
echo "=== Repository Mirroring Validation ==="
# Compare ref counts primary_refs=$(GIT_DIR="$primary_repo" git for-each-ref --format='%(refname)' | wc -l) mirror_refs=$(GIT_DIR="$mirror_repo" git for-each-ref --format='%(refname)' | wc -l)
echo "Primary refs: $primary_refs" echo "Mirror refs: $mirror_refs"
if [ "$primary_refs" -ne "$mirror_refs" ]; then echo "⚠ Reference count mismatch"
# Find missing refs comm -23 <(GIT_DIR="$primary_repo" git for-each-ref --format='%(refname)' | sort) \ <(GIT_DIR="$mirror_repo" git for-each-ref --format='%(refname)' | sort) | while read -r ref; do echo "Missing in mirror: $ref" done else echo "✓ Reference counts match" fi
# Compare recent commits primary_recent=$(GIT_DIR="$primary_repo" git for-each-ref --sort=-committerdate --format='%(refname) %(objectname)' refs/heads/ | head -5) mirror_recent=$(GIT_DIR="$mirror_repo" git for-each-ref --sort=-committerdate --format='%(refname) %(objectname)' refs/heads/ | head -5)
if [ "$primary_recent" != "$mirror_recent" ]; then echo "⚠ Recent commits differ" else echo "✓ Recent commits match" fi
echo "Mirroring validation complete"}
validate_mirroring "/path/to/primary.git" "/path/to/mirror.git"What’s the difference between for-each-ref and branch -a?
Section titled “What’s the difference between for-each-ref and branch -a?”for-each-ref provides customizable formatting and advanced filtering; branch -a just lists branch names. for-each-ref can show any ref type with rich metadata.
How do I list all branches with their last commit dates?
Section titled “How do I list all branches with their last commit dates?”Use git for-each-ref —sort=-committerdate —format=’%(refname:short) %(committerdate:relative)’ refs/heads/
Can for-each-ref show remote branches?
Section titled “Can for-each-ref show remote branches?”Yes, use refs/remotes/ pattern: git for-each-ref refs/remotes/
How do I format output for scripting?
Section titled “How do I format output for scripting?”Use —format with field specifiers and —shell for shell-quoted output.
What’s the difference between %(refname) and %(refname:short)?
Section titled “What’s the difference between %(refname) and %(refname:short)?”%(refname) shows full path like refs/heads/main; %(refname:short) shows just main.
How do I sort refs by version number?
Section titled “How do I sort refs by version number?”Use —sort=version:refname for semantic version sorting.
Can for-each-ref filter by commit content?
Section titled “Can for-each-ref filter by commit content?”Yes, use —contains=
How do I limit output to recent refs?
Section titled “How do I limit output to recent refs?”Use —count=
What’s %(HEAD) for?
Section titled “What’s %(HEAD) for?”%(HEAD) shows * for the current HEAD branch, space for others.
Can for-each-ref work with tags?
Section titled “Can for-each-ref work with tags?”Yes, use refs/tags/ pattern for tag references.
How do I exclude certain refs?
Section titled “How do I exclude certain refs?”Use —exclude=
What’s the performance impact of for-each-ref?
Section titled “What’s the performance impact of for-each-ref?”Generally fast, but complex formats on large repos can be slow. Use —count and specific patterns to optimize.
Can for-each-ref show upstream information?
Section titled “Can for-each-ref show upstream information?”Yes, use %(upstream) and %(upstream:short) for tracking branch info.
How do I find branches that contain a specific commit?
Section titled “How do I find branches that contain a specific commit?”Use git for-each-ref —contains=
Can for-each-ref show reflog information?
Section titled “Can for-each-ref show reflog information?”No, for-each-ref shows current ref state. Use git reflog for historical information.
How do I get refs from stdin?
Section titled “How do I get refs from stdin?”Use —stdin option to read patterns from standard input.
What’s —include-root-refs for?
Section titled “What’s —include-root-refs for?”—include-root-refs includes refs that are direct children of refs/ (rarely needed).
Can for-each-ref show object sizes?
Section titled “Can for-each-ref show object sizes?”Yes, use %(objectsize) in format string.
How do I find the oldest branches?
Section titled “How do I find the oldest branches?”Use —sort=committerdate (ascending) to show oldest first.
Can for-each-ref work with packed refs?
Section titled “Can for-each-ref work with packed refs?”Yes, it works with both loose and packed refs transparently.
Applications of the git for-each-ref command
Section titled “Applications of the git for-each-ref command”- Repository Analysis: Comprehensive ref inspection and statistics
- Branch Management: Automated branch cleanup and status reporting
- CI/CD Integration: Branch validation and automated workflows
- Custom Reporting: Flexible ref information display and formatting
- Repository Maintenance: Health checks and cleanup operations
- Script Integration: Programmatic access to ref information