How to Handle Merge Conflicts in Git: What They Are and How to Resolve Them

Merge conflicts are one of the most common friction points in collaborative development — and one of the most misunderstood. They look alarming the first time you see them, but once you understand what Git is actually telling you, resolving them becomes a manageable, repeatable skill.

What Is a Merge Conflict?

When you merge two branches in Git, it attempts to automatically combine changes from both. Most of the time, this works without any input from you. A merge conflict occurs when Git encounters two changes to the same part of the same file and can't determine which version to keep — so it stops and asks you to decide.

Conflicts don't mean something broke. They mean Git found ambiguity that only a human can resolve.

What Triggers a Merge Conflict?

The most common scenarios:

  • Two developers edit the same lines in the same file on different branches
  • One branch deletes a file while another branch modifies it
  • Rebasing or cherry-picking commits that touch the same code regions
  • Long-lived branches that diverge significantly from main before being merged

The longer a branch lives without syncing to its base branch, the higher the chance of conflicts accumulating.

How Git Marks a Conflict

When a conflict occurs, Git pauses the merge and marks the affected file(s) with conflict markers. Opening the file, you'll see something like this:

<<<<<<< HEAD button color: blue; ======= button color: green; >>>>>>> feature/new-theme 
  • Everything between <<<<<<< HEAD and ======= is the version from your current branch
  • Everything between ======= and >>>>>>> feature/new-theme is the version from the incoming branch

Git won't proceed until every conflict marker in every affected file is resolved and removed.

The Core Workflow for Resolving Conflicts

Step 1 — Identify Which Files Have Conflicts

Run git status after a failed merge. Files listed under "both modified" contain conflicts that need your attention.

Step 2 — Open and Edit the Conflicted File

You have three options for what to keep:

  • Accept the current branch version — delete the incoming block and the markers
  • Accept the incoming branch version — delete the current block and the markers
  • Write a combined version — manually edit the content to incorporate both changes, then remove all markers

There is no universally correct choice. The right resolution depends entirely on what the code is supposed to do.

Step 3 — Mark the File as Resolved

Once you've edited the file and removed all conflict markers, run:

git add <filename> 

This tells Git the conflict in that file is resolved.

Step 4 — Complete the Merge

After all conflicted files are staged, run:

git commit 

Git will pre-populate a commit message describing the merge. You can accept it or write your own.

Using a Merge Tool Instead of Editing Manually ⚙️

For complex conflicts, a visual merge tool can make the process significantly clearer. These tools display the two conflicting versions side-by-side alongside the common ancestor, giving you more context for each change.

Git has built-in support for several tools via git mergetool. Common options include:

ToolInterfaceBest For
vimdiffTerminalKeyboard-driven workflows
VS CodeGUI / editorDevelopers already using VS Code
IntelliJ IDEAGUI / editorJava and JVM-focused teams
MeldStandalone GUIVisual diffing on Linux/Windows
Beyond CompareStandalone GUILarge codebases, power users

You configure your preferred tool with git config --global merge.tool <toolname>, and then invoke it with git mergetool during an active conflict.

Strategies for Reducing Conflicts Before They Happen

Resolving conflicts is necessary sometimes, but reducing their frequency is better practice:

  • Merge or rebase from main frequently — short-lived branches diverge less
  • Keep commits small and focused — broad, sweeping changes create more collision surface
  • Communicate with teammates when editing shared files or core modules
  • Use feature flags to isolate work-in-progress without creating long-lived branches
  • Split large files — monolithic files with hundreds of lines are conflict magnets

Aborting a Merge

If you start a merge and realize you need to step back, run:

git merge --abort 

This restores your branch to the state it was in before the merge began. Nothing is lost.

The Variables That Determine How Complex Your Conflicts Will Be 🔍

Not all conflicts are equally difficult to resolve. Several factors shape how challenging the process gets:

  • Team size — more contributors touching the same codebase means more potential overlap
  • Branching strategy — trunk-based development produces fewer conflicts than long-lived feature branches
  • Codebase structure — tightly coupled code creates more conflict opportunities than modular, well-separated components
  • Language and file type — auto-generated files, lock files (package-lock.json, yarn.lock), and binary files behave differently than hand-written source code
  • Developer experience with Git — someone comfortable with rebasing and interactive history tools can resolve conflicts with more precision than someone using Git primarily through a GUI

The same two-branch merge can be trivial for one team and genuinely difficult for another, depending entirely on how the codebase is organized and how the team works.

Binary files — images, compiled assets, databases — cannot be resolved with conflict markers at all. Git can tell you there's a conflict, but you have to manually choose one version or the other using git checkout --ours or git checkout --theirs.

Understanding Git's conflict model is the foundation. How that applies in practice depends on your branching workflow, your team's habits, and the shape of the code you're working with.