Skip to main content

Merge conflicts and how to resolve them

One of the most confusing (and dangerous!) things for someone new to Git are merge conflicts. A merge conflict happens when Git tries to apply two changes to the same line of code and it doesn't know which one to pick.

Here's how they can happen...

Two developers working on the same branch

  1. Two developers (John and Anne) are working on the same branch. Anne makes, commits, and pushes a set of changes. At the same time, John makes and commits locally a set of changes. When he goes to push, Git will complain because he doesn't have the commits that were pushed by Anne.
  2. Therefore, he needs to pull the changes.
  3. If both John's commits and Anne's commits modify the same lines in the same file, there will be a Merge conflict.

Let's say this is app.py before they make their commits:

greeting = "Hello, world!"
print(greeting)

Anne makes this change:

- greeting = "Hello, world!"
+ greeting = "Hello, Python!"
print(greeting)

This shows that Anne deleted line 1 and replaced it with a new line. Similar, but with a different greeting string.

Anne pushes her changes. In the meantime, John has made these changes:

- greeting = "Hello, world!"
+ name = input("Enter your name: ")
+ greeting = f"Hello, {name}"
print(greeting)

John has deleted line 1, and added 2 lines instead.

As you can see, both developers modified line 1. Therefore Git can:

  • Pick one of the two developers's changes, which means discarding someone's work. Git won't do this on its own, as it's a destructive operation.
  • Show a merge conflict and let the developer fix it.

When John pulls Anne's changes into his branch, Git will modify the file and show this:

<<<<<<< John's commit
name = input("Enter your name: ")
greeting = f"Hello, {name}"
=======
greeting = "Hello, Python!"
>>>>>>> Anne's commit
print(greeting)

This looks terrifying when you first encounter it!

But there are three parts:

  • <<<<<<< is the current place where we are. Between this and ======= are John's changes.
  • Between ======= and >>>>>>> are Anne's changes (shown by the text "Anne's commit").
  • Afterwards, outside of the crocodile clips, is the unchanged code that is not part of the merge conflict.

John has to make a decision:

  1. "Use mine": Delete Anne's code and the merge conflict boilerplate. Then commit and push.
  2. "Use theirs": Keep only Anne's code and delete the merge conflict boilerplate and his own code. Then commit and push.
  3. Rewrite the code to whatever he wants, then commit and push.

"Use mine"

This is Git-speak for "keep my changes, discard the rest".

John would have to modify the code so it once again looks like his own code:

name = input("Enter your name: ")
greeting = f"Hello, {name}"
print(greeting)

Then, commit and push. This effectively gets rid of Anne's changes.

"Use theirs"

This is Git-speak for "keep the existing changes, discard my changes".

John would have to modify the code so it looks like Anne's code:

greeting = "Hello, Python!"
print(greeting)

Then commit and push. This effectively gets rid of his changes.

Fix manually

Finally, John would change the code to whatever he wants. For example, if he thinks Anne has included some useful changes and he also wants to include some of his changes.

He could rewrite the code to be this, for example:

name = input("Enter your name: ")
greeting = f"Hello from Python, {name}"
print(greeting)

The important thing here is that he's gotten rid of the merge conflict boilerplate (the <<<<<<<, =======, and >>>>>>>), and he's committed and pushed the new, fixed code.

Merging two branches with conflicting changes

Let's say John and Anne are working on different branches. Anne is working on branch anne-changes and John is working on john-changes.

When John goes to push his changes, Git will not complain. That's because Anne's changes are in another branch entirely, so he's not missing any commits in his branch.

However, let's say that Anne then merges her branch into master.

Then, John tries to merge his branch into master.

Merge conflicts will happen because we've got the same situation as before: John's commit changes a line that another commit also changed. Git has to ask you to pick which change you want to keep.

Wrapping Up

When you're working with Git, particularly if you work with a team, you'll frequently encounter merge conflicts.

::: warning Don't "Use theirs" or "Use mine" carelessly. If you mistakenly delete one of your teammates's work, that can cause some friction in a team. The best approach is to talk to people and make sure you're doing the right thing with the changes.

A merge conflict can be an opportunity to find the best solution to a problem.

Although often they're just Git getting confused about which lines are actually colliding, especially on very large files with many changes.

Many times I have accidentally deleted important code because I thought it was part of old or obsolete code, included in a merge conflict. Be careful! :::