Skip to content

pack-refs Git Command Guide

The git pack-refs command packs heads and tags for efficient repository access by storing refs in a single packed-refs file instead of individual files. This optimizes storage and performance for repositories with many refs.

Terminal window
git pack-refs [--all] [--no-prune] [--auto] [--include <pattern>] [--exclude <pattern>]
OptionDescription
--allPack all refs (default behavior)
--no-pruneDon’t remove packed ref files
--autoOnly pack if beneficial
OptionDescription
--include <pattern>Include refs matching pattern
--exclude <pattern>Exclude refs matching pattern
Traditional (one file per ref):
.git/refs/
├── heads/
│ ├── main
│ ├── develop
│ └── feature-x
└── tags/
├── v1.0.0
├── v1.1.0
└── v2.0.0
Packed (.git/packed-refs file):
# pack-refs with: peeled fully-peeled sorted
a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s refs/heads/main
b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s refs/heads/develop
...
# Comments start with #
# pack-refs with: peeled fully-peeled sorted
<SHA-1> <ref-name>
<SHA-1> <ref-name>
...
# Annotated tags include peeled object
<SHA-1> refs/tags/v1.0.0
^<peeled-SHA-1>
Terminal window
# Pack all refs
git pack-refs --all
# Pack refs automatically (if beneficial)
git pack-refs --auto
# Pack without removing loose refs
git pack-refs --all --no-prune
Terminal window
# Pack only tags
git pack-refs --include "refs/tags/*"
# Pack branches but exclude certain ones
git pack-refs --include "refs/heads/*" --exclude "refs/heads/temp/*"
# Pack specific ref patterns
git pack-refs --include "refs/remotes/origin/*"
Terminal window
# Check current ref storage
ls -la .git/refs/heads/
ls -la .git/packed-refs
# Pack and clean up
git pack-refs --all
git for-each-ref # Verify refs still work
# Unpack specific refs (make loose again)
git update-ref refs/heads/main $(git rev-parse refs/heads/main)
#!/bin/bash
# Complete repository ref optimization
optimize_refs() {
echo "Optimizing repository refs..."
# Analyze current ref storage
echo "Loose refs: $(find .git/refs -type f | wc -l)"
echo "Packed refs: $(wc -l .git/packed-refs 2>/dev/null || echo 0)"
# Pack all refs
git pack-refs --all
# Clean up old loose refs (safe after packing)
git pack-refs --all # Pack again to ensure all are packed
# Verify all refs are accessible
git for-each-ref --format="%(refname)" | head -10
echo "Ref optimization complete"
}
optimize_refs
Terminal window
# Pack only stable refs (tags and main branches)
pack_stable_refs() {
echo "Packing stable refs..."
# Pack tags
git pack-refs --include "refs/tags/*"
# Pack main branches
git pack-refs --include "refs/heads/main" \
--include "refs/heads/master" \
--include "refs/heads/develop"
# Keep feature branches loose for frequent updates
echo "Feature branches remain loose for active development"
}
pack_stable_refs
Terminal window
# Optimize remote tracking branches
optimize_remotes() {
local remote_name="$1"
echo "Optimizing remote: $remote_name"
# Pack remote refs
git pack-refs --include "refs/remotes/$remote_name/*"
# Update remote configuration
git config remote."$remote_name".prune true
# Prune stale remote refs
git remote prune "$remote_name"
echo "Remote optimization complete"
}
optimize_remotes "origin"
Terminal window
# Configure automatic packing
git config core.packedRefs true # Enable packed refs
git config pack.refs true # Enable ref packing
git config pack.writeBitmapIndex true # Write bitmap indexes
# Configure maintenance
git config maintenance.gc.enabled true
git config maintenance.prefetch.enabled true
git config maintenance.commit-graph.enabled true
Terminal window
# Monitor ref storage efficiency
analyze_ref_storage() {
echo "=== Ref Storage Analysis ==="
# Count different ref types
echo "Loose refs by type:"
find .git/refs -type f | sed 's|.git/refs/||' | cut -d/ -f1 | sort | uniq -c
# Packed refs statistics
if [ -f .git/packed-refs ]; then
echo "Packed refs: $(grep -c '^[^#]' .git/packed-refs)"
echo "Peeled tags: $(grep -c '^\^' .git/packed-refs)"
fi
# Storage comparison
loose_size=$(find .git/refs -type f -exec cat {} \; | wc -c)
packed_size=$(stat -f%z .git/packed-refs 2>/dev/null || echo 0)
echo "Loose storage: $loose_size bytes"
echo "Packed storage: $packed_size bytes"
echo "Efficiency: $(($packed_size * 100 / ($loose_size + $packed_size)))%"
}
analyze_ref_storage
Terminal window
# Optimize for large repositories
git config core.deltaBaseCacheLimit 512m # Delta cache
git config core.looseCompression 1 # Loose object compression
git config transfer.fsckObjects false # Skip fsck for speed
# Configure ref backend
git config core.refBackend files # Default ref backend
# Future: git config core.refBackend reftable # New reftable backend
#!/bin/bash
# Automated ref maintenance
ref_maintenance() {
echo "Performing ref maintenance..."
# Pack refs if beneficial
git pack-refs --auto
# Update server info for dumb protocols
git update-server-info
# Clean up unreachable objects
git gc --auto
# Verify repository health
git fsck --connectivity-only --no-progress
echo "Ref maintenance complete"
}
# Run daily
ref_maintenance
Terminal window
# Optimize refs in CI for better performance
ci_ref_optimization() {
echo "Optimizing refs for CI..."
# Shallow clone optimization
git config remote.origin.partialCloneFilter blob:none
# Pack refs for faster access
git pack-refs --all --no-prune
# Create commit graph
git commit-graph write --reachable
# Verify setup
git for-each-ref --format="%(refname)" | wc -l
echo "CI ref optimization complete"
}
ci_ref_optimization
Terminal window
# Optimize refs during repository migration
migrate_repository_refs() {
local source_repo="$1"
local target_repo="$2"
echo "Migrating refs from $source_repo to $target_repo"
# Clone with packed refs
git clone --mirror "$source_repo" "$target_repo"
cd "$target_repo"
# Optimize ref storage
git pack-refs --all
# Create bitmap index
git repack -a -d --write-bitmap-index
# Update for serving
git update-server-info
echo "Repository migration with optimized refs complete"
cd -
}
migrate_repository_refs "/old/repo" "/new/repo"
Terminal window
# Check ref resolution
git rev-parse refs/heads/main
# Verify packed-refs integrity
git for-each-ref | head -5
# Rebuild packed-refs if corrupted
git pack-refs --all
Terminal window
# Debug packing issues
GIT_TRACE=1 git pack-refs --all
# Check file permissions
ls -la .git/refs/
ls -la .git/packed-refs
# Ensure repository is not corrupted
git fsck
Terminal window
# Profile ref operations
time git for-each-ref > /dev/null
# Check ref count impact
echo "Total refs: $(git for-each-ref | wc -l)"
# Optimize large ref counts
git pack-refs --all
git update-ref --stdin < /dev/null # Clear ref cache
Terminal window
# Debug pattern matching
git for-each-ref --format="%(refname)" | grep "pattern"
# Test packing with specific patterns
git pack-refs --include "refs/tags/v1.*" --dry-run
# Verify packed refs
grep "pattern" .git/packed-refs
#!/bin/bash
# Enterprise-scale ref optimization
enterprise_ref_optimization() {
local repo_path="$1"
cd "$repo_path"
echo "Optimizing enterprise repository refs..."
# Pre-optimization analysis
echo "Before optimization:"
echo "Loose refs: $(find .git/refs -type f | wc -l)"
echo "Total refs: $(git for-each-ref | wc -l)"
echo "Disk usage: $(du -sh .git/refs/)"
# Strategic packing
# Keep active branches loose
git pack-refs --include "refs/tags/*" \
--include "refs/remotes/*" \
--exclude "refs/heads/main" \
--exclude "refs/heads/develop"
# Pack historical branches
git pack-refs --include "refs/heads/release/*" \
--include "refs/heads/hotfix/*"
# Create maintenance schedule
git config maintenance.gc.enabled true
git config maintenance.prefetch.enabled true
# Post-optimization analysis
echo "After optimization:"
echo "Packed refs: $(grep -c '^[^#]' .git/packed-refs 2>/dev/null || echo 0)"
echo "Remaining loose: $(find .git/refs -type f | wc -l)"
# Performance verification
time git for-each-ref --format="%(refname)" > /dev/null
echo "Enterprise ref optimization complete"
}
enterprise_ref_optimization "/repos/large-enterprise-project"
Terminal window
# Manage refs in large monorepos
monorepo_ref_management() {
echo "Managing monorepo refs..."
# Pack stable component refs
for component in api web mobile shared; do
git pack-refs --include "refs/heads/$component/*" \
--include "refs/tags/$component/*"
done
# Keep integration branches loose
echo "Integration branches (main, develop) remain loose"
# Create component-specific packed-refs
# Note: Git doesn't support multiple packed-refs files
# This would require custom implementation
echo "Monorepo ref management complete"
}
monorepo_ref_management
Terminal window
# Optimize refs for backup efficiency
backup_ref_optimization() {
local backup_repo="$1"
cd "$backup_repo"
# Pack all refs for backup
git pack-refs --all
# Create backup manifest
cat > ref-backup-manifest.txt << EOF
Ref Backup Manifest
Created: $(date)
Repository: $(basename "$backup_repo")
Total refs: $(git for-each-ref | wc -l)
Packed refs: $(grep -c '^[^#]' .git/packed-refs 2>/dev/null || echo 0)
Loose refs: $(find .git/refs -type f | wc -l)
Packed size: $(stat -f%z .git/packed-refs 2>/dev/null || echo 0) bytes
EOF
# Verify backup integrity
git for-each-ref --format="verify %(refname)" | head -10
echo "Ref backup optimization complete"
}
backup_ref_optimization "/backups/my-repo"

