Resolving merge conflicts
Git stopped the merge and printed CONFLICT. Learn why it happens, what the file markers mean, and the exact steps to fix it safely.
What you'll learn
- Why Git raises a conflict: the same lines changed on both branches and Git cannot auto-pick a winner
- How to read a conflicted file: the three sections separated by conflict markers, and what HEAD vs. incoming means
- The resolution loop: edit the file, remove every marker, git add, then git commit
Before you start
Why conflicts happen
Git is good at merging. When two branches each change different lines, Git combines them silently and moves on. A conflict happens only when both branches change the same lines — at that point Git has two valid candidates for the same content and no policy for choosing between them, so it stops and hands the decision back to you.
Here is the classic setup: main and feature both edited the same line in greeting.py. When you merge feature into main, Git cannot auto-resolve the overlap.
What a conflicted file looks like
When a conflict occurs, Git rewrites the affected file in place, embedding conflict markers to show you both versions side by side. Here is a real example:
def greet(name):
<<<<<<< HEAD
greeting = "Hi there"
=======
greeting = "Hey!"
>>>>>>> feature
return f"{greeting}, {name}!"
Breaking down the three sections:
- Everything between the opening seven-less-than marker and the separator line of equals signs is the HEAD side — the content from your current branch (the one you ran
git mergefrom). - Everything between the separator and the closing seven-greater-than marker is the incoming side — the content arriving from the branch you are merging in.
- Lines outside both marker pairs were not in conflict and are already correct.
The labels after the opening and closing markers are branch names (or commit SHAs when merging a detached HEAD).
Checking the status
Before touching anything, confirm which files need attention:
git status
Git lists conflicted files under “Unmerged paths”. Every file in that list must be resolved before the merge can complete. Files Git handled automatically are already staged; leave those alone.
The resolution loop
Resolving a conflict is a four-step loop, repeated for every conflicted file:
1. Open the file and decide on the final content.
Pick one side, combine ideas from both, or write something entirely new — whatever is correct. There is no rule that says you must keep either version verbatim.
2. Remove every conflict marker.
Delete the opening marker line, the separator line, and the closing marker line. The file must contain only valid code when you are done.
After editing, the function from the example above might look like:
def greet(name):
greeting = "Hi there, hey!"
return f"{greeting}, {name}!"
3. Stage the resolved file.
git add greeting.py
4. Repeat for any remaining conflicted files, then commit.
git status # confirm no more "Unmerged paths"
git merge --continue # or: git commit
Git opens your editor for the merge commit message (a default is pre-filled). Save and close to finish.
Aborting when you are not ready
If you started a merge and realized you need to stop — maybe to ask a teammate or check something first — you can return the repository to its pre-merge state:
git merge --abort
This is safe to run at any point before the merge commit is created. After --abort, your working tree and index are restored to exactly what they were before you ran git merge.
Summary
Conflicts only happen on overlapping changes. The resolution loop is always the same: read the markers to understand both sides, edit the file to the correct final state, remove every marker, stage the file, and commit. Use git status to track remaining work and git merge --abort to escape cleanly if needed.