News
🌿 Git Tutorials Recover Lost Commits with git reflog

Recover Lost Commits with git reflog

Undo a bad reset, rescue commits from a deleted branch, and find work you thought was gone. Reflog is the quiet hero of git.

Every git user has a "I just lost two hours of work" story. Usually it is a stray git reset --hard, a botched rebase, or a branch deleted before it was merged. Most of those stories should end differently: the commits are almost certainly still there, and git reflog can get them back.


What reflog actually is

git reflog is a log of every position HEAD has pointed to on your machine. Commits, checkouts, merges, rebases, resets — each of them records an entry. Entries live for about 90 days by default (30 for unreachable commits). Nothing you do in your working tree is truly gone during that window.

git reflog

Typical output:

a1b2c3d (HEAD -> main) HEAD@{0}: commit: fix login
7d8e9f0 HEAD@{1}: reset: moving to HEAD~3
c4d5e6f HEAD@{2}: commit: refactor auth
b1c2d3e HEAD@{3}: commit: add tests
e7f8a9b HEAD@{4}: commit: wire up API

Read that top-down as "what I did recently." HEAD@{0} is the current state. HEAD@{1} is where you were one move ago, and so on. If anything on that list was a mistake, you can rewind to just before it.


Scenario 1 — You ran git reset --hard and want it back

You meant to reset one commit, reset three, and now the last two commits you wrote are gone from git log. Do not panic.

git reflog

Find the entry before the reset. In the example above, that is HEAD@{2} (the reset is at HEAD@{1}, and the commit immediately before the reset is at HEAD@{2}).

Restore it:

git reset --hard HEAD@{2}

You are now back exactly where you were before the reset. Working tree, commits, and all.

Tip — if you have not committed your working tree changes, do that before running reflog recovery commands. Reflog covers committed history, not unstaged edits.


Scenario 2 — You deleted a branch that had unmerged work

You ran git branch -D feature/old-thing, and only after the shell returned realized the branch was not merged yet. git branch no longer shows it.

The branch pointer is gone, but the commits are still there. Reflog will still list them:

git reflog

Look for lines mentioning the branch name or the last commit message you remember. You can also use the standalone form:

git reflog show --all | grep -i "feature/old-thing"

Once you have the commit hash (any commit on the lost branch — the tip is best), recreate the branch from it:

git branch feature/old-thing a1b2c3d

git checkout feature/old-thing and you are back where you were.


Scenario 3 — A rebase went sideways

You ran git rebase -i and made a mess. Conflicts, wrong commits kept, lines accidentally deleted from the todo list.

git rebase --abort is the right move during a rebase. But if you already finished the rebase and only now noticed the damage:

git reflog

You will see rebase -i (start) and rebase -i (finish) entries. The commit immediately before the (start) is your pre-rebase state:

a1b2c3d HEAD@{0}: rebase -i (finish): returning to refs/heads/feature
7d8e9f0 HEAD@{1}: rebase -i (start): checkout ...
c4d5e6f HEAD@{2}: commit: actual work you want back

Restore:

git reset --hard HEAD@{2}

The rebase is undone as if it never happened.


Scenario 4 — You committed to the wrong branch, then blew it away

Happens most often with a hotfix: you commit a change, realize you were on main when you should have been on a branch, reset main back, and then — in frustration — force-push. Now the commit is nowhere.

If you have not done anything that would prune reflog (which is hard to do accidentally), the commit is still findable. Look for it:

git reflog | grep -i "hotfix keyword"

If the commit shows up:

git checkout -b hotfix/correct-branch a1b2c3d

This creates a new branch starting at the lost commit and checks it out. From there you can push normally.


Scenario 5 — "I do not remember what I did, I just know it is broken"

When you are not sure what went wrong, get a bigger picture than git reflog alone gives:

git reflog --date=iso --pretty=format:'%h %gd %gs' | head -50

That adds timestamps and formats the output into something scannable. You can also view the reflog of the index (useful when recovering staged-but-never-committed work in certain cases):

git reflog show stash
git reflog show --all

git fsck is another angle — it finds "dangling" commits (commits no branch or tag points to, but which still exist):

git fsck --lost-found

Output includes lines like dangling commit a1b2c3d.... Inspect each with git show a1b2c3d until you find the work. Then git branch rescue a1b2c3d pins a branch to it so it stops being dangling.


Make reflog last longer

Default expiration is 90 days for reachable entries and 30 for unreachable. If you want a safer window, set it globally:

git config --global gc.reflogExpire 180.days
git config --global gc.reflogExpireUnreachable 90.days

On a machine with plenty of disk, this is a cheap upgrade. The reflog is text — doubling its retention costs near nothing.


Reflog is per-clone

One important thing that trips people up: reflog is local to your machine. If you deleted the branch on the remote and then cloned fresh on a new laptop, the new laptop does not know about those old commits. Recover on the machine that did the damage, or look for someone on the team who still has a clone with the commits.

If nobody does and the commits were pushed to the remote at any point, the hosting provider (GitHub, GitLab, etc.) may still have them — the REST API often exposes dangling commit SHAs through the events endpoint for a limited time.


The habit that saves you

git reflog is the first command to run any time you feel that "wait, what just happened" jolt. Most of the time the lost work is in the top five lines. It takes five seconds and has rescued countless late-night commits. Build the reflex.