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
--theirsyour 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
rererenow. 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.