Skip to content

prune Git Command Guide

The git prune command removes all unreachable objects from the object database. It identifies objects that are not reachable from any refs and permanently deletes them to reclaim storage space. This is typically run automatically by git gc.

Terminal window
git prune [-n] [-v] [--progress] [--expire <time>] [--] [<head>...]
OptionDescription
-n, --dry-runDon’t remove anything, just report what would be removed
-v, --verboseReport all removed objects
--progressShow progress indicator
OptionDescription
--expire <time>Only expire loose objects older than
ParameterDescription
<head>...Additional heads to consider reachable
Reachable objects: Can be reached from refs (branches, tags, HEAD)
├── Current commits on branches
├── Objects referenced by current commits
├── Objects in reflog (if --expire not used)
└── Objects reachable from additional <head> parameters
Unreachable objects: Not reachable from any ref
├── Old commits from rebased branches
├── Objects from deleted branches/tags
├── Dangling objects from interrupted operations
└── Objects only in reflog (after expiration)
1. Identify: Find all refs (branches, tags, HEAD, reflog)
2. Traverse: Walk object graph from all reachable refs
3. Mark: Mark all reachable objects
4. Sweep: Remove unmarked loose objects
5. Clean: Remove unreachable entries from .git/shallow
Terminal window
# Dry run to see what would be removed
git prune --dry-run
# Prune with verbose output
git prune --verbose
# Prune with progress indicator
git prune --progress
Terminal window
# Prune only objects older than 2 weeks
git prune --expire=2.weeks.ago
# Prune objects older than specific date
git prune --expire=2023-01-01
# Prune very old objects (conservative approach)
git prune --expire=1.year.ago
Terminal window
# Consider additional heads reachable
git prune HEAD refs/original/refs/heads/master
# Prune with custom expiration
git prune --expire=30.days.ago --verbose
# Safe pruning with backup verification
git fsck --unreachable | grep commit > unreachable-before.txt
git prune --dry-run
git prune --verbose
#!/bin/bash
# Clean up after complex rebase operations
post_rebase_cleanup() {
echo "Cleaning up after rebase..."
# Show unreachable objects before pruning
echo "Unreachable objects before prune:"
git fsck --unreachable | wc -l
# Prune unreachable objects
git prune --verbose
# Clean up reflog entries
git reflog expire --expire-unreachable=now --all
# Final verification
echo "Unreachable objects after cleanup:"
git fsck --unreachable | wc -l
echo "Post-rebase cleanup complete"
}
post_rebase_cleanup
Terminal window
# Optimize repository size with careful pruning
optimize_repository_size() {
echo "Optimizing repository size..."
# Analyze current size
echo "Repository size before optimization:"
du -sh .git/
# Count loose objects
loose_count=$(find .git/objects -type f -name '[0-9a-f]*' | wc -l)
echo "Loose objects: $loose_count"
# Safe pruning approach
# First, expire old reflog entries
git reflog expire --expire=3.months.ago --all
# Then prune with conservative expiration
git prune --expire=2.months.ago --verbose
# Repack to consolidate
git repack -a -d --depth=250 --window=250
# Final size check
echo "Repository size after optimization:"
du -sh .git/
loose_count_after=$(find .git/objects -type f -name '[0-9a-f]*' | wc -l)
echo "Loose objects remaining: $loose_count_after"
}
optimize_repository_size
Terminal window
# Emergency cleanup for bloated repositories
emergency_cleanup() {
echo "=== EMERGENCY REPOSITORY CLEANUP ==="
echo "⚠ WARNING: This will permanently delete data ⚠"
# Create backup of current state
backup_dir="repo-backup-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$backup_dir"
cp -r .git "$backup_dir/"
echo "Backup created in: $backup_dir"
# Analyze current state
echo "Current repository analysis:"
git count-objects -v
# Aggressive but safe cleanup
# 1. Clean reflog
git reflog expire --expire=1.week.ago --all
# 2. Prune old objects
git prune --expire=1.month.ago --verbose
# 3. Aggressive repack
git repack -a -d -f
# 4. Final cleanup
git gc --aggressive --prune=now
# Verify repository integrity
if git fsck --full --strict; then
echo "✓ Repository integrity verified"
echo "Cleanup completed successfully"
# Remove backup after verification
read -p "Remove backup directory $backup_dir? (y/N) " -n 1 -r
if [[ $REPLY =~ ^[Yy]$ ]]; then
rm -rf "$backup_dir"
echo "Backup removed"
fi
else
echo "✗ Repository corruption detected!"
echo "Restore from backup: cp -r $backup_dir/.git .git"
exit 1
fi
}
# Use with extreme caution
emergency_cleanup
Terminal window
# Configure automatic pruning behavior
git config gc.pruneExpire 2.weeks.ago # Default prune expiration
git config gc.reflogExpire 90.days # Reflog expiration
git config gc.reflogExpireUnreachable 30.days # Unreachable reflog expiration
# Configure maintenance
git config maintenance.gc.enabled true
git config maintenance.commit-graph.enabled true
git config maintenance.loose-objects.enabled true
Terminal window
# Always run dry-run first
git prune --dry-run --verbose
# Check what will be removed
git fsck --unreachable | head -20
# Verify repository health before pruning
git fsck --full --strict
# Backup important branches before aggressive pruning
git branch backup-$(date +%Y%m%d) # Create backup branch
# Use conservative expiration times
git prune --expire=1.month.ago # Instead of --expire=now
Terminal window
# Monitor repository object counts
analyze_object_counts() {
echo "=== Repository Object Analysis ==="
# Count different object types
echo "Object counts by type:"
git cat-file --batch-check --batch-all-objects | \
cut -d' ' -f1 | sort | uniq -c | sort -nr
# Loose vs packed objects
git count-objects -v
# Unreachable objects
unreachable=$(git fsck --unreachable 2>/dev/null | wc -l)
echo "Unreachable objects: $unreachable"
# Repository size
echo "Repository size: $(du -sh .git/ | cut -f1)"
}
analyze_object_counts
#!/bin/bash
# Automated repository maintenance with safe pruning
automated_maintenance() {
echo "Starting automated repository maintenance..."
# Pre-maintenance checks
if ! git status --porcelain | grep -q .; then
echo "✓ Working directory is clean"
else
echo "✗ Working directory has uncommitted changes"
echo "Commit or stash changes before maintenance"
exit 1
fi
# Create maintenance backup
maintenance_backup="maintenance-backup-$(date +%Y%m%d-%H%M%S)"
git branch "$maintenance_backup"
echo "Maintenance backup created: $maintenance_backup"
# Safe maintenance steps
echo "Step 1: Cleaning reflog..."
git reflog expire --expire=30.days.ago --all
echo "Step 2: Pruning unreachable objects..."
git prune --expire=2.weeks.ago --verbose
echo "Step 3: Repacking repository..."
git repack -a -d --depth=250 --window=250
echo "Step 4: Updating auxiliary files..."
git update-server-info
# Post-maintenance verification
echo "Step 5: Verifying repository integrity..."
if git fsck --connectivity-only --no-progress; then
echo "✓ Repository integrity verified"
# Clean up maintenance backup
git branch -D "$maintenance_backup"
echo "Maintenance backup removed"
echo "✓ Automated maintenance completed successfully"
else
echo "✗ Repository integrity check failed!"
echo "Maintenance backup preserved: $maintenance_backup"
exit 1
fi
}
automated_maintenance
Terminal window
# Safe pruning in CI/CD pipelines
ci_safe_prune() {
echo "CI/CD safe pruning..."
# Only prune in clean CI environment
if [ -n "$CI" ] && [ -z "$(git status --porcelain)" ]; then
echo "✓ CI environment confirmed clean"
# Conservative pruning for CI
git reflog expire --expire=1.day.ago --all
git prune --expire=1.week.ago --quiet
# Update commit graph for performance
git commit-graph write --reachable --changed-paths
echo "✓ CI pruning completed"
else
echo "Skipping pruning - not in clean CI environment"
fi
}
ci_safe_prune
Terminal window
# Clean up after repository migration
post_migration_cleanup() {
local source_repo="$1"
echo "Post-migration cleanup for repository from $source_repo"
# Analyze what came from migration
echo "Analyzing migrated objects..."
git log --oneline --all | head -10
# Safe cleanup approach
# 1. Preserve recent history
git reflog expire --expire=1.year.ago --all
# 2. Prune old unreachable objects
git prune --expire=6.months.ago --verbose
# 3. Optimize storage
git gc --aggressive
# 4. Final verification
git fsck --full --strict
echo "Post-migration cleanup complete"
}
post_migration_cleanup "/path/to/source/repo"
Terminal window
# If you accidentally pruned important data
recover_from_prune() {
echo "Attempting to recover from accidental prune..."
# Check if reflog has the data
git reflog | head -20
# Try to recover from reflog
git checkout -b recovery HEAD@{1} # Or appropriate reflog entry
# If reflog doesn't help, check backups
ls -la *.backup 2>/dev/null || echo "No local backups found"
# Last resort: contact colleagues for copies
echo "Consider asking team members for repository copies"
}
recover_from_prune
Terminal window
# Diagnose and repair repository corruption
repair_corrupted_repo() {
echo "Diagnosing repository corruption..."
# Run detailed fsck
git fsck --full --strict 2>&1 | tee fsck-report.txt
# Check for specific corruption types
if grep -q "missing blob" fsck-report.txt; then
echo "Missing blob objects detected"
fi
if grep -q "dangling commit" fsck-report.txt; then
echo "Dangling commits found - usually safe to ignore"
fi
# Attempt repair
echo "Attempting automatic repair..."
if git fsck --full --strict --no-progress; then
echo "✓ Repository appears healthy"
else
echo "✗ Repository corruption detected"
echo "Consider restoring from backup"
exit 1
fi
}
repair_corrupted_repo
Terminal window
# Handle large repository pruning
large_repo_prune() {
echo "Handling large repository prune..."
# Check system resources
free -h
df -h .
# Use incremental approach
echo "Using incremental pruning approach..."
# Prune in smaller batches
for age in 1.year 6.months 3.months 1.month; do
echo "Pruning objects older than $age..."
git prune --expire="$age".ago --quiet
done
# Final aggressive cleanup
git gc --aggressive --prune=2.weeks.ago
echo "Large repository pruning complete"
}
large_repo_prune
Terminal window
# Handle permission issues during pruning
fix_prune_permissions() {
echo "Fixing prune permission issues..."
# Check permissions
ls -la .git/objects/
# Fix ownership
sudo chown -R $(whoami) .git/
# Fix permissions
find .git -type d -exec chmod 755 {} \;
find .git -type f -exec chmod 644 {} \;
# Retry prune
git prune --dry-run
echo "Permission issues resolved"
}
fix_prune_permissions
#!/bin/bash
# Enterprise-scale repository maintenance
enterprise_repo_maintenance() {
local repo_path="$1"
local retention_days="${2:-90}"
cd "$repo_path"
echo "Enterprise repository maintenance for $(basename "$repo_path")"
# Pre-maintenance analysis
echo "Pre-maintenance analysis:"
git count-objects -v
echo "Repository size: $(du -sh .git/ | cut -f1)"
# Create maintenance snapshot
maintenance_tag="maintenance-$(date +%Y%m%d-%H%M%S)"
git tag "$maintenance_tag" -m "Maintenance snapshot before cleanup"
# Safe maintenance procedure
echo "Step 1: Reflog cleanup..."
git reflog expire --expire="${retention_days} days ago" --all
echo "Step 2: Unreachable object pruning..."
git prune --expire="${retention_days} days ago" --verbose
echo "Step 3: Repository repacking..."
git repack -a -d --depth=250 --window=250
echo "Step 4: Auxiliary data update..."
git update-server-info
git commit-graph write --reachable
# Post-maintenance analysis
echo "Post-maintenance analysis:"
git count-objects -v
echo "Repository size: $(du -sh .git/ | cut -f1)"
# Integrity verification
if git fsck --connectivity-only --no-progress; then
echo "✓ Repository integrity verified"
# Clean up maintenance tag after successful verification
git tag -d "$maintenance_tag"
echo "✓ Enterprise maintenance completed successfully"
else
echo "✗ Repository integrity check failed!"
echo "Maintenance tag preserved: $maintenance_tag"
exit 1
fi
}
enterprise_repo_maintenance "/repos/critical-project" 180
Terminal window
# Clean up development branches and unreachable objects
dev_workflow_cleanup() {
echo "Development workflow cleanup..."
# Remove merged branches
git branch --merged | grep -v "\*" | xargs -n 1 git branch -d 2>/dev/null || true
# Remove old remote branches
git remote prune origin
# Clean up reflog
git reflog expire --expire=30.days.ago --all
# Prune unreachable objects
git prune --expire=2.weeks.ago --verbose
# Repack for efficiency
git repack -a -d
echo "Development workflow cleanup complete"
}
dev_workflow_cleanup
Terminal window
# Prepare repository for disaster recovery
disaster_recovery_prep() {
echo "Preparing repository for disaster recovery..."
# Create comprehensive backup
backup_file="disaster-recovery-$(date +%Y%m%d).bundle"
git bundle create "$backup_file" --all
# Aggressive cleanup while preserving recoverability
git reflog expire --expire=1.year.ago --all
git prune --expire=6.months.ago --verbose
# Create recovery manifest
cat > recovery-manifest.txt << EOF
Disaster Recovery Manifest
Created: $(date)
Backup file: $backup_file
Repository: $(pwd)
Last commit: $(git rev-parse HEAD)
Branches: $(git branch | wc -l)
Tags: $(git tag | wc -l)
Size after cleanup: $(du -sh .git/ | cut -f1)
EOF
# Test recovery
echo "Testing recovery from backup..."
if git bundle verify "$backup_file" >/dev/null 2>&1; then
echo "✓ Backup integrity verified"
echo "Recovery preparation complete"
else
echo "✗ Backup verification failed!"
exit 1
fi
}
disaster_recovery_prep

