Introduction
We've all been there: a misplaced commit, a branch that vanishes, or a force-push that overwrites a colleague's work. Recovering from Git disasters for Git & GitHub developers doesn't require panic—it requires knowing where Git keeps its safety net. Git is remarkably forgiving because it almost never truly deletes anything; understanding how to navigate the reflog, reset operations, and recovery workflows transforms scary moments into routine fixes.
This guide walks through four of the most common Git disasters we encounter in production teams and the calm, methodical steps to recover from each.
Deleted a branch — Find the commit in reflog and recreate the branch
Accidentally deleting a branch feels catastrophic until you remember that Git tracks every ref movement in the reflog. When you delete a branch locally with git branch -D branch-name or see it disappear after a force-push, the commits aren't gone—the pointer is.
Let's say your teammate deleted the feature/payment-integration branch:
git branch -D feature/payment-integrationFirst, check your local reflog to find where that branch was pointing:
git reflogYou'll see output like:
a1b2c3d (HEAD -> main) commit: Merge pull request #487
f4e5d6c feature/payment-integration: Refactor checkout flow
e7d8c9b feature/payment-integration: Add Stripe webhook handler
d0c1b2a main: Update dependencies
Find the commit hash where feature/payment-integration was last pointing. In this example, that's f4e5d6c. Recreate the branch at that commit:
git branch feature/payment-integration f4e5d6cPush it back to the remote if needed:
git push origin feature/payment-integrationIf the branch was already deleted from the remote, you can still recover it locally from the reflog, but the remote won't have the history. This is why teams should adopt branch protection rules and avoid force-deleting branches without confirmation.
Real-world scenario: A developer runs a cleanup script that deletes all local branches merged into main, including one that was never pushed. Instead of hunting through GitHub's UI or asking for a backup, the reflog has the answer in seconds.
Bad rebase — git reset --hard ORIG_HEAD or use reflog
Rebasing is powerful but risky. A rebase conflict, an accidental abort, or a misunderstood merge strategy can leave your branch in a broken state or with commits vanished.
Imagine you're rebasing feature/analytics onto the latest main:
git rebase mainHalfway through, you realize you made a mistake—perhaps you resolved conflicts incorrectly, or the rebase has tangled your commits. The simplest recovery is:
git rebase --abortThis takes you back to the state before the rebase started. If you've already completed the rebase but regret it, Git sets ORIG_HEAD to point to the commit before the last dangerous operation:
git reset --hard ORIG_HEADThis works for rebases, merges, resets, and checkouts. It's your immediate undo button.
If you've already moved forward with other commands and ORIG_HEAD is stale, the reflog is your backup:
git reflogFind the commit hash from before the rebase:
git reset --hard a1b2c3dReal-world scenario: A developer rebases interactively to clean up commits, accidentally squashes the wrong commits together, and ends up with a broken feature. A single git reset --hard ORIG_HEAD restores the state before the rebase, and they can try again more carefully.
Committed to the wrong branch — Cherry-pick to the right branch, reset the wrong one
You're working in a feature branch, make several commits, and suddenly realize you've been committing to main or a different feature branch entirely. Don't panic; cherry-pick solves this cleanly.
Suppose you've committed directly to main when you meant to work in feature/dark-mode. First, check which commits are in the wrong place:
git log --oneline -5 mainOutput:
a1b2c3d Dark mode: Add toggle component
f4e5d6c Dark mode: Update theme provider
e7d8c9b Dark mode: Add CSS variables
d0c1b2a Previous release
The last three commits belong in feature/dark-mode, not main. Check out the correct branch and cherry-pick those commits:
git checkout feature/dark-mode
git cherry-pick d0c1b2a..a1b2c3dNow go back to main and reset it to before those commits:
git checkout main
git reset --hard d0c1b2aIf you've already pushed the wrong commits to the remote, coordinate with your team before force-pushing:
git push origin main --force-with-leaseThe --force-with-lease flag is safer than --force because it checks that the remote hasn't changed since your last fetch.
Real-world scenario: In a busy day, a developer forgets to create a new branch and codes directly on main for two hours. Instead of reverting and re-doing the work, cherry-pick moves those commits to the right place in seconds.
Force-pushed over work — Teammates' local copies or reflog can recover it
Force-pushing is the most dangerous operation in Git. When someone force-pushes to a shared branch, they can overwrite commits that teammates have based work on.
Let's say your teammate force-pushed to develop and overwrote three commits that you were building on:
git push origin develop --forceIf you have the branch checked out locally, your local copy still has those commits. First, check your local reflog:
git reflog developYou'll see the old commits before the force-push:
a1b2c3d develop: Merge fixes for auth module
f4e5d6c develop: Update database schema
Create a new branch from the old commit to preserve it:
git branch develop-backup a1b2c3dNow fetch the new version from the remote and reset your local branch:
git fetch origin
git reset --hard origin/developIf you've already pushed your work on top of the old commits, you need to cherry-pick your commits onto the new develop:
git log --oneline develop-backup..HEADThis shows commits in your current branch not in the backup. Cherry-pick them back in:
git cherry-pick <commit-hash>If the force-push was a mistake and you need to recover the old version entirely, GitHub's reflog (when enabled) shows recent pushes. On GitHub Enterprise or with administrative access, the reflog can recover force-pushed commits for a limited time (typically 90 days).
Real-world scenario: A deployment pipeline auto-pushes to a shared branch and overwrites a developer's unmerged changes. The developer's local copy has the commits; they cherry-pick their work onto the new branch state and re-create their pull request.
Common mistakes
- Assuming commits are lost: Git rarely deletes commits. Before panicking, check the reflog. If it's not in the reflog, it's likely already garbage-collected (which takes weeks), but it's worth checking first.
- Using git reset --hard without understanding what you're resetting: Always verify which commit you're resetting to. A typo here can undo real work. Use
git logto double-check the hash before running the reset. - Force-pushing without communication: Always let teammates know before force-pushing to a shared branch. Better yet, use
--force-with-leaseto add a safety check, and establish team practices that make force-pushing rare. - Not using branch protection rules: On GitHub, protect main and develop branches to require pull requests and block force-pushes. This prevents many disasters before they happen.
- Forgetting that reflog is local: Your reflog doesn't recover commits that only existed on a teammate's machine or the remote. This is why local backups and communication matter.
Key takeaways
- The reflog is Git's safety net; check it first with
git reflogorgit reflog <branch-name>. - Use
git reset --hard ORIG_HEADto undo the last dangerous operation (rebase, merge, reset, checkout). - Cherry-pick moves commits from the wrong branch to the right one without re-doing the work.
- Always use
--force-with-leaseinstead of--forceto add a safety check when force-pushing. - Coordinate with teammates before dangerous operations, and use branch protection rules to prevent common disasters.
- Local copies of branches preserve commits even after remote force-pushes; create a backup branch before resetting.






Comments
Loading comments…