Skip to content
Back to blog
5 min read

Recovering from a Force Push with Reflog and Timestamps

How to recover a lost Git state after a destructive rebase and force push using the reflog and timestamps.


I recently found myself in a situation that most engineers eventually face: I had finished a complex rebase, tidied up my commit history, and confidently force-pushed to my remote branch. Moments later, I realised I had mistakenly dropped some commits containing several hours of work that I actually needed to keep.

It’s easy to see a force push as a catastrophic irreversible event. The word “force” itself suggests finality which can trigger panic. However this mistake is common and not a sign of incompetence. It’s more likely a result of juggling multiple things at once.

A force push is simply another of the tools in the git toolbox. It’s certainly a sharp tool but Git is designed with enough internal redundancy that recovery is usually a matter of knowing where to look.

What actually happens during rebase and force push#

To recover effectively, it helps to remember what Git is doing under the hood. In Git, branches are essentially just pointers to specific commits.

When you rebase, Git isn’t just moving commits; it is rewriting them, creating entirely new commit objects with different hashes even if the changes remain the same. When you then perform a git push --force, you are telling the remote server to move its branch pointer to your new, rewritten history. In doing so, the old commits are no longer referenced by that branch.

The key insight is that while the branch pointer has moved, the old commits themselves are not immediately deleted. They remain in Git's object database as "orphan" commits until the next garbage collection cycle. They are still there; you just don't have a name for them any more.

Introducing the reflog#

This is where the reflog becomes invaluable. While git log shows you the history of the current branch, git reflog shows you the history of where your local HEAD has been.

Every time you commit, checkout, rebase, or reset, Git records that movement in the reflog. It is a local-only diary of your actions.

git reflog

The reflog typically has a retention window of around 90 days. This means that even if you force-push and "overwrite" a branch, your local machine still remembers exactly which commit hash your branch was pointing to ten minutes ago.

Recovering by timestamp#

While the standard reflog output is useful, it can be difficult to parse if you have been performing many operations in a short period. In my case, I knew exactly when I made the mistake, which allowed me to use one of Git's most useful hidden features: timestamp-based filtering.

If you know you had the correct state around an hour ago, you can search the reflog within a specific time range:

git reflog --since="2026-02-06 09:30" --until="2026-02-06 10:30"

This turns the reflog from a dense list of hashes into a chronological narrative. I find that searching by time can be more reliable than trying to identify a specific commit message, especially during a rebase where messages might be duplicated or edited.

Inspect before acting#

Once you find a candidate commit hash from the reflog, resist the urge to immediately git reset. Instead, inspect the state of that commit to ensure it contains the work you expect.

git show <hash>

Or, to see the entire tree at that point:

git checkout <hash>

Entering a "detached HEAD" state is a safe way to verify the files without affecting your current branch, because no branch pointer is moved in the process. Once you have confirmed that this is indeed the state you want to recover, you can move to the final step.

Making the recovery safe#

The safest way to recover is not to overwrite your current branch immediately, but to "anchor" the lost commit with a new branch. This prevents the commit from being garbage collected and gives you a stable place to work from.

git branch recovery-2026-02-06 <hash>

By creating a new branch at that hash, you have successfully recovered your "lost" state. You can now cherry-pick the missing commits back into your working branch, or compare the two branches to see exactly what was lost. This approach treats recovery as a safety step rather than a final, high-pressure decision.

When the reflog isn't enough#

In rare cases, the standard reflog might not show what you need. If you were working on a different branch that you have since deleted, you might need to look at the reflog for all references:

git reflog --all

If even that fails, git fsck is rarely needed, but can identify "dangling" commits that aren't reachable from any branch or tag:

git fsck --lost-found

A critical warning: if you are in a recovery situation, avoid running git gc (garbage collection) until you have secured your lost commits. Garbage collection is the only process that will actually remove those orphaned commits from your disk.

Closing reflection#

Force pushes are often framed as something to be avoided. While they should be used with care, they have a place, but understanding how to recover from them makes them far less intimidating.

This experience reinforced that knowing how to recover is as important as knowing how to avoid the mistake in the first place. Understanding Git's internals, such as how it treats commits as immutable objects and how it tracks your movements through the reflog, reduces the fear of "breaking" or "rewriting" history. More generally, systems that feel fragile are often just poorly understood.

If you can find the hash, you can find your work.

Recovery Checklist#

  • Stop: Avoid running destructive commands or garbage collection.
  • Find: Use git reflog --since="..." --until="..." to locate the lost commit hash.
  • Verify: Use git show <hash> to confirm the contents.
  • Anchor: Create a new branch with git branch recovery-branch <hash> to secure the state.
  • Integrate: Merge or cherry-pick the recovered commits back into your working branch.