What’s the difference between loose and packed refs?

Section titled “What’s the difference between loose and packed refs?”

Loose refs are individual files in .git/refs/, packed refs are stored in single .git/packed-refs file. Packed refs are faster for reading but slower for frequent updates.

Pack refs when repository has many (>100) refs, especially tags and inactive branches. Keep active development branches loose for performance.

Yes, use git update-ref to create loose refs from packed ones. Git automatically unpacks refs when they’re updated.

What’s the impact of packed refs on performance?

Section titled “What’s the impact of packed refs on performance?”

Packed refs improve read performance for many refs but slightly slow down updates. Best for repositories with mostly read operations.

Check .git/packed-refs file existence and content. Use git for-each-ref to see all refs regardless of storage method.

Yes, use —include and —exclude patterns to selectively pack refs. Useful for keeping active branches loose while packing stable refs.

What’s the relationship between pack-refs and git gc?

Section titled “What’s the relationship between pack-refs and git gc?”

git gc calls pack-refs as part of maintenance. Use pack-refs directly for manual control over ref packing behavior.

Use git for-each-ref for ref enumeration (works with both loose and packed). Check .git/packed-refs for direct access if needed.

Yes, like any file. Use git fsck to verify integrity. Rebuild with git pack-refs —all if corrupted.

