X Xerobit

Merge Conflict Resolution — How to Resolve Git Conflicts

Git merge conflicts occur when two branches change the same lines differently. Here's how to read conflict markers, resolve conflicts using tools and command line, and prevent...

Mian Ali Khalid · · 6 min read
Use the tool
Text Diff
Compare two text blocks line-by-line or word-by-word. Unified and split view. Shows added, removed, and changed segments with full color coding.
Open Text Diff →

A merge conflict happens when two branches modify the same region of a file differently. Git can auto-merge most changes, but when two edits overlap, it marks the conflict and asks you to resolve it. Understanding the conflict format makes resolution faster.

Use the text compare tool to compare text versions when resolving conflicts manually.

What causes merge conflicts

# Branch A modifies line 5:
# function greet(name) { return "Hi " + name; }

# Branch B also modifies line 5 differently:
# function greet(name) { return `Hello, ${name}!`; }

# Merge:
git merge feature-branch
# Auto-merging greeting.js
# CONFLICT (content): Merge conflict in greeting.js
# Automatic merge failed; fix conflicts and then commit the result.

Git can auto-merge when changes are in different parts of the file. Conflicts only occur when:

  • Both branches modify the same lines
  • Both branches add content in the same location
  • One branch deletes a file that the other branch modifies

Reading conflict markers

<<<<<<< HEAD
function greet(name) { return "Hi " + name; }
=======
function greet(name) { return `Hello, ${name}!`; }
>>>>>>> feature/template-literals
MarkerMeaning
<<<<<<< HEADStart of your current branch’s version
=======Separator between the two versions
>>>>>>> branch-nameEnd of the incoming branch’s version

Your task: replace the entire block (including markers) with the correct final code.

Three resolution choices

Keep yours (HEAD): Delete the incoming version and markers:

function greet(name) { return "Hi " + name; }

Keep theirs: Delete your version and markers:

function greet(name) { return `Hello, ${name}!`; }

Combine both: Write a new version that incorporates both changes:

// Use template literal syntax (both teams wanted improvement):
function greet(name) { return `Hello, ${name}!`; }

Most conflicts require the “combine” approach — understanding what both branches were trying to do.

Resolving conflicts from command line

# 1. See which files have conflicts:
git status
# both modified: src/greeting.js

# 2. Open the conflicting file in your editor and fix it
# Remove conflict markers, keep correct code

# 3. Stage the resolved file:
git add src/greeting.js

# 4. Check all conflicts are resolved:
git status
# All conflicts fixed but you are still merging.
# (use "git commit" to conclude merge)

# 5. Complete the merge:
git commit
# Git pre-fills a merge commit message

# OR abort if you made a mistake:
git merge --abort  # Returns to pre-merge state

VS Code conflict resolution

VS Code shows conflict markers with clickable options:

Accept Current Change | Accept Incoming Change | Accept Both Changes | Compare Changes
  • Accept Current Change: Keep your version (HEAD)
  • Accept Incoming Change: Keep their version
  • Accept Both Changes: Append both versions (rarely what you want)
  • Compare Changes: Open side-by-side diff view

In the Source Control panel, files with conflicts show a C badge.

Three-way merge (base comparison)

Standard merge shows two versions. A three-way merge shows three:

  • Ours: Current branch
  • Theirs: Incoming branch
  • Base: Common ancestor (what both branches started from)
# Open three-way merge tool:
git mergetool
# or with VS Code as the tool:
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
git mergetool

Seeing the base version often makes conflicts clearer: you can see what each branch changed and why.

Preventing merge conflicts

Frequent integration

# Rebase your branch on main regularly:
git fetch origin
git rebase origin/main

# This moves your commits on top of the latest main,
# preventing large divergences that cause more conflicts

Small, focused commits

Large changes touching many files are more likely to conflict. Small commits that change one thing have less overlap with other work.

Communicate about shared code

If you know you’ll be changing a core module, coordinate with others working in the same area. It’s faster to align upfront than resolve conflicts after.

Use lock files

For dependency files (package-lock.json, Gemfile.lock, poetry.lock), conflicts are common but mechanical. Always regenerate lock files after merging rather than manually resolving the conflicts:

# After resolving package.json conflicts:
rm package-lock.json
npm install  # Regenerates lock file cleanly

# Then add and commit:
git add package-lock.json
git commit

Common conflict patterns and how to resolve them

Conflicting imports

<<<<<<< HEAD
import { UserService } from './services/user.service';
import { AuthService } from './services/auth.service';
=======
import { UserService } from './services/user.service';
import { EmailService } from './services/email.service';
>>>>>>> feature/email-notifications

Resolution: Keep both new imports (both are needed):

import { UserService } from './services/user.service';
import { AuthService } from './services/auth.service';
import { EmailService } from './services/email.service';

Conflicting function signatures

<<<<<<< HEAD
def process_user(user_id: int, notify: bool = False):
=======
def process_user(user_id: int, email: str = None):
>>>>>>> feature/email-support

Resolution: Combine parameters:

def process_user(user_id: int, notify: bool = False, email: str = None):

Conflicting version numbers

<<<<<<< HEAD
"version": "1.2.0"
=======
"version": "1.3.0"
>>>>>>> release/1.3

Resolution: Keep the higher version (or discuss with team):

"version": "1.3.0"

Database migration conflicts

Conflicts in sequential migration numbers require special handling:

migrations/
  0042_add_users_index.py  ← Your branch
  0042_add_orders_table.py ← Their branch (same number!)

Resolution: Renumber one migration:

migrations/
  0042_add_users_index.py
  0043_add_orders_table.py  ← Renumber and update dependencies

Related posts

Related tool

Text Diff

Compare two text blocks line-by-line or word-by-word. Unified and split view. Shows added, removed, and changed segments with full color coding.

Written by Mian Ali Khalid. Part of the Dev Productivity pillar.