Git Rebase Gone Wrong: How to Recover Lost Commits and Unfuck Your Branch

Git Rebase Gone Wrong: How to Recover Lost Commits and Unfuck Your Branch

By Elena Rodriguez · April 19, 2026 · 16 min read

Quick Answer

Nearly every git rebase disaster is recoverable. Git keeps every commit you have ever touched in the reflog for 30 to 90 days, even after they leave the main history. The recovery workflow is: check git reflog, find the SHA of the last good state, reset or cherry-pick back to it. For commits that are not in the reflog (because they were never on any branch of yours), git fsck --lost-found surfaces dangling objects. Force-with-lease and good aliases prevent most disasters in the first place.

Key Insight

Nearly every git rebase disaster is recoverable. Git keeps every commit you have ever touched in the reflog for 30 to 90 days, even after they leave the main history. The recovery workflow is: check git reflog, find the SHA of the last good state, reset or cherry-pick back to it. For commits that are not in the reflog (because they were never on any branch of yours), git fsck --lost-found surfaces dangling objects. Force-with-lease and good aliases prevent most disasters in the first place.

The Monday Morning Rebase

It is 9:47 AM. You are trying to clean up your feature branch before a pull request. You fire off git rebase -i main, drop two commits you thought were obsolete, squash three more, force-push, and close the lid.

Ten minutes later your teammate Slacks: "did you mean to remove the fix for the auth bug?"

Your stomach drops. The commit is not in git log anymore. The force-push nuked it from origin. You are pretty sure it is gone forever.

It almost certainly is not. Git is much better at preserving history than most developers believe, and nearly every "I destroyed my branch" scenario is recoverable if you know the right incantations within the first 30 days.

This is the field manual. I have used every technique below to rescue my own work and teammates' work. Bookmark it.

For general git fundamentals, see our advanced git tutorial which covers the underlying model that makes these recoveries possible.

First Response: Did You Just Notice Immediately?

If you just ran a rebase and realized it was wrong — before you have done anything else — the fastest possible recovery is one line:

bash
git reset --hard ORIG_HEAD

ORIG_HEAD is a reference Git automatically sets before every rebase, merge, and reset. It points to your branch's state immediately before the last destructive operation. Resetting to it undoes the rebase entirely.

This works if the rebase finished. If you are mid-rebase (conflicts, edit stops), use:

bash
git rebase --abort

That terminates the in-progress rebase and returns to exactly where you started.

If neither worked because you have already done other operations after the rebase, move to the reflog.

The Reflog: Git's Undo History

git reflog is the recovery command. It shows every position HEAD has occupied in this repository, with timestamps and operation descriptions.

bash
$ git reflog
4a3b2c1 HEAD@{0}: rebase (finish): returning to refs/heads/feature/auth
4a3b2c1 HEAD@{1}: rebase (pick): add login validation
9e8f7d6 HEAD@{2}: rebase (pick): refactor auth middleware
b5c4a3f HEAD@{3}: rebase (start): checkout main
2d1e0f9 HEAD@{4}: commit: fix auth edge case
c0b1a2d HEAD@{5}: commit: add retry logic

Each line is a HEAD position. HEAD@{0} is current; HEAD@{3} is three operations ago. The SHAs are real commit SHAs — even "dropped" commits from a rebase are still in the object database and reachable through their reflog entry.

Finding the pre-rebase state

Look for the "rebase (start)" line. The SHA next to it is the commit HEAD was pointing at just before the rebase began. To completely undo the rebase:

bash
git reset --hard HEAD@{3}  # or use the SHA directly

Your branch is now exactly as it was before the rebase.

Recovering a specific dropped commit

Sometimes you do not want to undo the whole rebase — you only want one commit back. Find its SHA in the reflog:

bash
$ git reflog
...
2d1e0f9 HEAD@{4}: commit: fix auth edge case

Cherry-pick it onto your current branch:

bash
git cherry-pick 2d1e0f9

This replays the commit's diff as a new commit on top of your current branch. Conflicts possible; resolve and git cherry-pick --continue.

Reflog on a specific branch

By default git reflog shows HEAD's history. To see a specific branch's reflog:

bash
git reflog show feature/auth

This is useful when you switched branches between operations and want to trace a particular branch's movements.

When the Reflog Does Not Have It: git fsck

