Learning Objectives
By the end of this lesson you will be able to:
- Read where you are in a stopped rebase and decide what to do
- Choose correctly between
--continue,--skipand--abort - Recover a branch after a rebase you already finished and regret
- Explain why a rebase is never actually unrecoverable
First: Find Out Where You Are
A stopped rebase is not an error state. It is a pause, and Git will tell you everything about it:
git status
interactive rebase in progress; onto e2b90d1
Last commands done (2 commands done):
pick 1a77c60 test: extract payment fixture builder
squash 9d3e072 fix
Next commands to do (2 remaining commands):
pick 4c1f8ab test: add refund boundary cases
pick 7b2e551 docs: update the test README
You are currently rebasing branch 'test/PAY-4417' on 'e2b90d1'.
Unmerged paths:
both modified: tests/payments/refund.spec.ts
Everything you need is there: what has been done, what remains, and what is blocking. Read this before typing anything. It is the most useful output in the whole module and most people scroll past it.
The Three Exits
--continue
You resolved the conflict, or amended the commit, and want to proceed.
git add tests/payments/refund.spec.ts
git rebase --continue
Requirement: nothing may still be unmerged. If a file still has conflict markers, Git refuses and says so.
--skip
Discards the commit currently being applied and moves to the next one.
git rebase --skip
This is right in exactly one common situation: the commit's changes are already present in the new base. Someone cherry-picked your fix into main last week, so replaying it produces a conflict against an identical change. Skipping it is correct — the change is not lost, it is already there.
It is wrong whenever you have not verified that. --skip throws the commit away, and it is the one option in this lesson that loses work if you guess.
--abort
git rebase --abort
Cancels everything and restores the branch exactly as it was before the rebase started. Always available, always complete, never a bad idea when you are unsure. It is the answer to "I don't understand what is happening" every time.
The Decision, In One Table
| Situation | Command |
|---|---|
| Conflict resolved and staged | --continue |
Amended the commit after edit |
--continue |
| This commit's change is already in the base | --skip |
| I don't understand this conflict | --abort |
| The plan was wrong to begin with | --abort, then start over |
Notice that two of the five rows are --abort. Aborting and running a simpler rebase is almost always faster than fighting a complicated one.
After the Rebase Finished — and You Regret It
--abort is only available during a rebase. Once it has completed, the branch has already been rewritten. This is where people panic, and it is completely unnecessary.
git reset --hard ORIG_HEAD
ORIG_HEAD is a reference Git sets automatically before any operation that moves the branch dramatically — rebase, merge, reset. It holds where you were. One command puts the branch back.
If you have run other commands since and ORIG_HEAD has moved on, the reflog has the full record:
git reflog
d4a91e0 HEAD@{0}: rebase (finish): returning to refs/heads/test/PAY-4417
d4a91e0 HEAD@{1}: rebase (squash): test: extract payment fixture builder
1a77c60 HEAD@{2}: rebase (start): checkout e2b90d1
7b2e551 HEAD@{3}: commit: docs: update the test README ← the branch before the rebase
git reset --hard HEAD@{3}
The whole branch, exactly as it was. This is Module 5's subject, and it is the reason nothing in this module is genuinely dangerous — as long as you have not already force-pushed.
Pro Tip: Before a rebase you are nervous about,
git branch backup/pre-rebase. It costs nothing, it makes recovery agit reset --hard backup/pre-rebase, and you delete it thirty seconds later when the rebase turns out fine.
The One Real Trap
The recovery paths above are all local. Once you have force-pushed a bad rebase and a colleague has fetched it, you are no longer recovering your own mistake — you are coordinating a fix with another person.
So: rebase, check the result, then push.
git log --oneline --graph main..HEAD # is this the branch I meant?
git diff main...HEAD # is the final content still right?
git push --force-with-lease
The git diff main...HEAD step is worth the second it takes. It shows the cumulative change of your whole branch, which a correct history-rewrite leaves identical to what it was before. If that diff changed, you dropped or mis-squashed something.
Key Takeaways
git statusduring a rebase reports what is done, what remains, and what blocks it--continueafter resolving;--skiponly when the change is already in the base;--abortwhenever unsure--skipdiscards a commit — it is the one option that loses work- After a finished rebase,
git reset --hard ORIG_HEADundoes it - The reflog recovers the branch even after
ORIG_HEADhas moved on - A backup branch before a risky rebase costs nothing
- Check
git diff main...HEADbefore force-pushing: a clean rewrite leaves it unchanged