Recover from a failed merge or rebase
Advanced Git CLI
Why this matters
The worst moment in Git is being halfway through something you do not understand, with the shell prompt showing a state you did not ask for. The fix is one command, and knowing it in advance turns a conflict from an emergency into a task.
The escape hatches
| You are in | Get out with | Result |
|---|---|---|
| A merge with conflicts | git merge --abort |
Exactly where you were before the merge |
| A rebase with conflicts | git rebase --abort |
Exactly where you were before the rebase |
| A cherry-pick | git cherry-pick --abort |
Same |
| A revert that conflicted | git revert --abort |
Same |
All four are safe and complete: they restore the branch, the staging area and your files to the state before the operation started. Nothing is lost, because nothing had been committed yet.
Where am I?
git status always says, in its first two lines. During a merge:
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
(use "git merge --abort" to abort the merge)
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: docs/faq.mdDuring a rebase:
interactive rebase in progress; onto c9c26d7
Last command done (1 command done):
pick 9e420d2 docs: clarify the licence answer
No commands remaining.
You are currently rebasing branch 'docs/licence-wording' on 'c9c26d7'.
(fix conflicts and then run "git rebase --continue")
(use "git rebase --skip" to skip this patch)
(use "git rebase --abort" to check out the original branch)Both messages contain the commands you need. Git is not being cryptic here; it is telling you the three options, and one of them is always "go back to how it was".
Aborting a merge
$ git merge docs/licence-wordingAuto-merging docs/faq.md
CONFLICT (content): Merge conflict in docs/faq.md
Automatic merge failed; fix conflicts and then commit the result.$ git merge --abort
$ git status --short
$ git log --oneline -1c9c26d7 docs: shorten the licence answer
Clean tree, same commit, as though the merge never happened. Aborting is a perfectly respectable choice: it buys you time to ask the other author what they meant before resolving (Section 11).
Rebase: continue, skip or abort
A rebase replays your commits one at a time, so it stops at each one that conflicts:
CONFLICT (content): Merge conflict in docs/faq.md
error: could not apply 9e420d2... docs: clarify the licence answer
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".| Option | Use when |
|---|---|
git rebase --continue |
You resolved the conflict and staged the files. Git commits and moves to the next one |
git rebase --skip |
This commit's change is genuinely no longer needed, usually because the same fix is already on the new base |
git rebase --abort |
You want to stop and think. Nothing is lost |
--skip is the one to be careful with: it drops that commit entirely. Use it only when you have looked at the change and confirmed it is already present.
$ git rebase --abort
$ git log --oneline -29e420d2 docs: clarify the licence answer
d3b92ec chore: add CI configuration and contributing guideBack to the original branch, with the original commits and their original hashes.
Undoing a merge that is already committed
Once the merge commit exists, the escape hatches no longer apply and the pushed question decides again.
Not pushed: the merge is the last commit, so move the branch back:
$ git reset --hard ORIG_HEADORIG_HEAD is set by the merge itself, which is exactly what it is for.
Pushed: revert it. A merge commit has two parents, so Git needs to know which side to keep:
$ git revert HEADerror: commit 85fd3d5f2a165b6f0e3bc4f9c9fb7c18a7ef5174 is a merge but no -m option was given.
fatal: revert failed$ git revert -m 1 HEAD[main 0abc660] Revert "Merge branch 'docs/licence-wording'"
1 file changed, 1 insertion(+), 1 deletion(-)-m 1 means "keep the first parent", which is the branch you merged into, normally main. In other words, undo the incoming changes and keep the branch as it was.
How to do it
Four commands cover the whole lesson:
$ git merge --abort
$ git rebase --abort
$ git reset --hard ORIG_HEAD # merge already committed, not pushed
$ git revert -m 1 <merge hash> # merge already pushedWhen in doubt, run git status and read its parenthetical hints; they name the right command for the state you are in.
During a conflict the Source Control view shows Merge Changes and offers Abort Merge in the ⋯ menu. A rebase in progress shows Continue, Skip and Abort buttons in the same place.
A conflict opens the Conflicts dialog with Abort available. During a rebase, the Git tool window shows a Rebasing banner with Continue, Skip Commit and Abort actions, and the status bar names the branch being rebased.
A merge request with conflicts shows them on the page, with a Resolve conflicts editor for simple cases. If the resolution is complex, GitLab tells you to do it locally, and the commands it shows are the ones in this lesson. Undoing a merged merge request is the Revert button, which is git revert -m 1 performed for you.
The same: a Resolve conflicts editor for simple cases, and a Revert button on a merged pull request that opens a new pull request undoing the merge.
Common mistakes
- Continuing a rebase without staging the resolved files, which Git refuses with a message about unmerged paths.
- Using
--skipto make a conflict go away, which silently drops that commit's change. - Forgetting
-m 1when reverting a merge, then not reading the error, which says exactly what is missing. - Panicking and deleting the repository folder. The escape hatches always work, and a clone is not a backup of your unpushed work.
- Resolving the same conflict repeatedly in a long rebase instead of aborting and merging.
Try it yourself
Goal: create a conflict, escape it twice, then undo a completed merge.
- On two branches, change the same line of
docs/faq.mddifferently and commit both. - Merge one into the other, see the conflict, then run
git merge --abortand confirm the tree is clean. - Try again with
git rebaseinstead, read the status message in full, and rungit rebase --abort. - Now complete the merge: resolve the conflict,
git add,git commit. - Undo it with
git reset --hard ORIG_HEAD, and confirm the merge commit is gone.
Expected result: two clean escapes and one completed merge undone, with the branch back at the commit it started from each time.
Show solution
Step 3's status output is the one to read carefully: it names the branch, the commit it is replaying, and all three options. Every mid-operation state in Git announces itself the same way, so getting used to reading it is worth more than memorising any single command.