The reflog expires. By default, reachable commits expire after 90 days and unreachable ones after 30. If you are trying to recover an ancient commit, it may have fallen off.

When that happens, git fsck --lost-found walks the object database and reports every commit that is not referenced by any branch, tag, or reflog entry:

bash
$ git fsck --lost-found
Checking object directories: 100% (256/256), done.
dangling commit 7f6e5d4c3b2a1...
dangling blob 3a2b1c0d9e8f7...

Inspect each dangling commit:

bash
git show 7f6e5d4c3b2a1

If it is the commit you wanted, cherry-pick it or create a branch at it:

bash
git branch recovered-work 7f6e5d4c3b2a1

fsck is a last resort — reflog is usually sufficient — but it saves you when the reflog has aged out.

Surviving the Interactive Rebase Disaster

Interactive rebases are the most common source of "I lost commits" calls. The typical disaster:

bash
git rebase -i HEAD~10

Opens your editor. You see ten commits with "pick" next to each. You change some to "squash", accidentally delete a line ("drop"), save, quit. Git rewrites history. Your dropped commit looks gone.

The immediate recovery

bash
git reset --hard ORIG_HEAD

Done. This works as long as you have not run another destructive operation yet.

Recovering after additional operations

If you have made more commits or done another rebase since the disaster, ORIG_HEAD now points to the latest operation, not the original bad rebase. Go to the reflog:

bash
git reflog

Find the entry labeled "rebase -i (start)" — that is the SHA immediately before the bad rebase. Reset or cherry-pick from there.

Mid-rebase escape

If you are still in the middle of the interactive rebase (Git stopped on a conflict or an "edit" stop), abort:

bash
git rebase --abort

Git returns to the pre-rebase state. No damage done.

If you have partially resolved conflicts and want to keep that work, use git rebase --continue instead. --abort is specifically for throwing away the in-progress rebase.

The Force-Push Disaster and How to Recover

This is the worst case. You rebased locally, force-pushed, and now the remote has overwritten your teammate's commits — or yours.

Check the remote's reflog (if you have it)

If the remote is a self-hosted server you control:

bash
ssh git@server "cd /srv/git/repo.git && git reflog refs/heads/main"

The server keeps a reflog for the remote refs. The previous tip is right there. You can then fetch that SHA and push it back.

GitHub and GitLab recovery

GitHub retains activity for about 90 days. Three options:

  1. Events API: query https://api.github.com/repos/OWNER/REPO/events filtered for PushEvent. The before field in push events is the old ref, which you can cherry-pick or reset to.
  2. Network graph: the repo's Insights > Network tab sometimes shows orphaned branches.
  3. GitHub Support: for serious incidents, open a support ticket. They can often restore refs within the support window.

GitLab has a similar Events API and support process.

If a teammate has the old commits

Easiest recovery path: if anyone on the team still has the old commits in their local clone, they can push them back up:

bash
# on teammate's machine
git reflog
# find SHA of the old main
git push origin <sha>:main --force-with-lease

Check team Slack before going to support — usually someone has the history.

Preventing the Next Disaster

Alias --force to --force-with-lease

--force-with-lease refuses to overwrite a remote that has moved since you last fetched. If your teammate pushed between your fetch and your force-push, the push fails safely.

bash
git config --global alias.pushf "push --force-with-lease"

Now git pushf is the safe force-push.

Enable reflog for long retention

Extend the reflog expiry to give yourself more recovery runway:

bash
git config --global gc.reflogExpire "200 days"
git config --global gc.reflogExpireUnreachable "90 days"

Use a GUI during rebases

Commands like git rebase -i hide what you are about to do behind an editor. Tools that visualize the branch graph before and after make mistakes less likely:

  • GitLens for VS Code — visualizes commits, branches, and the rebase plan in a side panel.
  • Magit for Emacs — the gold standard for terminal-adjacent git UX.
  • fugitive.vim for Vim — commits to the Vim model but handles rebase gracefully.
  • lazygit — terminal UI with branch graph and interactive rebase support.
  • GitHub Desktop and Sourcetree — GUI apps that make rebase explicit.

Pair any of these with your CLI workflow and most disasters become visible before they happen. For a broader dev tool review, see our best AI tools for developers and our Cursor vs Claude Code vs Copilot comparison.

Commit often on the feature branch, rebase rarely

