What Causes Merge Conflicts?
Git merges automatically when the two branches change different parts of the codebase. A conflict occurs specifically when:
- Two branches modify the same lines in the same file differently.
- One branch modifies a file and the other branch deletes that same file.
Git is excellent at merging changes to different files and even different parts of the same file. It only gets stuck when it literally cannot tell which human decision to keep.
# Trigger a conflict by merging two branches that both changed the same line
git switch main
git merge feature/login
# Auto-merging index.html
# CONFLICT (content): Merge conflict in index.html
# Automatic merge failed; fix conflicts and then commit the result.
# Check which files have conflicts
git status
# 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: index.html
Understanding Conflict Markers
When a conflict occurs, Git edits the conflicted file to show you both versions using special markers:
<!-- index.html with conflict markers -->
<nav class="main-nav">
<a href="/">Home</a>
<<<<<<< HEAD
<a href="/login">Sign In</a>
<a href="/register">Register</a>
=======
<a href="/auth/login">Login</a>
>>>>>>> feature/login
</nav>
| Marker | Meaning |
|---|---|
<<<<<<< HEAD |
Start of YOUR changes (the branch you're merging INTO β current branch) |
======= |
Dividing line between the two versions |
>>>>>>> feature/login |
End of THEIR changes (the branch you're merging FROM) |
Every developer encounters conflicts. They look alarming the first time but become routine quickly. The markers clearly show you exactly what changed and from where. Take conflicts section by section β don't try to resolve the whole file at once if there are multiple conflicts.
Resolving Conflicts Manually
To resolve a conflict, open the conflicted file in your editor and edit it to contain exactly what you want. Remove all three marker lines and keep the correct code (which might be one version, the other, or a combination of both).
Example: resolving the conflict above
<!-- After resolution: combined both versions -->
<nav class="main-nav">
<a href="/">Home</a>
<a href="/auth/login">Sign In</a>
<a href="/register">Register</a>
</nav>
<!-- No conflict markers remain. The file is now valid HTML. -->
Complete resolution workflow
# Step 1: See which files have conflicts
git status
# Step 2: Open each conflicted file, edit to resolve, save
# (edit in your text editor β remove ALL <<<<<<<, =======, >>>>>>> markers)
# Step 3: Mark each resolved file as resolved
git add index.html
git add src/styles.css # if multiple files had conflicts
# Step 4: Verify no more conflicts remain
git status
# All conflicts fixed but you are still merging.
# (use "git commit" to conclude merge)
# Step 5: Commit to finalize the merge
git commit
# Editor opens with auto-generated merge commit message:
# "Merge branch 'feature/login'"
# Save and close to complete the merge
If you accidentally commit a file that still contains <<<<<<< or >>>>>>> markers, your code will be broken and syntax errors will appear. Always double-check by searching the file for <<<< before running git add.
Using Merge Tools
Visual merge tools show both versions side by side, making it easier to choose what to keep. Most modern editors have built-in conflict resolution UIs.
VS Code Merge Editor
VS Code automatically detects conflicted files and shows a special three-panel merge editor when you open them. Buttons let you "Accept Current Change", "Accept Incoming Change", "Accept Both Changes", or manually edit the result pane.
Git's built-in mergetool
# Launch the configured merge tool for all conflicted files
git mergetool
# Configure VS Code as your merge tool
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
# Or use vimdiff (terminal-based)
git config --global merge.tool vimdiff
git mergetool # opens vimdiff for each conflicted file
Aborting a Merge
If you started a merge and things are getting too complex, you can abort and return to the pre-merge state:
# Abort mid-merge and restore everything to before the merge
git merge --abort
# Verify you're back to the pre-merge state
git status
# On branch main
# nothing to commit, working tree clean
Tips to Prevent Conflicts
You can't prevent all conflicts, but you can reduce their frequency and severity:
| Practice | Why it helps |
|---|---|
| Pull frequently | Keep your branch close to main β fewer diverged changes = fewer conflicts |
| Short-lived branches | Branches that live hours/days conflict less than ones that live weeks |
| Small, focused PRs | A PR that touches 5 files is less likely to conflict than one touching 50 |
| Communicate with team | Tell teammates when you're refactoring a shared file they're also editing |
| Modular code structure | Isolate concerns into separate files β fewer people editing the same file |
π Summary
- Conflicts happen when two branches modify the same lines of the same file β Git can't decide which version to keep.
- Git marks conflicts with
<<<<<<< HEAD,=======, and>>>>>>> branch-namemarkers in the file. - To resolve: edit the file to remove markers and keep the right code β
git add <file>βgit commit. - Use
git merge --abortto cancel the merge and return to the pre-merge state. - VS Code and
git mergetoolprovide visual side-by-side conflict resolution. - Prevent conflicts by pulling frequently, keeping branches short-lived, and writing modular code.
FAQ
Run git status β all conflicted files appear under "Unmerged paths" as "both modified". You can also run git diff to see all conflict markers in all files at once. In VS Code, conflicted files show with a "C" indicator in the Source Control panel and are listed under "Merge Changes".
Before committing: just edit the file again. You haven't committed yet, so nothing is final. If you've already committed the merge: use git revert -m 1 <merge-commit-hash> to create a commit that undoes the merge, or git reset --hard ORIG_HEAD if you haven't pushed (Git saves the pre-merge state in ORIG_HEAD).
HEAD in conflict markers refers to the branch you're currently on β the branch you're merging INTO. If you ran git switch main then git merge feature/login, then <<<<<<< HEAD shows the version from main and >>>>>>> feature/login shows the version from feature/login. The "incoming" change is always the branch you named in the git merge command.
Yes, but Git can't show markers in binary files. Instead, Git will say "CONFLICT (binary): Merge conflict in logo.png" and keep one version (usually the current branch's). You then need to manually choose which version to keep: git checkout --ours logo.png (keep current branch's version) or git checkout --theirs logo.png (keep the incoming branch's version), then git add logo.png.