Skip to main content

Merge Conflicts

Merge conflicts happen when Git can't automatically combine changes from two branches. They're a normal part of collaboration — not something to fear.

Why Conflicts Happen

A conflict occurs when two branches modify the same lines in the same file. Git doesn't know which version to keep, so it asks you to decide.

Common scenarios:

  • Two developers edit the same function
  • One developer renames a variable while another modifies the same line
  • A file is deleted on one branch and modified on another

Conflicts do not happen when:

  • Two branches edit different files
  • Two branches edit different parts of the same file

What a Conflict Looks Like

When you run git merge or git pull and there's a conflict, Git marks the file with conflict markers:

function getUser(id) {
<<<<<<< HEAD
  return db.users.findById(id);
=======
  return db.users.findOne({ _id: id });
>>>>>>> feature/update-db
}
  • <<<<<<< HEAD — your current branch's version
  • ======= — the divider between the two versions
  • >>>>>>> feature/update-db — the incoming branch's version

Resolving Conflicts Step by Step

  1. Identify conflicted files:
git status
# Look for "both modified" files
  1. Open the file and find the conflict markers.

  2. Choose the correct code. Remove the conflict markers and keep the version you want — or combine both:

function getUser(id) {
  return db.users.findOne({ _id: id });
}
  1. Stage the resolved file:
git add src/user.js
  1. Commit the merge:
git commit -m "Resolve merge conflict in user query method"

Resolving Conflicts During Rebase

Conflicts during rebase work differently. Git pauses at each conflicting commit:

git rebase main
# CONFLICT in src/user.js

# Fix the conflict, then:
git add src/user.js
git rebase --continue

If things go wrong and you want to start over:

git rebase --abort

Using a Merge Tool

Visual merge tools make conflicts easier to understand:

# Open the default merge tool
git mergetool

# Configure VS Code as your merge tool
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait --merge $REMOTE $LOCAL $BASE $MERGED'

VS Code has excellent built-in conflict resolution — it shows "Accept Current", "Accept Incoming", "Accept Both", and "Compare Changes" buttons above each conflict.

Accepting One Side Entirely

When you know one branch should win completely:

# Keep your version for all conflicts
git checkout --ours src/user.js
git add src/user.js

# Keep the incoming version for all conflicts
git checkout --theirs src/user.js
git add src/user.js

For an entire merge:

# Accept all of ours
git merge feature/update-db --strategy-option ours

# Accept all of theirs
git merge feature/update-db --strategy-option theirs

Prevention Strategies

The best conflict is one that never happens:

  • Pull frequently. Merge main into your branch daily to catch conflicts early when they're small.
  • Keep branches short-lived. The longer a branch lives, the more it diverges.
  • Communicate with your team. If two people need to edit the same file, coordinate.
  • Break up large files. A 500-line file creates more conflicts than five 100-line files.
  • Use consistent formatting. Auto-formatters like Prettier prevent whitespace-only conflicts. Configure them to run on save or as a pre-commit hook.
# Example: run Prettier as a pre-commit check
npx lint-staged
  • Use lock files correctly. For package-lock.json or pnpm-lock.yaml conflicts, don't resolve manually — regenerate:
# Delete the conflicted lock file, reinstall, and stage
rm pnpm-lock.yaml
pnpm install
git add pnpm-lock.yaml