Skip to content

← Writing

engineering

Resolving Merge Conflicts Like You Mean It

· Jerwin Arnado ·

A merge conflict is Git admitting it can’t decide: two branches changed the same lines, and it needs a human to pick. The markers it leaves behind look alarming the first time, but they encode a simple question. Once you can read them — and you’ve been rebasing feature branches onto fresh upstream, so you will — conflicts go from panic to routine.

Reading the markers

When a merge or rebase conflicts, Git writes both versions into the file, fenced by markers:

<<<<<<< HEAD
$timeout = 30;
=======
$timeout = 60;
>>>>>>> feat/longer-timeout

Three markers, three parts:

  • <<<<<<< HEAD — everything below is your current branch’s version (the one you’re merging into, or during a rebase, the commit being replayed onto).
  • ======= — the divider.
  • >>>>>>> feat/longer-timeout — everything above is the incoming branch’s version.

Your job: delete the markers and leave the file the way it should actually be — which might be one side, the other, or a combination. Here, maybe the answer is 60:

$timeout = 60;

Save, and the conflict in that file is resolved. There’s nothing magic under the markers; it’s just two candidate texts and a decision.

The resolution loop

git status                      # lists "Unmerged paths" — your conflict to-do list
# edit each conflicted file, remove markers, keep the right result
git add path/to/resolved/file   # mark it resolved
git status                      # confirm nothing left unmerged

Then finish the operation you were in:

git merge --continue            # if you were merging
# or
git rebase --continue           # if you were rebasing

git add is how you tell Git a file is resolved — there’s no separate “resolve” command; staging the cleaned file is the signal. Want out entirely? git merge --abort or git rebase --abort returns you to before the operation, conflicts and all undone.

Taking one side wholesale

Sometimes you know one side is simply correct. Skip the hand-editing:

git checkout --ours   path/to/file     # keep HEAD's version
git checkout --theirs path/to/file     # keep the incoming version
git add path/to/file

A rebase gotcha worth burning in: during a rebase, --ours and --theirs are swapped relative to a merge, because a rebase replays your commits onto the other branch — so “ours” becomes the branch you’re rebasing onto. When in doubt, open the file and read the markers rather than trusting the flag name.

Using a merge tool

For a gnarly conflict, a three-way visual tool beats raw markers. Configure one once:

git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'

Then launch it on the conflicted files:

git mergetool

VS Code, Meld, and Beyond Compare all show three panes — your version, theirs, and the common ancestor (the “base”) — which makes it obvious what each side changed rather than just that they differ. The base pane is the part hand-editing hides from you, and it’s often what makes the right resolution clear.

rerere: never solve the same conflict twice

If you rebase a long-running branch repeatedly, you hit the same conflict every time. rerere — “reuse recorded resolution” — memorizes how you resolved a conflict and replays it automatically next time the identical conflict appears:

git config --global rerere.enabled true

That’s the whole setup. From then on, the first time you resolve a given conflict Git records it; every subsequent identical conflict is resolved for you (you still git add to confirm). On a feature branch you rebase daily onto a moving main, this quietly saves hours.

Caveats and best practices

  • Conflicts scale with branch age. The real fix is structural: short-lived branches and frequent syncing mean small, rare conflicts. A week-old branch conflicting in ten files is a process smell, not bad luck.
  • Always re-read the whole region after resolving. It’s easy to delete a marker but leave a stray brace or a half-merged line. Run the tests after — a clean merge isn’t a correct result.
  • Don’t blindly --theirs your way out. Taking one side wholesale is fine when you know it’s right and dangerous when you’re just tired. The markers exist because a human judgment is genuinely needed.
  • Turn on rerere now. It costs nothing and only ever helps; future-you rebasing a stubborn branch will be grateful.

Conclusion

<<<<<<< HEAD        your side
=======             ──────────
>>>>>>> branch      their side
# edit → remove markers → keep the right result
git add <file>
git merge --continue   # or  git rebase --continue

A conflict is just two candidate texts and a question Git can’t answer alone. Read the markers, decide, stage, continue. Keep branches short so conflicts stay small, turn on rerere so you never repeat one, and the scariest-looking part of Git becomes a ten-second chore.