GitResolving Conflicts

Resolving Conflicts

Resolving conflicts is a small skill that pays back forever. The first one feels intimidating; the tenth one takes a minute. The recipe is always the same: identify what conflicts, edit each file, mark them resolved, finish the merge. This page walks you through a complete resolution from start to finish with realistic examples.

Step-by-step recipe
  • git status — see which files conflict.

  • Open each conflicted file.

  • Pick the correct content; delete the <<<, ===, >>> markers.

  • git add <file> to mark each one resolved.

  • git commit (or git merge --continue) to complete the merge.

A worked example

Suppose main and feature-x both edited a function:

src/login.js — on main

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

src/login.js — on feature-x

JS
function greet(name) {
  return `Hi there, ${name}.`;
}

The merge

Bash
git switch main
git merge feature-x
# CONFLICT (content): Merge conflict in src/login.js
# Automatic merge failed; fix conflicts and then commit the result.

src/login.js after Git's mark-up

JS
function greet(name) {
<<<<<<< HEAD
  return `Hello, ${name}!`;
=======
  return `Hi there, ${name}.`;
>>>>>>> feature-x
}
Decide what to keep

Read both versions. Pick the right answer for the project:

  • Keep only Hello, ${name}! — keep the current branch’s version.

  • Keep only Hi there, ${name}. — take the incoming branch’s version.

  • Combine them — e.g., greet differently based on locale.

  • Rewrite — sometimes both are wrong and you start over.

The final version

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

Save the file. Remove all three conflict markers and everything you don’t want.

Mark resolved and commit

Bash
git add src/login.js
git status
# All conflicts fixed but you are still merging.
#   (use "git commit" to conclude merge)

git commit
# (default message: "Merge branch 'feature-x'" — edit if you like)
If there are many conflicts

Tackle them one file at a time

Bash
git status                # see the list
git diff --name-only --diff-filter=U   # just the names
git mergetool             # open them in your visual tool
# or open them yourself in your editor and edit each
Helpful inspection commands

What did each side change?

Bash
# Show the common ancestor's version
git show :1:src/login.js     # "stage 1" — base
git show :2:src/login.js     # "stage 2" — our side
git show :3:src/login.js     # "stage 3" — their side

# Diff against the merge base (Git's view of the conflict)
git log --merge -p -- src/login.js
git log --merge --oneline -- src/login.js
Picking one side without editing

Bash
# Take our version verbatim
git checkout --ours src/login.js
git add src/login.js

# Take their version verbatim
git checkout --theirs src/login.js
git add src/login.js
Verifying resolution

Catch leftover markers

Bash
git diff --check
# Reports any line still containing <<< === or >>>

# Run tests after every merge — conflicts that LOOK resolved can still
# break behaviour
npm test
pytest
go test ./...
Continuing a merge

Bash
# Equivalent to 'git commit' when no further conflicts remain
git merge --continue
Bailing out

Bash
git merge --abort
# Restores everything to pre-merge state
Tips that experienced developers use
  • Use a 3-way diff view. Tools like VS Code or Beyond Compare show base / ours / theirs side by side — much easier than reading conflict markers in text.

  • Enable rerere (git config rerere.enabled true). Git remembers your resolutions and replays them automatically if the same conflict happens again (common during long rebases).

  • Take small bites. If you have 20 conflicting files, resolve and stage one at a time. Long resolution sessions invite mistakes.

  • Talk to the original author. Sometimes the right answer requires context only they have — ping them in chat.

  • Run tests immediately. A compilable resolution is not the same as a correct one.

Lockfile conflicts
For lockfiles (`package-lock.json`, `yarn.lock`, `Gemfile.lock`, etc.) the easiest workflow is: pick one side (`git checkout --ours`) or delete the file, then re-run the install command to regenerate it.
Tip
After resolving conflicts, write a more descriptive merge commit message than the default. Mention which side won which decisions — your future self may need that context.