What’s the difference between git prune and git gc?

Section titled “What’s the difference between git prune and git gc?”

git gc is a complete garbage collection that calls git prune plus other optimizations; git prune only removes unreachable loose objects. Use gc for comprehensive maintenance.

Run manually when git gc —auto doesn’t trigger, or for specific cleanup needs. Most users should use git gc instead.

No, git prune only removes objects unreachable from all refs. It uses git fsck —unreachable to identify safe deletions.

Controls how old objects must be before pruning. Protects recently created unreachable objects (from interrupted operations).

How do I recover from accidental git prune?

Section titled “How do I recover from accidental git prune?”

Check reflog for recoverable commits, restore from backups, or ask colleagues for repository copies. Prevention is better than recovery.

Yes, operates on object database regardless of working directory. Essential for server-side repository maintenance.

What’s the impact of git prune on repository size?

Section titled “What’s the impact of git prune on repository size?”

Removes loose unreachable objects, potentially significant space savings. Packed unreachable objects remain until repack -a -d.

How do I check what git prune will remove?

Section titled “How do I check what git prune will remove?”

Use —dry-run —verbose to see exactly what objects would be removed without actually deleting them.

Can git prune cause repository corruption?

Section titled “Can git prune cause repository corruption?”

No, when used correctly. It only removes objects verified as unreachable. Always run git fsck before and after.

