Undo or Abort a Git Merge, and Resolve Merge Conflicts

A merge can go wrong in three ways. You merge the wrong branch, Git stops with conflicts that you do not want to resolve now, or Git refuses to merge at all. Each case has a short answer, and the right one depends on two things: did the merge finish, and did you push it? This page answers each question in turn: undo a merge, abort a merge, resolve the conflicts, and fix "refusing to merge unrelated histories".
TLDR
| Question | Short answer |
|---|---|
| Undo a merge that is not pushed | git reset --merge ORIG_HEAD right after the merge, or git reset --merge HEAD~1 if the merge commit is the newest commit |
| Undo a merge that is already pushed | git revert -m 1 <merge-commit>, then push the new commit |
| Stop a merge that has conflicts | git merge --abort |
| Resolve a conflict | Edit the file, remove the markers, git add <file>, then git commit |
| Take one side for a whole file | git restore --ours <file> or git restore --theirs <file>. The two swap during a rebase |
| "refusing to merge unrelated histories" | Check the remote URL first. Then add --allow-unrelated-histories to the merge, or rebase |
Prerequisites
- Git 2.23 or later for
git restoreandgit merge --quit. The output on this page comes from Git 2.39. The Introduction to Git guide uses Git 2.56, and the commands are the same. - A repository where you can practice. To try the commands without risk, make a copy of the repository first, or use the Git merge conflict simulator in your browser.
How Do I Undo a Git Merge That Has Not Been Pushed?
Run git reset --merge ORIG_HEAD right after the merge. A merge sets ORIG_HEAD to the commit your branch pointed at before the merge, so this command moves the branch back there:
git merge feat
git reset --merge ORIG_HEAD
A merge that made a merge commit looks like this before and after the reset:
Before the reset: After the reset:
F1---F2 feat F1---F2 feat
/ \ /
A---B---------M main A---B main
Use --merge, not --hard. Both move the branch back and remove the merged changes. The difference is your uncommitted work. --merge keeps unstaged changes in files that the merge did not touch. --hard deletes them. Neither keeps changes that you staged after the merge, so commit or stash those first. In this test, notes.txt had an unstaged edit when the merge was undone:
If Git did a fast-forward merge, there is no merge commit. ORIG_HEAD still points at the old tip, so the same command works.
What if I ran other commands after the merge?
ORIG_HEAD changes each time you run reset, rebase, pull or another merge. Find the old position in the reflog instead:
git reflog -5
b88d01b HEAD@{0}: merge feat: Merge made by the 'ort' strategy.
614c03d HEAD@{1}: commit: B
The line below the merge is your branch before the merge. Reset to it with git reset --merge HEAD@{1}, or with its hash. If the merge commit is the newest commit on the branch, git reset --merge HEAD~1 also works: HEAD~1 follows the first parent of the merge, which is your branch before the merge.
How do I remove a merge but keep the commits I made after it?
Replay the later commits onto the first parent of the merge. Here M is the hash of the merge commit, and C and D came after it:
git log --oneline --graph -6 # find the hash of the merge commit
git rebase --onto M^1 M # replay the commits after M onto the parent before M
Before: A---B---M---C---D main After: A---B---C'---D' main
\ /
F1
Do not use git rebase -i for this. The todo list of an interactive rebase does not show merge commits. It shows the commits of the merged branch instead, and if you save the list without changes, Git copies those commits into a straight line. The merge is gone, but its changes are still there.
How do I keep the changes but drop the merge commit?
Run git reset --soft HEAD~1. The branch moves back, and the merged result stays staged. Your next git commit records it as one normal commit with one parent. If you want this result from the start, use git merge --squash feat and then git commit. The Git merge guide compares the merge types.
How Do I Undo a Merge That Is Already Pushed?
Do not reset a branch that other people have pulled. Make a new commit that reverses the merge, and push it:
git log --oneline --merges -5 # find the merge commit
git revert -m 1 9f3c2ab # 9f3c2ab is the merge commit
git push origin main
A merge commit has two parents, so Git needs to know which side to keep. -m 1 keeps the first parent, which is the branch you merged into. The revert removes the changes that came from the other branch.
After you revert a merge, Git still considers the commits of that branch as merged. If you fix the branch and merge it again, Git does not bring back the changes that the revert removed. Revert the revert first (git revert <hash-of-the-revert-commit>), then merge the new fixes. The Git project describes this case in How to revert a faulty merge.
The history chapter of the Git guide compares revert and reset with real output.
How Do I Abort a Merge With Conflicts?
Run git merge --abort. Git stops the merge, removes the conflict markers and returns your branch and files to the state before git merge. Any conflicts that you already resolved are lost too:
git merge feature
# CONFLICT (content): Merge conflict in src/app.js
# Automatic merge failed; fix conflicts and then commit the result.
git merge --abort
git status
# On branch main
# nothing to commit, working tree clean
git merge --abort works only while a merge is in progress. If the merge already finished, undo it with the reset from the first section. Without a merge in progress, Git prints:
fatal: There is no merge to abort (MERGE_HEAD missing).
Some details to know:
- Commit or stash before you merge. Git refuses to start a merge that would overwrite uncommitted changes. If other files had uncommitted changes,
git merge --aborttries to keep them, but the git merge documentation warns that it cannot always rebuild them. - A conflicted
git pullis a merge or a rebase. Aftergit pullstops with conflicts, rungit merge --abort. If your pull rebases (pull.rebase trueorgit pull --rebase), rungit rebase --abort. - Each operation has its own abort. Use
git rebase --abort,git cherry-pick --abortandgit revert --abortfor those operations. - Leave the files as they are.
git merge --quitforgets that a merge is in progress but does not change your files. The conflict markers stay, andgit statusstill shows the files as unmerged. It is for the rare case where you want to keep the half-merged files.
To check for a merge in progress in a script, test for MERGE_HEAD. This also works in worktrees, where .git is a file and not a directory:
if git rev-parse -q --verify MERGE_HEAD >/dev/null; then
echo "A merge is in progress"
fi
To see the result of a merge before you commit to it, merge without a commit, look, then abort or commit:
git merge --no-commit --no-ff feature
git diff --staged # the changes the merge would bring
git merge --abort # or: git commit
How Do I Resolve Merge Conflicts in Git?
Open each conflicted file, edit it to the content you want, remove the conflict markers, and stage it. When all files are staged, commit:
git status # lists the files under "Unmerged paths"
# edit src/config.js
git add src/config.js
git commit # or: git merge --continue
A conflict happens when both branches changed the same lines, or when one branch changed a file that the other deleted. Git marks the lines in the file:
const config = {
<<<<<<< HEAD
apiUrl: 'https://api.example.com',
timeout: 5000,
=======
apiUrl: 'https://api.example.com',
timeout: 3000,
retries: 3,
>>>>>>> feature
};
The part between <<<<<<< HEAD and ======= is your current branch. The part between ======= and >>>>>>> feature is the branch you merge. The resolved file can be one side, the other side, or a combination. Here the team keeps the timeout of main and the new retries setting:
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retries: 3,
};
See the original lines too
With the default markers, you see the two new versions but not the line they both changed. Set the zdiff3 conflict style (Git 2.35 and later), and Git adds the common ancestor after |||||||:
git config --global merge.conflictStyle zdiff3
<<<<<<< HEAD
MAIN
||||||| dc9acde
base
=======
FEAT
>>>>>>> feat
Now you can see that the original line was base, and that both branches changed it.
Take one side for a whole file
To keep one version of a file and drop the other, restore that side and stage it:
git restore --ours -- src/config.js # the version of your current branch
git restore --theirs -- src/config.js # the version of the branch you merge
git add src/config.js
git checkout --ours -- <file> does the same in older Git versions. Use these only when you are sure that the other side has nothing you need. The changes on the other side are lost for that file.
During a rebase, ours and theirs swap. A rebase replays your commits on top of the other branch, so "ours" is the branch you rebase onto, and "theirs" is your own commit. In a test, git restore --ours during git rebase main gave the version of main, not the version of the feature branch.
Check for leftover markers before you commit
Git does not stop you from committing a file that still has conflict markers. git diff --check finds them:
git diff --cached --check
# src/config.js:1: leftover conflict marker
Then run your tests. Two changes can merge without a conflict and still break the build together.
Use a merge tool
git mergetool opens each conflicted file in a merge tool. git mergetool --tool-help lists the tools that Git knows on your system, such as meld, kdiff3 and vimdiff. To use VS Code, define it as a custom tool, as the VS Code documentation shows:
git config --global merge.tool code
git config --global mergetool.code.cmd 'code --wait --merge "$REMOTE" "$LOCAL" "$BASE" "$MERGED"'
See how a past conflict was resolved
git show --remerge-diff <merge> (Git 2.36 and later) shows the difference between the automatic merge result, with its conflict markers, and the commit that someone made:
git show --remerge-diff HEAD
@@ -1,5 +1 @@
-<<<<<<< 6ed13fc (main)
-MAIN
-=======
-FEAT
->>>>>>> 3841f85 (feat)
+MAIN and FEAT
If you resolve the same conflict again and again, for example on a long rebase, turn on rerere. Git then records each resolution and applies it the next time.
Make conflicts smaller
- Keep branches short, and merge
maininto them (or rebase them onmain) often. - Do large refactors on their own branch, and merge them fast.
- Make small commits. A small change conflicts less, and its conflict is easier to read.
To practice, the Git merge conflict simulator gives you conflicts to resolve, with the three index stages, --ours and --theirs, and a rebase. The merge chapter of the Git guide resolves a conflict step by step.
How Do I Fix "refusing to merge unrelated histories"?
Git says this when the two branches have no commit in common. First check that you pull from the right repository. If you do, allow the merge once, or rebase your commits on top:
git remote -v # is this the repository you mean?
git fetch origin
git merge --allow-unrelated-histories origin/main
# or: git pull --no-rebase --allow-unrelated-histories origin main
The usual cause is a new project. You run git init and commit locally, and you also create the repository on GitHub or GitLab with a README. Each side now has its own first commit, and neither is an ancestor of the other. git merge-base finds no common commit:
git merge-base HEAD origin/main
echo $? # 1: no common commit
On Git 2.39 with no pull.rebase setting, a plain git pull origin main stops even earlier, with fatal: Need to specify how to reconcile divergent branches. Add --no-rebase to see the real error, or --rebase to rebase.
When both sides have a README, the merge stops with an add/add conflict. Resolve it like any other conflict:
Auto-merging README.md
CONFLICT (add/add): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.
Rebase instead of a merge
To put your local commits on top of the remote history without a merge commit, rebase. git rebase has no --allow-unrelated-histories option, and it does not need one. It copies your commits onto the other branch:
git fetch origin
git rebase origin/main # resolve any add/add conflict, then: git rebase --continue
git push -u origin main
When not to allow it
Stop and check if you are not sure why the histories differ:
- Wrong remote URL.
git remote -vshows another project. Fix it withgit remote set-url origin <url>(more on remote URLs). - You want the remote and not your local work. Clone the repository again, or fetch and reset:
git fetch originthengit reset --hard origin/main. This discards your local commits. - You want your local history and not the remote one. If the remote holds only the generated README, overwrite it with
git push --force-with-lease origin main. Never do this on a repository that other people use.
To avoid the problem, create the remote repository empty (no README, license or .gitignore), or clone it first and add your files to the clone.
Combine two projects into one repository
To bring another project into a subdirectory and keep its history, use git subtree. It ships with most Git packages:
git subtree add --prefix=billing https://github.com/acme/billing.git main
This adds the other project under billing/ with one merge commit. Compare subtrees and submodules in the advanced Git guide.
Related Resources
Try it hands-on
Run the commands from this article in the browser. Nothing to install.
Git Concepts Simulator
Practice Git in an interactive browser terminal with a visual repository model. Learn the working directory, staging area, local commits, branch pointers, remotes, fast-forward merges, fetch, and rebase.
Git Merge Conflict Simulator
Resolve Git merge conflicts in a browser lab. Read the conflict markers and the three index stages, edit the file to what it should be, take a side with --ours or --theirs, see why they swap during a rebase, back out with git merge --abort, and catch a clean merge that breaks the build.
We earn commissions when you shop through the links below.
Bitrise
Managed runners for GitHub Actions
Bitrise Build Hub runs your GitHub Actions jobs on M4 Macs and AMD Linux machines. Change runs-on and keep your workflows, secrets and logs on GitHub.
Atomsized
AWS platform engineering and GitOps
Design and automation for reliable AWS and Kubernetes platforms, safer delivery workflows, and preview and UAT environments your engineers can understand and own.
DigitalOcean
Cloud infrastructure for developers
Simple, reliable cloud computing designed for developers
DevDojo
Developer community & tools
Join a community of developers sharing knowledge and tools
SMTPfast
Developer-first email API
Send transactional and marketing email through a clean REST API. Detailed logs, webhooks, and embeddable signup forms in one dashboard.
QuizAPI
Developer-first quiz platform
Build, generate, and embed quizzes with a powerful REST API. AI-powered question generation and live multiplayer.
Want to support DevOps Daily and reach thousands of developers?
Become a SponsorTags
Found an issue?
Related Posts
Also worth your time on this topic
Delete a Commit from a Git Branch: Reset, Rebase or Revert
Remove the last commit with git reset --hard HEAD~1, drop an older one with git rebase -i, or undo a pushed commit safely with git revert <commit-hash>.
Git Workflow and Collaboration Fundamentals
Master essential Git workflows, branching strategies, and collaboration techniques used in modern development teams.
50 minutes
Git Staging and Committing
Explain the Git staging area. What is the difference between git add, git commit, and git push?
junior