Recover Lost Commits / Reflog
Accidentally deleted a branch? Lost commits after a hard reset? Git's reflog is your safety net. It records every movement of your branch pointers, allowing you to recover commits you thought were gone forever.
The reflog (reference log) is Git's safety net. It records every time your branch pointers move—commits, checkouts, merges, rebases, resets, and more. Unlike the commit history, which shows the tree of commits, the reflog shows the chronological history of where your HEAD has been. Think of it as a "browser history" for Git.
If you accidentally delete a branch, reset to the wrong commit, or lose work during a rebase, the reflog is your best friend. Git keeps reflog entries for about 90 days by default, giving you plenty of time to recover from mistakes. The reflog is local to your repository, so it's not shared when you push or pull.
To view the reflog, run git reflog or git log -g. The output shows each movement of HEAD with a commit hash, an index number (like HEAD@{1}), and a description of what happened. The most recent entries appear first.
The index numbers are relative to the current position. HEAD@{0} is where you are now. HEAD@{1} is where you were before your last operation. HEAD@{2} is two operations ago, and so on. This makes it easy to navigate backward through your Git history.
# View the reflog
$ git reflog
# Example output:
a1b2c3d (HEAD -> main) HEAD@{0}: commit: Fix login bug
e4f5g6h HEAD@{1}: checkout: moving from feature to main
i7j8k9l HEAD@{2}: commit: Add new feature
m9n0o1p HEAD@{3}: reset: moving to HEAD~2
q2r3s4t HEAD@{4}: commit: Initial commit
git show, git checkout, or git reset to recover lost commits.
Scenario 1: You did a hard reset and lost commits. This is the most common reason to use reflog. Run git reflog, find the commit hash from before the reset (look for "reset" in the description), and reset back to that commit.
Scenario 2: You deleted a branch that had unmerged work. The commits are still in the reflog. Find the last commit of the deleted branch in the reflog, then create a new branch at that commit using git branch .
Scenario 3: A rebase went wrong. During a rebase, Git creates many reflog entries. Find the commit from before the rebase started (look for "rebase" or "checkout") and reset to that point with git reset --hard.
# Scenario 1: Recover from bad hard reset
$ git reflog
a1b2c3d HEAD@{2}: reset: moving to HEAD~3
e4f5g6h HEAD@{3}: commit: Important work before reset
$ git reset --hard e4f5g6h
# Scenario 2: Recover a deleted branch
$ git reflog
i7j8k9l HEAD@{5}: commit: Last commit of deleted-feature
$ git branch recovered-feature i7j8k9l
# Scenario 3: Abort a failed rebase and restore
$ git reflog
m9n0o1p HEAD@{10}: checkout: before rebase
$ git reset --hard m9n0o1p
Sometimes you don't need to restore the entire commit—just one file from a lost commit. You can use git checkout with the commit hash and file path to extract a single file from a lost commit.
First, find the commit hash in the reflog. Then run git show to preview the file's content. To restore it to your working directory, use git checkout . This is useful when you want to recover a specific function or configuration without undoing other changes.
# Preview a file from a lost commit
$ git show e4f5g6h:src/app.js
# Restore a single file from a lost commit
$ git checkout e4f5g6h -- src/app.js
$ git status # File is now staged for commit
# Restore multiple files
$ git checkout e4f5g6h -- src/ config/
Reflog entries expire after 90 days by default. After that, Git's garbage collection (git gc) may remove unreachable commits permanently. This means you have a 90-day window to recover lost work. After 90 days, recovery becomes much harder or impossible.
You can adjust the expiration time with configuration settings. For critical repositories, you might want to keep reflog entries longer. To manually expire old entries, use git reflog expire. To trigger garbage collection, use git gc.
# Set reflog expiration to 180 days
$ git config gc.reflogExpire "180.days.ago"
$ git config gc.reflogExpireUnreachable "90.days.ago"
# Manually expire old reflog entries
$ git reflog expire --expire=now --all
# Run garbage collection
$ git gc --prune=now
# Check reflog expiration settings
$ git config --get gc.reflogExpire
git gc --prune=now permanently deletes unreachable commits. Only do this if you're certain you don't need any lost commits.
The reflog is local, so it won't help if your local hard drive fails. For important work, always push to GitHub regularly. If a commit was pushed and then force-pushed over, you can still recover it from GitHub's event timeline or by checking the reflog of a coworker who pulled before the force push.
On GitHub, you can view a repository's network graph to see all branches and commits. If a branch was deleted, you may still find its commits referenced in pull requests or issues. For force-pushed commits, GitHub's API can sometimes retrieve them if you act quickly.
# Check if a commit exists on GitHub
$ git ls-remote origin | grep
# Fetch all remote branches and tags
$ git fetch --all
# Check reflog of a remote branch
$ git reflog show origin/main
The reflog gives you commit hashes, but sometimes you need to see the commit tree to understand what was lost. Use git log --graph --oneline --all to see all reachable commits. For unreachable commits (those only in reflog), use git log --walk-reflogs.
You can also create a temporary branch at a reflog entry to explore it safely. Use git branch temp then git log temp to see what's there. Once you've confirmed it's the right commit, you can merge or cherry-pick as needed.
# See all commits including unreachable ones
$ git log --graph --oneline --all
# See reflog as a graphical timeline
$ git log --graph --oneline --walk-reflogs
# Create a temporary branch to explore a lost commit
$ git branch explore-lost e4f5g6h
$ git checkout explore-lost
$ git log --oneline -10
# After verification, cherry-pick the lost commit
$ git checkout main
$ git cherry-pick e4f5g6h
Push frequently. The safest commit is one that's on GitHub. Push after completing logical work, not just at the end of the day.
Use branches for experiments. Create a branch before trying risky operations like rebasing or resetting. If something goes wrong, your original branch is safe.
Review before resetting. Always run git status and git log before a hard reset. Consider using git reset --soft or git stash first.
Enable push protection. GitHub's branch protection can prevent force pushes to important branches, protecting shared work from accidental overwrites.
Back up your repositories. Regular backups of your local repositories (or using GitHub's repository backups) provide an additional safety net.
git reflog and look for entries with "stash". The stash reflog is stored separately. Use git stash list to see stashes, or git fsck --unreachable to find orphaned commits that were stashed.The reflog is Git's emergency recovery system. When you make a mistake, don't panic—check the reflog first. Most lost commits can be recovered with a few commands.