Skip to main content

Handling Merge Conflicts

What is a Merge Conflict?

A merge conflict occurs when Git cannot automatically resolve differences between two commits during a merge or pull. This happens when:

  • The same line was edited differently in two branches
  • One branch deleted a file that another branch modified
  • Two branches added different files with the same name

When Conflicts Occur

Merge conflicts happen during:

  • git merge - merging one branch into another
  • git pull - pulling remote changes (which is fetch + merge)
  • git rebase - rebasing onto another branch
  • git cherry-pick - applying commits from one branch to another

Identifying Conflicts

Git Status

Check merge status
git status

Output during a conflict:

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: app.js
both modified: config.json

Conflict Markers

When you open a conflicted file, you'll see conflict markers:

Merge conflict markers in code
// ... existing code ...

<<<<<<< HEAD
// This is your current branch's version
const API_URL = 'https://api.example.com';
const TIMEOUT = 5000;
=======
// This is the incoming branch's version
const API_URL = 'https://api.new.com';
const TIMEOUT = 10000;
>>>>>>> feature-update

// ... more code ...

Understanding Markers

  • <<<<<<< HEAD - Start of your current branch's changes
  • ======= - Separator between your changes and incoming changes
  • >>>>>>> branch-name - End of incoming branch's changes

Resolving Conflicts

Manual Resolution

  1. Open the conflicted file(s)
  2. Choose which version to keep:
Keep your version (remove markers)
const API_URL = 'https://api.example.com';
const TIMEOUT = 5000;
  1. Or keep their version:
Keep incoming version
const API_URL = 'https://api.new.com';
const TIMEOUT = 10000;
  1. Or combine both:
Combine both versions
const API_URL = 'https://api.example.com';
const NEW_API_URL = 'https://api.new.com';
const TIMEOUT = 10000; // Use the larger timeout

Step-by-Step Conflict Resolution

Complete merge conflict resolution process
# 1. Check status to see conflicted files
git status

# 2. Edit each conflicted file (remove markers, keep desired code)
vim app.js
vim config.json

# 3. Stage the resolved files
git add app.js config.json

# 4. Complete the merge
git commit -m "Resolve merge conflicts"

Conflict Resolution Tools

Using VSCode

VSCode provides built-in conflict resolution:

  • "Accept Current Change" - Keep your version
  • "Accept Incoming Change" - Keep their version
  • "Accept Both Changes" - Keep both versions
  • "Compare Changes" - See differences side-by-side

Command Line Tools

Use merge tool
git mergetool

This opens your configured merge tool (like Meld, KDiff3, etc.)

See conflict in context
git diff

Shows conflict markers with surrounding context.

Conflict Resolution Strategies

Strategy 1: Interactive Resolution

Resolve conflicts one by one
# See list of conflicts
git diff --name-only --diff-filter=U

# Resolve each file
git add file1.js
git add file2.js

# Complete merge
git commit -m "Resolve conflicts"

Strategy 2: Use Ours or Theirs

Use our version for a specific file
git checkout --ours path/to/file
git add path/to/file

# Or use their version
git checkout --theirs path/to/file
git add path/to/file

Strategy 3: Abort and Restart

Cancel the merge and start over
git merge --abort
Cancel a rebase
git rebase --abort
Cancel a pull
git pull --abort

Practical Conflict Examples

Example 1: Conflicting Line Changes

Your branch (main):

const config = {
apiUrl: 'https://api.example.com',
timeout: 5000
};

Incoming branch (feature-update):

const config = {
apiUrl: 'https://api.new.com',
timeout: 10000
};

Conflict in file:

<<<<<<< HEAD
const config = {
apiUrl: 'https://api.example.com',
timeout: 5000
};
=======
const config = {
apiUrl: 'https://api.new.com',
timeout: 10000
};
>>>>>>> feature-update

Resolution - communicate with team:

const config = {
apiUrl: process.env.API_URL || 'https://api.example.com',
timeout: 10000 // Higher timeout is safer
};

Example 2: Deleted vs Modified

Your branch: Deleted a file Their branch: Modified the same file

Resolution
# Decide: should the file exist?
git rm path/to/file # Delete it
# OR
git add path/to/file # Keep it

Example 3: Both Added Same File

Two branches added different new files:

Check what exists
git ls-files
# One of the files might have conflict markers

# Open and verify you want both files
git add new-file1.js new-file2.js

Preventing Conflicts

1. Communication

  • Discuss with team which files you'll edit
  • Use feature branches for different features
  • Avoid editing the same lines simultaneously

2. Frequent Pulls

Keep your branch updated
git pull origin main regularly

This brings in changes early, making conflicts smaller.

3. Proper Branching Strategy

Create branches from main
git checkout main
git pull origin main
git checkout -b feature/my-feature

Always start from an updated main branch.

4. Review Changes Before Merge

Review before merging
git log main..feature-branch
git diff main...feature-branch

Understand what you're merging before doing it.

5. Small, Focused Changes

  • Make changes to different parts of the codebase
  • Avoid massive refactors without team coordination
  • Create specific feature branches instead of long-running branches

Best Practices During Conflicts

Workflow for handling conflicts
# 1. Keep your changes safe
git stash save "my current work"

# 2. Try the merge
git merge feature-branch

# 3. If conflicts arise
git status # See which files conflict

# 4. Resolve conflicts carefully
# - Read the code, understand both sides
# - Don't just delete one side without thinking
# - Test the resolved code

# 5. Mark as resolved and complete merge
git add resolved-files
git commit -m "Resolve conflicts and merge feature-branch"

# 6. Run tests to verify
npm test

Conflict Resolution Checklist

When resolving conflicts:

  • Understand what each change is trying to do
  • Verify the resolution makes logical sense
  • Remove all conflict markers (<<<<<<<, =======, >>>>>>>)
  • Test the resolved code if possible
  • Run automated tests (linting, tests)
  • Stage the resolved files with git add
  • Commit with a clear message explaining the resolution

Understanding Conflict Causes

Find what caused the conflict
git log --oneline main..feature-branch
# See what commits are in feature-branch

git show <commit-hash>
# See what that specific commit changed

git blame conflicted-file.js
# See who changed each line

Using Git Attributes

Create .gitattributes to handle conflicts better:

.gitattributes - Binary files
# Don't merge binary files
*.png merge=binary
*.jpg merge=binary

# Union merge for specific files
package.json merge=union

Key Takeaways

  • Merge conflicts are normal and manageable
  • Always read conflict markers carefully before resolving
  • Test your resolution before committing
  • Communicate with team to prevent conflicts
  • Use version control to understand who made what changes
  • Small, focused changes reduce conflict frequency
  • Frequently pull/update to catch conflicts early
  • When in doubt, ask your team about the right resolution

Common Mistakes to Avoid

Don't: Just delete one side without understanding it ✅ Do: Read both versions and think about what's needed

Don't: Resolve conflicts without testing ✅ Do: Verify the resolved code works correctly

Don't: Keep working on a branch while main changes significantly ✅ Do: Regularly pull main into your feature branch

Don't: Create conflicts through poor communication ✅ Do: Discuss major refactors with your team first