The more commits you have on a feature branch, the more reflog entries protect you. Micro-commits during development with a cleanup squash at the end is safer than rebasing constantly.

Back up before a big rebase

Before any complex interactive rebase, stash a backup branch:

bash
git branch backup/feature-auth-$(date +%s)

If the rebase goes wrong, git reset --hard backup/feature-auth-1699999999 recovers instantly. Delete backup branches after merge.

Useful Recovery One-Liners

bash
# Undo the last rebase/merge/reset
git reset --hard ORIG_HEAD

# See every HEAD movement in the last 30 days
git reflog --date=relative

# Find commits whose message contains a keyword
git log --all --reflog --source --remotes --oneline --grep="auth edge"

# Find dangling commits
git fsck --lost-found --no-reflogs

# Create a branch from a dangling SHA
git branch rescue 7f6e5d4c3b2a

# See what a commit changed without checking it out
git show <sha>

# Cherry-pick a range of commits
git cherry-pick abc123..def456

# Abort an in-progress rebase
git rebase --abort

# Abort an in-progress merge
git merge --abort

Real Scenarios I Have Recovered From

Scenario A: The squash-that-dropped-five-commits

Developer ran git rebase -i HEAD~20, meant to squash the bottom five commits, accidentally deleted ten lines. Force-pushed. Realized 10 minutes later.

Recovery: git reflog showed the pre-rebase SHA. git reset --hard <sha> locally, then git push --force-with-lease to restore remote. Total time: 4 minutes.

Scenario B: The teammate-wiping force push

Developer had an old local main. Rebased feature onto it. Force-pushed feature, which was fine. Later force-pushed main, wiping 3 commits teammate had pushed in the meantime.

Recovery: teammate's local main still had the commits. Teammate pushed their commits back with force-with-lease. Total time: 10 minutes. Would have been prevented by force-with-lease in the first place.

Scenario C: The amend-the-wrong-commit

Developer amended a commit to fix a typo. Discovered they had amended the wrong commit; the original commit's content was "gone".

Recovery: git reflog showed the pre-amend SHA. git show <sha> confirmed the old commit was intact in the object database. git cherry-pick <sha> replayed it onto the branch as a new commit. Total time: 2 minutes.

Scenario D: The I-deleted-the-branch

Developer deleted a local branch thinking it was merged. It was not.

Recovery: git reflog includes branch deletions. Found the SHA of the deleted branch's tip. git branch feature-auth <sha> recreated the branch at that SHA. Total time: 1 minute.

When You Cannot Recover

Rare but possible:

  • The commit was never in your local repo (someone else had it and force-pushed remote-only).
  • git gc --prune=now --aggressive has been run and the reflog has expired.
  • The disk is corrupted or the repo was deleted.

For remote-only losses, GitHub/GitLab support is your best bet. For local gc + expiry, you are genuinely stuck — this is why reflog retention matters.

The Golden Rules

  1. `git reflog` first, panic never. Almost every "I lost a commit" panic is solved by the reflog.
  2. `ORIG_HEAD` is free insurance. Know it, use it, thank Git for it.
  3. Never `--force`, always `--force-with-lease`. Alias it so you cannot forget.
  4. Back up before big rebases. A throwaway branch costs nothing.
  5. Visualize before you rebase. A GUI that shows the resulting graph catches 80 percent of mistakes.

External references:


This post is part of our developer tools coverage. For the foundational git skills, see our [advanced git tutorial](/blog/git-advanced-tutorial-2026).

Key Takeaways

  • Every commit you have ever had checked out lives in the reflog for at least 30 days — dropped, amended, and abandoned commits are almost always recoverable
  • git reflog shows the full history of HEAD movements; git reflog <branch> shows a specific branch's movements
  • ORIG_HEAD is automatically set before any rebase, merge, or reset — git reset --hard ORIG_HEAD undoes the last destructive operation in one line
  • Cherry-pick is the surgical recovery tool when you need specific commits back without wiping recent work
  • git fsck --lost-found finds dangling commits that are not referenced by any branch or reflog entry
  • Force-push disasters on remote branches are recoverable through the remote's reflog, but only if you have SSH access or the remote exposes a UI (GitHub and GitLab both do)
  • Always use --force-with-lease instead of --force, and alias rebase operations to force safer defaults

Frequently Asked Questions

I did an interactive rebase and dropped a commit. Is it gone forever?