What’s the storage savings from packed refs?

Section titled “What’s the storage savings from packed refs?”

Significant for many refs: loose refs use ~100 bytes each for filesystem overhead, packed refs store all refs efficiently in one file.

How do I migrate from loose to packed refs?

Section titled “How do I migrate from loose to packed refs?”

Simply run git pack-refs —all. Git handles both storage methods transparently. Loose refs take precedence over packed ones.

No, Git uses single .git/packed-refs file. All packed refs are stored together.

What’s the performance impact on git log?

Section titled “What’s the performance impact on git log?”

Minimal impact. Git’s ref resolution works efficiently with both loose and packed refs.

How do I monitor ref packing effectiveness?

Section titled “How do I monitor ref packing effectiveness?”

Compare git for-each-ref execution time before/after packing. Monitor .git directory size changes.

Yes, symbolic refs (like HEAD) remain loose files. Only direct refs (branches, tags) get packed.

What’s the difference between pack-refs and pack-objects?

Section titled “What’s the difference between pack-refs and pack-objects?”

pack-refs packs reference metadata; pack-objects packs Git objects (blobs, trees, commits). Both optimize storage but for different data types.

  1. Repository Optimization: Improve performance for repositories with many refs through efficient storage
  2. Storage Management: Reduce filesystem overhead from many small ref files
  3. Server Performance: Optimize Git servers for faster ref lookups and clone operations
  4. Maintenance Automation: Include in automated repository maintenance schedules
  5. Backup Efficiency: Streamline backup processes by consolidating ref storage
  6. Enterprise Scaling: Support large repositories with thousands of branches and tags