Table of contents
- What is Git reflog and why it's important
- Recovering from an accidental hard reset
- Recovering from an unintended rebase
- Recovering a deleted branch
- Recovering from a force push (remote commit recovery)
- Reflog tips and limitations
- Using Graphite to avoid and visualize commit mistakes
Git's reference logs (reflogs) are a lifesaver when you think you've lost commits. This guide will show how to use git reflog
to recover lost work in various scenarios. It assumes you know basic Git (branches, commits, reset, rebase) and provides step-by-step examples for local and remote recovery.
What is Git reflog and why it's important
A reflog is essentially a log of every move the Git HEAD has made in your local repository. Every commit, checkout, reset, or rebase updates the HEAD and gets recorded in the reflog. In other words, reflog records when branch tips and other refs were updated in the local repo. This is extremely useful because even if a commit is “lost” (no branch or tag pointing to it), its reference may still exist in the reflog, allowing you to retrieve it.
Key points about reflog:
- Reflog is local: each repository has its own reflog; it isn’t pushed to remotes. That’s why you can recover commits you lost locally, but you won’t see reflog entries on Git hosting services.
- Reflog entries are indexed like
HEAD@{0}
,HEAD@{1}
, etc., where{0}
is the latest state and higher indices go back in time. You can use these in Git commands (for example,HEAD@{1}
refers to the previous HEAD). - By default, reflog keeps history for a limited time (90 days for normal entries, 30 days for those with no refs) before older entries expire (more on this in tips at the end).
Recovering from an accidental hard reset
Let’s say you ran git reset --hard HEAD~1
and realized you reset away a commit you still needed. The branch pointer has moved, but the commit isn’t truly gone – it’s just orphaned. You can get it back:
Find the lost commit in the reflog: Run
git reflog
to list recent history. Look for the entry just before the reset. For example, you might see:Terminal$ git reflog54ad9f0 HEAD@{0}: reset: moving to HEAD~1b921d4c HEAD@{1}: commit: Fix login bugHere
HEAD@{1}
(commitb921d4c
) is the commit that was “lost” by the reset. Reflog shows that HEAD moved fromb921d4c
to an earlier commit.Restore the commit: You have a couple of options to recover the commit:
Reset back to it: Use
git reset --hard <commit-hash>
to move the branch HEAD back to the lost commit. In our example,git reset --hard b921d4c
would put the branch back to the Fix login bug commit. (Be careful: this will discard any new uncommitted changes on your working directory.)Create a new branch at that commit: If you don’t want to disturb the current branch’s state, create a new branch from the commit:
Terminalgit checkout -b recovered <commit-hash>This makes a new branch pointing to the lost commit, allowing you to inspect or merge it as needed.
The commit is now recovered on your local repo. You can continue working or merge it back into your main line of development.
Recovering from an unintended rebase
Rebases rewrite commit history, so it’s easy to drop or alter commits by accident. If a rebase went wrong, git reflog
can help you undo it:
Identify the pre-rebase commit: Find the commit that was at the tip of your branch before the rebase. Run
git reflog
and look for entries related to the rebase. Often, you’ll see an entry likeHEAD@{N}: rebase:...
or a checkout indicating where HEAD was before. That commit hash is the last good state before the rebase. (If multiple rebases or complex changes happened, find the point just before things went awry.)Reset the branch to the old commit: Once you have the commit hash (let’s call it
<old-commit>
), do:Terminalgit reset --hard <old-commit>This brings the branch back to the state before the rebase. All the original commits will reappear as they were before the rebase.
Alternative – cherry-pick missing commits: If the rebase dropped specific commits that you want to keep (and you don’t want to completely undo the rebase), you can cherry-pick those commits onto the current branch. First, use
git reflog
(or evengit log --all
) to locate the hashes of the missing commits, then rungit cherry-pick <hash>
for each to reapply them on your branch.
Recovering a deleted branch
Maybe you deleted a branch and then realized it had commits you still need. If the branch was deleted, its commits might not be referenced by any branch now – but if they were recently active, you can likely recover them with reflog:
Find the branch’s last commit: When you delete a branch, Git often prints the commit SHA that the branch was at. If you have that from the terminal output, note it. If not, use reflog to find it:
- If you had checked out that branch or merged it recently, run
git reflog
(which shows HEAD history) orgit reflog --all
to search for the branch’s commits. Look for the branch name or commit message in the reflog output. Each branch has its own reflog too, but once deleted, you might rely on HEAD’s reflog or--all
to track it down. - You can also try
git log --all --grep='<commit message fragment>'
if you remember part of a commit message, to locate the commit.
- If you had checked out that branch or merged it recently, run
Recreate the branch at that commit: Once you have the commit hash (e.g.
abc1234
), run:Terminalgit checkout -b <branch-name> abc1234This creates a new branch pointing to the recovered commit. The branch is now restored with all its commits intact.
Push to remote if needed: If the branch was also on a remote and you want to restore it there, push the new branch:
git push origin <branch-name>
. (If the remote branch was deleted and you want to reinstate it, ensure collaborators know – especially if it was deleted intentionally after merging.)
Recovering from a force push (remote commit recovery)
Recovering lost commits on a remote (e.g. GitHub or GitLab) often comes into play after an accidental git push --force
that overwrote history. The remote server itself doesn’t have reflog accessible to you, but your local clone does. Here’s how to get back a commit that disappeared from a remote branch:
Find the commit in your local reflog for the remote branch: Your local repo keeps track of where the remote branch pointers were when you last fetched or pushed. Use:
Terminalgit reflog show origin/<branch-name>For example, to recover a commit lost on
origin/main
, rungit reflog show origin/main
. This will show the update history of your tracking branch. You should see an entry for the force-push; the line before it will have the old commit hash that was onorigin/main
before the push.Checkout the lost commit (optional verification): Once you identify the commit hash that was lost, it’s wise to check it out to verify its contents. For example:
Terminalgit checkout <lost-commit-hash>This puts you in a detached HEAD at that commit. You can inspect the code to confirm this is indeed the commit you want to restore.
Restore the remote branch: There are a couple of ways to put this commit back on the remote:
Force-push the commit to the original branch: If you want
origin/<branch-name>
to go back to that commit, you can push it directly. For example:Terminalgit push origin <lost-commit-hash>:<branch-name> --forceThis tells Git to update the remote branch to the specified commit (the same effect as a force push from that commit). After this, collaborators should pull or reset to this restored history as needed.
Push as a new branch (safer): If you prefer not to rewrite the remote branch forcefully (for example, if new commits were added after the bad push and you want to preserve them), you can push the recovered commit as a new branch:
Terminalgit checkout -b recovered-branch <lost-commit-hash>git push origin recovered-branchThis way, the lost commit is on the remote in a separate branch. You can then decide how to integrate it (merge, cherry-pick, etc.) without clobbering others’ work.
In either case, the important part is that the commit is no longer lost – your reflog helped retrieve it. If the intention is to undo a mistake, you’ll likely use the force-push method to put the history back as it was.
Reflog tips and limitations
- Reflog is local and temporary: The reflog exists only in your local
.git
directory. You can’t fetch another person’s reflog or push yours to a server. This also means if you clone a fresh repo, you won’t have the reflog history of the original. - Entries expire and commits can vanish: Reflog entries expire after a time (by default, 90 days for normal entries, and 30 days for those that became unreachable). After that, or after a garbage collection, orphaned commits might be cleaned up permanently. So act relatively quickly when you need to recover something. (Also, avoid running
git gc
until you’ve recovered the commits you need.) - Use new branches for safety: When in doubt, recover lost commits on a new branch rather than immediately resetting HEAD of a main branch. This allows you to review and test before affecting your team’s main history. You can always merge or rebase the recovered commits back once you’re confident.
- Good commit messages help: Since reflog shows commit messages, having descriptive commit messages makes it easier to spot the right commit in a sea of reflog entries. For example, seeing “Fix login bug” in the reflog is quicker to identify than an ambiguous message.
- Prevention and backups: While reflog is great for recovery, it’s best to avoid needing it. Be cautious with destructive commands like
reset --hard
andpush --force
(use them sparingly and with purpose). For critical branches, consider using protected branches or backup solutions if appropriate. And always ensure your work is pushed or backed up somewhere if it’s important.
Git’s reflog is an indispensable safety net for undoing mistakes. With the steps above, you can confidently recover commits from resets, rebases, branch deletions, and even remote overwrites.