No. Run git reflog. You will see a list of every HEAD movement with timestamps and descriptions. Find the entry just before the rebase (usually labeled "rebase (start)" or similar) and note the SHA. You can then either git reset --hard <sha> to completely undo the rebase, or git cherry-pick <sha> to pull back just the specific commit you dropped. The reflog keeps entries for 30 days by default (90 for unreachable commits), so as long as you notice within that window, you can recover.

What is ORIG_HEAD and how do I use it?

ORIG_HEAD is a reference that Git automatically sets to the previous position of HEAD before any destructive operation (rebase, merge, reset). If you just completed a rebase and realized it was wrong, git reset --hard ORIG_HEAD reverts your branch to exactly where it was before the rebase started. This is the single fastest recovery and should be your first attempt for any "I just did a rebase and it broke" situation. See the [official git documentation](https://git-scm.com/docs/gitrevisions).

How is git reflog different from git log?

git log shows the commit history of your current branch as it exists right now. git reflog shows the history of where HEAD has pointed over time in your local repository — including commits that have been rewritten, dropped, or abandoned. If a commit was ever checked out by you, it is in the reflog. Think of reflog as Git's local undo history. It is local only; it is not pushed to remotes.

I force-pushed a rebased branch and destroyed commits on the remote. Can I get them back?

Often yes. If any team member still has the old commits in their local reflog, they can push them back. If the remote is GitHub, the Events API retains refs for about 90 days and the Network/Activity tab in the repo settings sometimes shows old heads. GitLab exposes the [Events API](https://docs.gitlab.com/ee/api/events.html) similarly. You can also contact GitHub or GitLab support — they can often restore refs within a support window. For self-hosted remotes, check the server-side reflog if it is enabled.

What is git fsck --lost-found and when do I need it?

git fsck --lost-found walks every object in the repository's object database and finds commits that are not reachable from any branch, tag, or reflog entry — "dangling" objects. You need it when a commit has fallen off the reflog (rare, usually only after gc.reflogExpire days pass or after explicit git gc --prune=now). The command writes the dangling commit SHAs to .git/lost-found/ so you can inspect them with git show and cherry-pick if needed.

Why is --force-with-lease safer than --force?

git push --force overwrites the remote branch with your local version, destroying any commits that other people pushed in between. git push --force-with-lease only overwrites if the remote is still at the state you last fetched — if someone else pushed new commits, the push fails and you are forced to pull and reconcile. It is still a destructive operation, but it prevents the scenario where you rebase based on an old fetch and then wipe out teammate commits. Alias push --force to push --force-with-lease in your git config to make this the default.

My rebase is in progress and I want to bail out. How?

git rebase --abort. This terminates the in-progress rebase and returns your branch to exactly where it was before you ran git rebase. Works mid-conflict, mid-edit, any time before you complete the rebase. If you have already completed the rebase and realized it was wrong, use git reset --hard ORIG_HEAD instead. For merges, git merge --abort is the equivalent.

Which GUI tools help prevent rebase disasters?

[fugitive.vim](https://github.com/tpope/vim-fugitive) (Vim), [Magit](https://magit.vc) (Emacs), and [GitLens](https://gitlens.amod.io) (VS Code) all show a live branch graph and flag destructive operations before you run them. [GitHub Desktop](https://github.com/apps/desktop) and [Sourcetree](https://www.sourcetreeapp.com) show visual commit history with undo options. For terminal users, lazygit and tig provide quick visualization. The best prevention is always seeing what rebase will do before you commit to it — GUIs make that visible in a way the command line does not.

About the Author

E

Elena Rodriguez

Full-Stack Developer & Web3 Architect

BS Software Engineering, Stanford | Former Lead Engineer at Coinbase

Elena Rodriguez is a full-stack developer and Web3 architect with seven years of experience building decentralized applications. She holds a BS in Software Engineering from Stanford University and has worked at companies ranging from early-stage startups to major tech firms including Coinbase, where she led the frontend engineering team for their NFT marketplace. Elena is a core contributor to several open-source Web3 libraries and has built dApps that collectively serve over 500,000 monthly active users. She specializes in React, Next.js, Solidity, and Rust, and is particularly passionate about creating intuitive user experiences that make Web3 technology accessible to mainstream audiences. Elena also mentors aspiring developers through Women Who Code and teaches a popular Web3 development bootcamp.