# Fix Git Reflog Expired and Losing Orphaned Branch Reference
You need to recover a deleted branch or a lost commit, but git reflog does not show it:
git reflog
# The entry for the deleted branch is not hereOr:
git reflog show --all
# Still missing the commit you needThe reflog entries have expired. By default, Git keeps reflog entries for 90 days for reachable commits and 30 days for unreachable ones.
Understanding Reflog Expiration
The reflog records every change to branch HEADs. It is your safety net for recovering lost work. But reflog entries are not permanent:
# Check reflog expiration settings
git config gc.reflogExpire
git config gc.reflogExpireUnreachableDefault values:
- gc.reflogExpire: 90 days (reachable commits)
- gc.reflogExpireUnreachable: 30 days (unreachable commits, like deleted branches)
After garbage collection runs (git gc), expired reflog entries are permanently deleted.
Step 1: Check If GC Has Run
If the branch was recently deleted, GC may not have run yet:
git reflog --all | grep "branch_name"If this returns nothing, the reflog entry has expired or GC has already cleaned it up.
Step 2: Find Orphaned Commits With fsck
Even without reflog entries, orphaned commits may still exist in the object database:
git fsck --lost-found --no-reflogsThis finds all unreachable (orphaned) objects:
dangling commit abc1234def5678...
dangling commit 901abcdef23456...
dangling blob 567890abcdef1234...Examine each dangling commit:
git show abc1234def5678
git log --oneline abc1234def5678If you find the commit you need, recover it:
git branch recovered-branch abc1234def5678Step 3: Extend Reflog Retention
To prevent future reflog expiration:
```bash # Keep reflog entries for 1 year git config --global gc.reflogExpire 365.days.ago git config --global gc.reflogExpireUnreachable 180.days.ago
# Or never expire (use with caution - reflog grows indefinitely) git config --global gc.reflogExpire never git config --global gc.reflogExpireUnreachable never ```
Step 4: Prevent GC From Running Automatically
Git runs GC automatically when certain thresholds are met. To disable automatic GC:
git config --global gc.auto 0Or set a high threshold:
git config --global gc.auto 10000GC will only run automatically when there are 10,000+ loose objects.
Step 5: Create Permanent References
If a commit is important, create a permanent reference so GC never removes it:
```bash # Create a branch git branch important-work abc1234
# Or create a tag git tag v1.0-important abc1234 ```
Tags and branches are permanent references. As long as a commit is reachable from any reference, GC will never remove it.
Step 6: Recover From Backup
If GC has already run and the commits are truly gone, check for backups:
```bash # Check if the repo was bundled ls *.bundle
# Check other clones of the repo ssh colleague@machine "cd /path/to/repo && git log --oneline lost-branch" ```
If a colleague cloned the repo before the branch was deleted, they have the commits.
Step 7: Use Git Bundle for Safety
Create periodic bundles of important branches:
```bash # Bundle all branches git bundle create /backup/repo-backup.bundle --all
# Bundle specific branch git bundle create /backup/feature-backup.bundle feature-branch ```
Restore from bundle:
git clone /backup/feature-backup.bundle -b feature-branch recovered-repoThis is a lightweight backup that includes all objects and references.
Step 8: Reflog-Based Recovery Workflow
When you accidentally delete a branch:
```bash # IMMEDIATELY check reflog (before GC runs) git reflog
# Find the last commit of the deleted branch git reflog | grep "checkout: moving from feature to"
# Recreate the branch git branch feature HEAD@{5} ```
The key is speed -- do this immediately after the accidental deletion, before GC has a chance to clean up.
Configuring Reflog for Critical Repositories
For repositories where data loss would be catastrophic:
```bash # In the repository git config gc.reflogExpire never git config gc.reflogExpireUnreachable 365.days.ago git config gc.auto 0
# Set up automated bundles echo '0 2 * * 0 cd /path/to/repo && git bundle create /backup/weekly-$(date +\%Y\%m\%d).bundle --all' | crontab - ```
This creates weekly full bundles and never expires the reflog, providing two layers of recovery.