What’s the relationship between prune and reflog?

Section titled “What’s the relationship between prune and reflog?”

git prune considers reflog entries reachable by default. Use git reflog expire first to make reflog entries unreachable.

How do I handle git prune in automated scripts?

Section titled “How do I handle git prune in automated scripts?”

Use —dry-run first, check exit codes, implement timeouts, and have recovery procedures. Log all operations.

No, only removes loose unreachable objects. Use git repack -a -d to remove unreachable packed objects.

What’s the performance impact of git prune?

Section titled “What’s the performance impact of git prune?”

Depends on repository size and loose object count. Can be slow on large repos with many loose objects.

Compare git count-objects output before/after, check repository size changes, and monitor loose object counts.

Yes, but operates on main repository object database. Worktree-specific objects are handled through the main repo.

Use —dry-run —verbose first, ensure clean working directory, have backups, use conservative —expire times.

  1. Repository Maintenance: Clean up unreachable objects to reclaim storage space and improve performance
  2. Post-Operation Cleanup: Remove objects left behind by rebases, branch deletions, and interrupted operations
  3. Storage Optimization: Reduce repository size by eliminating unnecessary loose objects
  4. Disaster Recovery: Prepare repositories for backup by removing expendable data
  5. Performance Tuning: Maintain optimal repository performance through regular cleanup
  6. Compliance Management: Support data retention policies by removing old unreachable objects