The first time a terminal prints CONFLICT, it feels like something broke. It didn’t. Git hit two edits it couldn’t combine and passed the decision to you, which is all git merge conflict resolution really is. The answer to how to resolve merge conflicts in Git is to edit each conflicted file into its final content, stage it, and finish the operation.
Merges into a main branch, pulls and rebases cause most of these. Cherry-picks do too. The trigger never changes: both branches touched the same lines since their merge base.
Git settles every hunk it can by itself, according to the git merge documentation. Marker lines go in only around the hunks it can’t settle, so the job covers those hunks and not the whole file.
How to resolve a merge conflict in Git, step by step

Git keeps the merge open until every conflicted file is staged, so the work is really a loop over the file list.
- Run git status and read the “Unmerged paths” list. Files marked “both modified” need your decision.
- Open one conflicted file and search for the line that starts with seven less-than signs.
- Edit the region into whatever the file should contain. That can be one side, both sides, or something new. Delete all the marker lines.
- Run git diff –check to catch any marker line you left behind.
- Stage the file with git add. Repeat steps 2 to 5 for every file on the list.
- Finish with git merge –continue, then run git push once the merge commit exists.
Saving in your editor means nothing to Git. It treats a file as resolved the moment it is staged, so staging the file with git add is the signal that counts.
For that last step, the Git documentation says git merge –continue checks that an interrupted merge is actually in progress before it calls git commit. Plain git commit works too. It just won’t stop you from running it at the wrong moment.
Halfway through a messy file, git diff AUTO\_MERGE shows what you have changed so far to resolve the textual conflicts (Git documentation). Handy when you’ve lost track.
What causes a merge conflict and how do you spot one?
Git stops when both branches changed the same lines since their merge base and it can’t pick between the edits. The merge base is the common ancestor commit behind the three-way merge.
Deleting a file on one branch while the other edits it leaves no marker lines at all. Same for a rename against an edit, and for changes to a binary file.
Content conflicts
The terminal gives it away first. Git prints “CONFLICT (content): Merge conflict in” followed by the file name, then “Automatic merge failed; fix conflicts and then commit the result.” After that, git status reports “You have unmerged paths” and lists each affected file as “both modified.”
If you want to see how the two sides got here, git log –merge -p followed by a path shows diffs for the HEAD version first, then the MERGE\_HEAD version (Git documentation).
Start with git status because it names every file at once. During a conflict, git diff switches to a combined format that shows how each side differs from the file in your working tree.
Conflicts that stop the merge from starting
Sometimes git merge refuses to begin at all. The cause is uncommitted local changes that the merge would overwrite, not a clash with another developer’s work (Atlassian). You have a few ways out.
- Commit the local changes, or stash them with git stash push
- Discard them with git checkout or git reset
Clean the working tree, then retry. Merging on top of a dirty tree is exactly the situation where a later abort may not restore your original state.
How to read conflict markers and conflict styles
Everything between the opening marker and the ======= line comes from your current branch (HEAD). Everything between ======= and the closing marker comes from the incoming one.
What each marker line shows
The labels printed after the opening and closing markers name the references being merged. That tells you which branch each block belongs to, which matters more than it sounds once a file has six conflict regions.
The default style shows only the two competing versions and hides the text both branches started from. That missing base is why so many conflicts feel like guesswork.
Conflict styles and when to switch
| Style | Marker lines | Conflict region holds | |||||||
|---|---|---|---|---|---|---|---|---|---|
| merge (default) | Opening, =======, closing | Both sides, no base | |||||||
| diff3 | Adds a | line before ======= | Both sides plus the base text | ||||||
| zdiff3 | Same as diff3 | Both sides plus base, with identical edge lines moved out |
Git’s documentation describes zdiff3 as similar to diff3, except that matching lines near the start or end of the region get removed. You end up reading a smaller region, which is the whole appeal.
Switching takes one git config command: git config –global merge.conflictStyle zdiff3. The GitHub Blog’s Git 2.35 write-up dates zdiff3 to January 2022, so older installs reject the value.
Once the base is visible, the logic is simple. If the base text matches one side, the other side holds the real change and usually wins. If both sides differ from the base, you have to combine them by hand.
Embedded Git libraries can lag behind, though. In January 2022, users who set zdiff3 saw an “unknown style” error from tools built on libgit2, such as cargo-deny (git2-rs issue 795). Check which Git version your tooling bundles before touching a global setting.
And if you mangle a file mid-edit, git restore –merge followed by the path recreates the conflicted merge in that file.
Which method should you use to resolve a merge conflict?
Edit by hand when both sides hold changes you need. Pick a whole side only when the other version of that file is obsolete.
| Method | Best when | Where it fails |
|---|---|---|
| Manual edit | Both sides carry needed changes | Easy to leave a marker line behind |
| checkout –ours or –theirs | One side is obsolete for the file | Drops the other side’s clean edits too |
| git mergetool | Large files that need a base view | Leaves .orig backup files |
| IDE merge editor | Many conflicts across many files | Accept buttons discard the other side |
| GitHub web editor | Small line conflicts in a pull request | Disabled for complex conflicts |
Picking one side: –ours, –theirs and -X
The git checkout command with –ours or –theirs pulls stage 2 or stage 3 of the unmerged file, according to the Git checkout documentation. It swaps the entire file, so clean edits from the other branch inside that file vanish with it.
The merge option -X ours or -X theirs behaves differently. It favors one side only inside conflicting hunks, and changes that don’t conflict still merge from both branches.
Either way, you still need git add on the file. Choosing a side doesn’t mark it resolved.
Visual tools: mergetool, VS Code, IntelliJ IDEA
git mergetool accepts emerge, gvimdiff, kdiff3, meld, vimdiff and tortoisemerge, and git mergetool –tool-help lists what your install supports (Git documentation). It writes \*.orig backups, which is annoying. Setting mergetool.keepBackup to false stops that.
VS Code added its 3-way merge editor as a preview in the June 2022 release, version 1.69 (VS Code release notes). The Resolve in Merge Editor button opens incoming, current and result panes, and Complete Merge stages the file.
IntelliJ IDEA opens its Conflicts dialog automatically when Git reports a conflict. Accept yours and Accept theirs throw away the other branch’s changes, so use Merge when both sides matter (JetBrains Guide).
GitHub’s conflict editor
You can resolve a conflict in the GitHub web editor from the pull request page, but it only covers competing line changes.
As of October 2026, GitHub Docs says the Resolve conflicts button is deactivated for anything more complex, and those cases go to the command line. Where Copilot cloud agent is enabled, a Fix with Copilot button appears in the merge box.
How to resolve conflicts during a rebase
Resolve the conflict, stage the file with git add, then run git rebase –continue. Git replays the next commit and may stop again.
Git’s rebase documentation says it halts at the first problematic commit and leaves marker lines in the tree. Because a rebase replays your commits one at a time, a second stop is normal. It doesn’t mean you did something wrong.
Continue, skip or abort
git rebase –continue restarts the rebase once the file is resolved and staged. Git creates the replayed commit itself, so there is no separate git commit step.
git rebase –skip drops the current commit and every change it carried. Rarely the right call.
git rebase –abort puts the branch back in the state it had before the rebase started.
Ours and theirs flip
The two labels swap meaning during a rebase, and the Git documentation flags the reversal explicitly. The same swap applies to git pull –rebase.
In a merge, –ours is your current branch and –theirs is the incoming one. In a rebase it’s the other way around: –ours is the upstream you’re rebasing onto, and –theirs is your own commit being replayed.
Open the file and read it before picking a side. Muscle memory from merges is what causes the wrong pick.
Cherry-pick and stash pop
A conflicted git cherry-pick follows the same routine. Resolve, git add, then git cherry-pick –continue. If it goes badly, git cherry-pick –abort backs out and restores the earlier state.
A conflicted git stash pop behaves differently. Git prints that the stash entry is kept in case you need it again.
Resolve the files, stage them, then drop the stash entry yourself. Skip that and the old entry sits on the list until someone applies it twice.
How to abort a merge or undo a bad resolution
The right undo depends on how far the merge got. An open merge uses git merge –abort, a local merge commit uses git reset, and a pushed merge commit needs git revert.
| Where the merge stands | Command | Watch out for |
|---|---|---|
| Still open, conflicts unresolved | git merge –abort | Cannot always rebuild uncommitted edits |
| Committed, not pushed | git reset –merge ORIG\_HEAD | –hard would discard local changes |
| Committed and pushed | git revert -m 1 | Re-merging later needs a revert of the revert |
Aborting an open merge
The git merge documentation says git merge –abort tries to reconstruct the pre-merge state. If uncommitted changes existed when the merge began and you edited them further afterward, it may be unable to restore them.
So commit or stash your work before you start the merge. With a merge in progress, git merge –abort is equivalent to git reset –merge.
Undoing a committed merge
Before the push, a reset is safe. After the push, history is shared and a revert is the only polite option.
Merge and pull both leave the old branch tip in ORIG\_HEAD, so git reset can return to it before you push. The Git reset documentation says –merge keeps your local changes while –hard discards them.
Once it’s pushed, git revert with -m 1 records a new commit that undoes the merge and treats the first parent as the mainline.
The revert documentation warns that reverting a merge declares you never want the tree changes it brought in. Merging the same branch again later brings back only newer commits, unless you revert the revert first.
When the standard fix does not work: delete, binary and lock file conflicts
Some conflicts leave no usable marker lines to edit, so the usual edit-and-stage routine stalls.
| Conflict | What Git shows | Fix |
|---|---|---|
| Delete versus modify | “deleted by us” or “deleted by them” | git add to keep, git rm to delete |
| Binary file | “Cannot merge binary files” | Take one whole version |
| Lock file | Markers in a generated file | Regenerate with the package manager |
Delete versus modify
One branch removed a file that the other edited, and Git can’t tell which intent wins. GitHub Docs shows the message as “CONFLICT (modify/delete)” followed by the file name.
To keep the file, run git add on it. To drop it, run git rm instead.
Renames can surface the same way. Linode’s guide describes backing out of the merge, renaming the file in your branch to match upstream, and retrying.
Binary files
Git can’t combine two versions of an image or a PDF, so one whole file has to win. Pick it with the checkout flags, then stage it. git checkout –ours keeps your version and git checkout –theirs takes the incoming one.
Git’s merge strategy documentation adds a detail for the -X options. For a binary file, the entire contents come from the chosen side.
Lock files
As of npm@5.7.0, the npm documentation resolves lockfile conflicts by fixing any package.json conflicts first, then running npm install again (optionally with –package-lock-only).
Never line-edit a generated lockfile. If package.json itself conflicts, resolve that by hand before regenerating.
How to prevent merge conflicts and reuse earlier resolutions
The less a branch drifts from its target, the fewer overlapping edits pile up. And git rerere stops you from solving the same conflict twice.
Habits that shrink the conflict surface
Merge small branches within days, not weeks. Commit one shared formatter configuration so style-only edits never collide. Before a risky merge, try a trial run with git merge –no-commit, then back out with git merge –abort if it looks ugly.
Teams practicing trunk-based development push this furthest, merging small changes into the main line daily.
Regular pulling from the target branch keeps the merge base recent, which limits how many lines can overlap.
Reusing resolutions with git rerere
The git rerere documentation says you need to set rerere.enabled before the command does anything. The Pro Git book gives the switch: git config –global rerere.enabled true.
On the first conflict, rerere records the conflicted automerge result along with your hand resolution. When the same conflict shows up again, it applies the recorded fix. git merge runs it after a failed automerge, git commit runs it when committing a merge result, and git rebase uses the recorded information too.
The docs describe the main use case as long-lived topic branches, where the same conflicts keep returning until the branch lands.
Where rerere fails
It relies on marker lines to detect a conflict, so a file that already contains lines resembling markers may not record a resolution. It also leaves the index alone, so you still review with git diff and run git add yourself.
A wrong recorded fix is the worst case. It keeps coming back until you run git rerere forget on that path.
Treat a reapplied resolution as a draft. Read it before staging it, same as a hand edit.
Git Merge Conflict Resolution FAQ
Is a merge conflict an error?
No. Git pauses the merge because two edits overlap and a person has to pick the result.
Nothing is lost at that point. The merge commit does not exist until you create it.
Why is git push rejected after I resolved the conflict?
The remote branch moved on while you worked, so Git refuses a push that isn’t a fast-forward.
After a merge, pull again, resolve anything new, then push. After a rebase, the rewritten history needs git push –force-with-lease.
Does resolving a conflict delete the other person’s code?
Not from history. Their commits stay reachable on their branch exactly as written.
Only the file content you choose lands in the merge commit. Lines you discard can still be recovered from those original commits.
What is the ort merge strategy, and does it change how conflicts look?
Git uses ort by default when merging two branches. Marker lines and conflict styles look the same either way.
You rarely need to name it. The -s option only matters when you want a different strategy on purpose.
Verifying a Git Merge Conflict Resolution Before You Push
A Git merge conflict resolution needs two checks before the push: a review of the resolution diff and a passing test run. Git detects textual overlap only, not broken behavior.
Run git show –remerge-diff on the merge commit first. It compares the merge commit with Git’s own automatic merge, so only your resolution decisions appear. That takes seconds and catches wrong picks cheaply. Then build and test the merged tree, and push once both pass.
Two branches that edit different lines of related logic merge without a single marker line and can still fail at runtime. A conflict-free merge needs the same test run.
The trade-off is minutes of waiting before each push. Running the suite in continuous integration on the merged result moves that wait off your machine.
- VS Code Extensions That Are Actually Worth Installing - October 13, 2026
- Google Play App Signing Explained - October 11, 2026
- 5 Best NinjaOne Alternatives for Focused Endpoint Management - October 10, 2026



