Git merge conflicts happen when Git can’t automatically figure out how to combine changes from different branches, usually because the same lines of code were modified in both.
Let’s say you have a main branch and a feature branch. You’ve been working on feature, adding a new function. Meanwhile, someone else updated the same part of the file on main. When you try to merge feature into main, Git throws a conflict.
Here’s a typical scenario:
You’re on main, and you run:
git merge feature
Git responds with:
Auto-merging src/app.js
CONFLICT (content): Merge conflict in src/app.js
Automatic merge failed; fix conflicts and then commit the result.
This means Git found a disagreement in src/app.js. It doesn’t know which version of the conflicting code to keep.
Common Causes and How to Fix Them
The core of a merge conflict is always the same: Git sees two different versions of the same text and needs you to decide which one is correct, or if a new version combining both is needed.
-
Conflicting changes on the same lines: This is the most frequent cause. Both branches modified the exact same lines of code.
-
Diagnosis: Open the conflicted file (e.g.,
src/app.js). You’ll see markers like<<<<<<<,=======, and>>>>>>>.<<<<<<< HEAD function greet(name) { console.log("Hello, " + name + "!"); } ======= function greet(name, greeting = "Hello") { console.log(greeting + ", " + name + "!"); } >>>>>>> featureThe
HEADsection is what’s currently in yourmainbranch. Thefeaturesection is what came from the branch you’re merging. -
Fix: Manually edit the file to contain the desired final version. Remove the
<<<<<<<,=======, and>>>>>>>markers. In our example, you might decide to keep the newgreetingparameter:function greet(name, greeting = "Hello") { console.log(greeting + ", " + name + "!"); }Then, stage the resolved file:
git add src/app.js -
Why it works: You’re telling Git, "I’ve reviewed the conflicting changes and this is the final, correct version."
git addmarks the file as resolved.
-
-
One file deleted, another modified: A file was deleted on one branch and modified on another. Git doesn’t know if the modification should be kept or if the deletion should prevail.
-
Diagnosis: Git will list the conflicted file and indicate its status.
git statusOutput might show:
Unmerged paths: (use "git add <file>..." to mark resolution) both modified: config.ymlOr, if a file was deleted on
mainand modified onfeature:Unmerged paths: (use "git add/rm <file>..." to mark resolution) both deleted: old_feature.txt modified elsewhere: old_feature.txt -
Fix: Decide whether to keep the modified file or honor the deletion.
- To keep the modified file:
git add <conflicted_file> - To honor the deletion (i.e., delete the file on the merged branch):
git rm <conflicted_file>thengit commit(Git will usually infer this if yougit rma file that Git thinks is deleted on HEAD). If the file was deleted onHEADand modified onfeature, you might need togit checkout HEAD -- <conflicted_file>to restore the deletion fromHEAD’s perspective and thengit add <conflicted_file>.
- To keep the modified file:
-
Why it works:
git addorgit rmexplicitly tells Git your decision about the file’s final state.
-
-
Conflicting renames: A file was renamed on one branch and modified on another. Git gets confused about tracking the file.
-
Diagnosis:
git statuswill often show a file as both "modified" and "deleted" or similar, or mention a rename conflict.Unmerged paths: (use "git add/rm <file>..." to mark resolution) both modified: src/utils.js deleted: old_util.jsThis often happens when
old_util.jswas renamed tosrc/utils.json one branch, andold_util.jsitself was modified on another. -
Fix: You need to tell Git what the final name and content should be.
-
If the rename is correct and the modification should be applied to the new name:
git mv old_util.js src/utils.js # This stages the rename and the content merge git add src/utils.js # If there were actual content conflicts within the renamed fileOr more directly, if Git already detected the rename and modification:
git add src/utils.js # If the rename is accepted and content is fineIf there were content conflicts within the renamed file, you’d edit
src/utils.jsto resolve them, thengit add src/utils.js. -
If the rename should be rejected and the file should remain
old_util.jswith its modifications:git checkout HEAD -- old_util.js # Revert to the deletion state from HEAD git add old_util.js # Stage the modified version of the old name
-
-
Why it works:
git mvis Git’s way of tracking renames. By usinggit addorgit rm, you confirm Git’s understanding of the file’s lifecycle.
-
-
Large binary files with conflicting edits: Git doesn’t do well with merging binary files (like images, PDFs, compiled executables). If two branches modify the same binary file, Git can’t merge them.
-
Diagnosis:
git statuswill show the binary file as unmerged.Unmerged paths: (use "git add <file>..." to mark resolution) binary file <filename> has both differences -
Fix: You must choose one version or the other. You cannot merge binary files.
- To keep the version from the branch you’re merging into (e.g.,
main):git checkout HEAD -- <binary_file> - To keep the version from the branch you’re merging from (e.g.,
feature):git checkout feature -- <binary_file>Then stage the chosen file:
git add <binary_file> - To keep the version from the branch you’re merging into (e.g.,
-
Why it works: You’re explicitly telling Git which of the two identical-but-different binary files to keep.
-
-
Submodule conflicts: If you’re using Git submodules, and a submodule has been updated differently in both branches you’re merging, you’ll get a submodule conflict.
-
Diagnosis:
git statuswill show the submodule as "modified".Unmerged paths: (use "git add <modified_submodule>..." to mark resolution) modified: my_submodule (new commits)This means the commit hash recorded for
my_submodulediffers between the branches. -
Fix: You need to decide which commit of the submodule to use.
- To use the commit from the
HEADbranch:git checkout HEAD -- my_submodule git add my_submodule - To use the commit from the
featurebranch:git checkout feature -- my_submodule git add my_submodule
You might also need to
cd my_submoduleandgit checkout <specific_commit_hash>if you need a particular version not currently checked out. - To use the commit from the
-
Why it works: You’re updating the superproject’s record of the submodule’s commit hash to reflect your chosen version.
-
Completing the Merge
Once you’ve resolved all conflicts and staged the files:
git status # Verify all conflicts are resolved and staged
git commit
Git will usually pre-populate a commit message like "Merge branch 'feature' into main". You can edit it or use it as is.
After fixing all merge conflicts and committing, the next error you might encounter is a CI/CD pipeline failure due to tests not passing, or a new bug introduced by the merge that wasn’t apparent during the conflict resolution.