git.tools / guides

How to recover from a force push

Force-pushed and wiped commits off GitHub? They're very likely still recoverable — if you move before GitHub's garbage collector does. Here's how.

Last updated September 18, 2026 · 3 min read

Take a breath. A force push feels like you just deleted production, but in git almost nothing is truly deleted right away. The commits you wiped are probably still sitting on GitHub's servers — unreachable, but intact.

The catch: they're on a clock. GitHub's garbage collector eventually removes commits nothing points to. The window is undocumented and measured in weeks, but treat it like hours and you'll never be sorry.

Case 1: you have the commits locally

Best case, and the most common one — you force-pushed from your machine, so your local clone still has everything.

git reflog

Find the line where your branch pointed at the good commit (reflog lists every move HEAD made, newest first). Then put the branch back:

git branch recovery-branch abc1234
git push origin recovery-branch

You now have the lost state on GitHub under a new name, and you can merge or cherry-pick from it at your leisure.

Case 2: you don't have the commits locally

Someone else pushed the commits, or you wiped them from a machine you no longer have. Reflog can't help you — it only knows your clone's history. What you need is the SHA of a lost commit, and there are three places one usually survives:

A pull request. If the commits were ever in a PR, GitHub keeps them alive under the PR's hidden refs even after the source branch is gone. Open the PR — the commits are listed right there, SHAs included.

GitHub's activity feed. The repo's Activity view (and its API equivalent) records force pushes, including the SHA the branch pointed at before the push. That's exactly the commit you want back.

CI logs. Any build that checked out the branch printed the commit SHA it built. Scroll back to a build from before the force push.

Once you have a SHA, check that GitHub still has the object. The quickest way from a browser:

https://github.com/<owner>/<repo>/commit/<sha>

If the page loads, the commit is alive. Create a branch pointing at it (the GitHub API's "create ref" endpoint does this without any local clone), and you've recovered.

The git.tools way

Scan the wreckage with Rescue

Rescue scans your repo's GitHub activity for force pushes and branch deletions, checks which lost commits are still alive on GitHub's servers, and restores the ones that are onto a new branch. No local clone needed.

Open Rescue →

Why the recovered work should live on a new branch

Notice every path above ends with "create a new branch at the old SHA" — never "force push the branch back." Two reasons. First, whatever state the branch is in now may contain work you'd lose by blasting over it. Second, a recovered branch can be compared, reviewed, and merged like any other change. Recovery should leave a paper trail, not a second incident.

Make the next one boring

Force pushes happen. What turns them into disasters is having no backup and no idea where to look. Two cheap habits: use --force-with-lease so git refuses to clobber commits you haven't seen, and take a save point before any history rewrite so "recovery" becomes "reset to the bookmark."

Common questions

How long does GitHub keep commits after a force push?

GitHub doesn't publish a number. In practice, unreachable commits survive for weeks before garbage collection removes them — but nothing is guaranteed, which is why you should recover immediately, not tomorrow.

Does git reflog help if the commits were pushed from another machine?

No. Reflog is local-only and only records what happened in your clone. For commits you never had locally, you need the SHA from somewhere else — a pull request, GitHub's activity feed, or a CI log.

Can I recover commits after GitHub's garbage collection ran?

Unfortunately not. Once the objects are collected, GitHub support can't bring them back either. Your only hope at that point is a teammate's local clone or a CI checkout that still has them.

How do I stop this from happening again?

Two habits: use git push --force-with-lease instead of --force (it refuses to overwrite commits you haven't seen), and make a backup branch before any history rewrite. Our backup guide covers the thirty-second version.