Merge conflict

On this page 7

A merge conflict is the situation in which a version control system cannot combine two sets of changes automatically, because both changed the same part of a file or made incompatible changes to its structure. Among development tools, it is the point where an automated merge stops and a developer must decide which content is correct.

Conflicts arise in Git during merges, rebases, cherry-picks, reverts and stash applications, and they also appear inside pull requests on hosting platforms such as GitHub. A conflict is not an error in the tool; it is the tool correctly refusing to guess when two lines of development disagree.

How a merge conflict arises

Git merges by performing a three-way merge. It finds the merge base, the most recent common ancestor of the two branches, and compares each branch with that base. A change made on only one side is applied automatically; a region changed differently on both sides becomes a conflict hunk.

When a conflict occurs, Git pauses the operation, writes conflict markers into the affected file and records three versions of it in the index: the base as stage 1, the current branch as stage 2 and the incoming branch as stage 3. The markers look like <<<<<<< HEAD, a separator line ======= and >>>>>>> branch-name, with each side’s text between them.

The merge remains in progress until every conflicted file is resolved, staged with git add and committed. Until then, git status lists the files as unmerged, and git merge --abort or git rebase --abort returns the repository to its state before the operation.

Types of merge conflicts

Not every conflict is a pair of edited lines. Git distinguishes several conflict types, and recognizing the type tells the developer which resolution steps apply.

Conflict typeTypical causeHow it appears
Content conflictBoth sides edited overlapping linesConflict markers inside the file
Modify/delete conflictOne side changed a file the other deletedFile listed as deleted by us or them
Rename conflictBoth sides renamed the same file differentlyTwo candidate paths for one file
Add/add conflictBoth sides created a file at the same pathMarkers covering the whole file
Binary conflictBoth sides changed an image or archiveNo markers; one version must be chosen
Semantic conflictChanges merge cleanly but break behaviorNo report from Git; tests or build fail
Common types of merge conflicts in Git

The semantic conflict is the most dangerous because Git does not detect it. One branch renames a function while another adds a new call to the old name; both merges succeed textually, yet the result does not compile. Only automated tests and continuous integration catch this class reliably.

Resolving a merge conflict

Resolving a conflict means producing the correct combined content, not merely deleting markers. The correct result is often a blend of both sides, and it requires understanding why each change was made.

  1. Run git status to list the unmerged paths.
  2. Open each file, read both sides and the base if needed, and write the intended result.
  3. Remove every conflict marker and build and test the code.
  4. Stage the files with git add and complete the operation with git commit or git rebase --continue.

Several tools make this easier. Setting merge.conflictStyle to diff3 or zdiff3 adds the base version to the markers, which shows what each side actually changed. Graphical merge tools in an IDE present the base, both sides and the result in separate panes. The option git rerere records conflict resolutions and reapplies them automatically when the same conflict reappears, which helps during long rebases.

Command or settingEffectUse when
git checkout --ours fileTakes the current branch versionThe incoming change is obsolete
git checkout --theirs fileTakes the incoming versionThe incoming change supersedes yours
git mergetoolOpens a configured visual merge toolHunks are large or interleaved
merge.conflictStyle zdiff3Shows the base inside the markersIntent of each side is unclear
git rerereReuses recorded resolutionsRepeated rebases hit the same conflict
git merge --abortCancels the merge entirelyThe merge was started by mistake
Resolution commands and when to use them

One caution applies to rebasing: during a rebase, ours and theirs are swapped compared with a merge, because Git replays your commits on top of the target branch. Choosing a side by name without checking the content is a common way to discard work.

Preventing and reducing merge conflicts

Conflicts cannot be eliminated, but their frequency and size depend on team practices. The longer two branches diverge, the more overlapping edits accumulate, so the most effective prevention is integrating small changes often.

PracticeEffect on conflictsTrade-off
Short-lived branchesLess divergence from the main lineRequires frequent small pull requests
Regular syncing with mainConflicts surface early and smallMore merge or rebase steps
Consistent formatting with a linterAvoids whitespace and style churnNeeds agreed configuration
Smaller, focused filesFewer people edit the same regionMore files to navigate
Feature flagsUnfinished work merges early in a disabled stateFlag cleanup overhead
Generated files excluded or regeneratedLock files and builds stop conflictingBuild step must be reliable
Practices that reduce merge conflicts

Lock files for package managers are a frequent hotspot. A sound approach is to resolve the manifest first and then regenerate the lock file with the package manager rather than editing it by hand.

Common mistakes when handling conflicts

  • Committing leftover markers: a forgotten ======= line breaks the build or, in data files, silently corrupts content.
  • Choosing one side wholesale without reading it, which discards a colleague’s fix.
  • Skipping tests after resolution, so a semantic conflict reaches the main branch.
  • Force-pushing a rebased shared branch, which rewrites history other people depend on.
  • Resolving conflicts in someone else’s code alone when the intent is unclear instead of asking the author.

Many teams add a pre-commit check that rejects conflict markers and require a passing pipeline before a pull request can merge. These two safeguards catch the majority of resolution mistakes before they affect other developers.

In plain words

A merge conflict happens when two people change the same sentence of a shared document in different ways, and the software has to combine their versions. It can merge changes to different paragraphs on its own, but it cannot know which wording of the same sentence is right.

So it stops, shows both versions side by side and asks a person to decide. The tool is being careful, not broken. Once someone writes the final wording and saves it, the two versions become one again and work continues.

A real-world example

Two developers on a team of six work on a web shop. One changes the shipping cost function to add a free-shipping threshold of 50 dollars, while the other, on a separate branch, changes the same function to support a new currency. Both open pull requests on the same day.

The first pull request merges cleanly. When the second developer updates the branch with the main line, Git reports a content conflict in the shipping module covering about 15 lines. With the zdiff3 conflict style enabled, the markers show the original function, the threshold change and the currency change together.

The developer writes a version that applies the threshold after currency conversion, removes the markers and runs the test suite. One test fails because the threshold was compared in the wrong currency, which reveals a semantic issue that neither branch had alone. After a two-line fix, the tests pass and the combined change merges in less than an hour.