What you will achieve
You will deliberately mess up a rebase, then recover using the reflog and ORIG_HEAD. The recovery patterns work for any rewrite gone wrong - botched interactive rebase, accidental drop, mid-rebase confusion.
Setup: a sandbox with real commits
mkdir rebase-recovery && cd rebase-recovery
git init
for i in 1 2 3 4 5; do
echo "$i" > file.txt
git add . && git commit -m "Commit $i"
done
git log --oneline
# 5 commits, ready to break
Disaster 1: accidentally dropped commits
Run an interactive rebase and "drop" a commit:
git rebase -i HEAD~4
# Editor opens. Change "pick" to "drop" for one commit. Save.
Verify the loss:
git log --oneline
# one fewer commit than before
Recovery 1: ORIG_HEAD
Git automatically saves the pre-rebase tip in ORIG_HEAD:
git reset --hard ORIG_HEAD
git log --oneline
# all 5 commits restored
Disaster 2: rebase conflict resolution gone wrong
Trigger conflicts and resolve them badly:
git checkout -b feature HEAD~2
echo "feature change" >> file.txt
git commit -am "Feature change"
git rebase main
# conflicts
Suppose you resolved by keeping the wrong side and continued. Now your branch has wrong content.
Recovery 2: reflog
git reflog
# HEAD@{0} rebase finished
# HEAD@{1} rebase: applying ...
# HEAD@{2} rebase (start): checkout main
# HEAD@{3} commit: Feature change <-- pre-rebase tip
git reset --hard HEAD@{3}
You are back to the pre-rebase state. Re-rebase, this time with care.
Disaster 3: rebase, then more work, then realise rebase was wrong
You rebased, made several commits, then realised the rebase introduced a regression. ORIG_HEAD is overwritten by the new commits. Use the reflog:
git reflog
# find the SHA before "rebase (start)"
git reset --hard <sha-before-rebase>
You will lose the post-rebase commits. Cherry-pick the ones you want to keep first:
# From the bad state, save the good commits onto a temp branch:
git branch save-work HEAD~2..HEAD # creates a branch at the good commits
git reset --hard <sha-before-rebase>
git cherry-pick save-work
git branch -D save-work
Disaster 4: force-pushed the bad rebase
You rebased badly and force-pushed before noticing. The remote has the broken history. Recovery options:
- Local reflog still has the original. Reset and force-push again, restoring the good state.
- A teammate has a clone with the original. They push their copy back.
- The host has a "branch protection" log or activity log. Some hosts retain pushed SHAs for hours; support can sometimes recover them.
# Local recovery
git reflog
git reset --hard <good-sha>
git push --force-with-lease
Disaster 5: rebase ate the wrong parent
You rebased a feature branch onto the wrong base. The branch now has unrelated commits at its root. Reset to ORIG_HEAD and re-rebase onto the correct base:
git reset --hard ORIG_HEAD
git rebase main # the correct base
Preventing future disasters
Make a backup branch before risky operations
git branch backup-before-rebase
git rebase -i HEAD~10
# If anything goes wrong:
git reset --hard backup-before-rebase
git branch -D backup-before-rebase
Increase reflog expiry
git config gc.reflogExpire 365.days
git config gc.reflogExpireUnreachable 90.days
Push before rebasing
If you push the branch (perhaps to a backup remote) before rebasing, the original commits are preserved off-machine.
git push backup HEAD:my-branch-backup
git rebase ...
Enable rerere
git config --global rerere.enabled true
"Reuse recorded resolution" remembers conflict resolutions so a re-do of the rebase resolves them automatically.
The mental model
Rebase rewrites commits, but the originals do not vanish - they linger as orphan commits in the object database, reachable via reflog for ~90 days. git reset --hard moves the branch pointer back; the commits are still there. Recovery is almost always one git reflog away.
The discipline
Botched rebase is not a disaster; it is a five-minute setback. Practise the recovery once on a sandbox, and the panic disappears for good.