Git Merge Conflicts: What They Are and How to Fix Them
Table of Contents
Working with Git is a dream when all of the developers on a project are committing changes to different lines of code. Things get a little hairy when changes to the same lines are made at the same time. That's when Git throws up its hands, declares a merge conflict, and makes you sort it out. In this post you'll learn what merge conflicts actually are, how to read those nasty <<<<<<< conflict markers, and how to resolve conflicts both by hand and with Git's built-in tools.
All the content from our Boot.dev courses are available for free here on the blog. This one is the "Merge Conflicts" chapter of Learn Git 2. If you want to try the far more immersive version of the course, do check it out!
What Is a Merge Conflict in Git?
A merge conflict occurs when two commits modify the same line and Git can't automatically decide which change to keep and which change to discard.
Consider the following commit history:
C feature
/
A - B main
The main branch has a file with these lines:
package main
func isNice(num int) bool {
return num == 69 // commit B changed this line
}
While feature has:
package main
func isNice(num int) bool {
return num == 420 // commit C changed this line
}
If we merge feature into main, Git will detect that the return line was changed in both branches independently: which creates a conflict.
When a conflict happens (usually as the result of a merge or rebase) Git will prompt you to manually decide which change to keep. It's okay when the same line is modified in one commit, and then again in a later commit. The problem arises when the same line is modified in two commits that aren't in a parent-child relationship.
When Does the Conflict Actually Happen?
Conflicting changes on two different branches is not a problem. The problem only arises when you try to merge those branches. When you do, Git will detect the conflict and ask you to resolve it.
Say you work at MegaCorp™, an "enterprise" CRM service backed by the power of Git. That essentially just means the company stores lists of customer data in a Git repo... truly cutting-edge stuff. You added a new customer to the end of customers/all.csv on your add_customers branch, while your coworker Greg (stupid Greg) added a different customer to the same spot in the same file. Because his change landed on main first (stupid Greg), it's up to you to resolve the conflict:
git switch add_customers
git merge main
# Automatic merge failed; fix conflicts and then commit the result.
Scary message, but nothing is broken. Run git status to confirm that you're in a conflict:
git status
# You have unmerged paths.
# (fix conflicts and run "git commit")
How Do You Read Conflict Markers?
Resolving conflicts is a manual process. When they happen, Git marks the conflicted files and asks you to resolve the conflict by editing the files in your editor. Open the conflicted file (in our case, customers/all.csv) and you'll see a nasty bit of text that looks like this:
first_name,last_name,company,title
<<<<<<< HEAD
karson,yummy,intercooler,ceo
=======
jayson,gross,htmz,contributor
>>>>>>> main
Your editor might even highlight the conflict markers to make it easier to see, but at the end of the day, it's just text.
- The top section, between the
<<<<<<< HEADand=======lines, is our branch's version of the file: the customer you added - The bottom section, between the
=======and>>>>>>> mainlines, is the version of the file that's on themainbranch ("theirs" or as I say "Stupid Greg's")
How Do You Resolve a Merge Conflict?
In many cases, you might want to keep one change and discard the other. That's common when you're dealing with code changes. In other cases, like our customer list, you want to keep both changes: both customers belong in the file. Either way, the process is the same: edit the file so it contains exactly what you want, and delete the conflict markers.
Keeping both changes, the resolved file looks like this:
first_name,last_name,company,title
karson,yummy,intercooler,ceo
jayson,gross,htmz,contributor
After manually editing any conflicting files (sometimes it's more than one file, or more than one section of a file) you need to simply add and commit the changes. This tells Git that you've resolved the conflict and it can continue with the merge.
git add customers/all.csv
git commit -m "resolve conflict with stupid Greg's customer"
Git will let you commit conflict markers, so check you removed them all before you continue with a merge.
Run git log to examine your handiwork. You should see the two conflicting commits come together in the new merge commit. Conflict resolved!
Why Do You Have to Write the Merge Commit Message Yourself?
Something you may have noticed when resolving merge conflicts is that you don't get the "standard" merge commit message:
Merge branch 'main' into add_customers
You have to manually provide a message. Guh. Typing. It actually makes sense when you think about it: Git doesn't know what you did to resolve the conflict, and it's encouraging you to document your changes via a custom commit message.
What Do “Ours” and “Theirs” Mean in a Merge Conflict?
In a merge conflict:
- "Ours" refers to the branch you are on (merging into)
- "Theirs" refers to the branch being merged
# you're on the "ours" branch, the "theirs" branch is "their_branch"
git merge their_branch
That's why the top section of a conflict is labeled HEAD (that's you, "ours") and the bottom section is labeled with the name of the incoming branch ("theirs").
How Do You Keep Only One Side of a Conflict?
Manually editing files works, but when you know you just want to keep only the changes from one branch and discard the other's, there's a faster way. The git checkout command can checkout the individual changes during a merge conflict using the --theirs or --ours flags:
--ourskeeps the version of the file from your current branch (the one you're on before merging)--theirsuses the version of the file from the branch you're merging into your current branch
git checkout --theirs path/to/file
That resolves the entire file in one shot: no manual editing required. And when multiple files conflict, you can mix and match: keep "theirs" in one file, keep "ours" in another. Once every file is resolved, add and commit like usual.
How Do You Undo a Botched Merge?
Maybe you resolved the conflict the wrong way. Maybe your manager at MegaCorp™ informs you that only Greg is allowed to add customers to all.csv (stupid Greg). Either way, sometimes you need a do-over.
If you're still mid-conflict and haven't committed anything yet, git merge --abort throws away the merge attempt and puts your branch back the way it was.
If you already committed the merge, do a reset --hard back to the commit just before the merge commit:
git log --oneline
git reset --hard <commit_before_merge>
Then you can take a different approach to resolving the conflict, like re-running the merge and using git checkout --theirs this time. A hard reset rewrites your branch and discards uncommitted work, so read up on git reset before you swing that hammer, and know that git reflog can rescue you if you reset to the wrong commit.
Merges aren't the only place conflicts show up: rebases run into the exact same walls, and resolving a conflict mid-rebase has its own quirks. We cover that in git rebase conflicts, and if you'd rather practice all of this hands-on in a real terminal, that's exactly what Learn Git 2 is for.
Frequently Asked Questions
What causes a merge conflict in Git?
A merge conflict happens when two commits modify the same line of a file and the commits aren't in a parent-child relationship. Git can't automatically decide which change to keep, so it asks you to resolve it manually.
What is HEAD in a merge conflict?
HEAD marks your current branch's version of the conflicting lines. The section between <<<<<<< HEAD and ======= is 'ours', and the section between ======= and >>>>>>> is 'theirs', the incoming branch's version.
What is the difference between ours and theirs in Git?
Ours refers to the branch you're currently on, the one you're merging into. Theirs refers to the branch being merged in. You can keep one side of a conflicted file with git checkout --ours or git checkout --theirs.
How do I abort a merge in Git?
If you haven't committed yet, run git merge --abort to throw away the merge attempt. If you already committed the merge, use git reset --hard to reset your branch back to the commit before the merge.
Can Git commit unresolved conflict markers?
Yes. Git will happily let you commit files that still contain conflict markers, so double-check that you removed them all before you add and commit the resolution.
