Resolving Git Merge Conflicts

Johnny Simpson - Oct 23 '22 - - Dev Community

Merge conflicts are often a part of life for working on complex development projects. In this guide we'll look at how you can deal with them, and when they will occur.

When do merge conflicts occur?

A merge conflict in Git will occur if you have made changes to the same file on a the branch you are on, and the one you are trying to merge into. In this case, Git won't know which one should be used - so it will throw a merge conflict error.

Consider the scenario where a change has been made to file.md in the main branch and my-branch. When we checkout main and try to merge my-branch into main, we will run into the following error:

git merge my-branch

Auto-merging file.md
CONFLICT (content): Merge conflict in file.md
Automatic merge failed; fix conflicts and then commit the result.
Enter fullscreen mode Exit fullscreen mode

Git doesn't know what to do here, so it's our job to try and resolve it.

Stabilizing the current branch

Sometimes, when we try to merge a file, the merge will fail completely:

error: Your local changes to the following files would be overwritten by merge:
        file.md
Please commit your changes or stash them before you merge.
Enter fullscreen mode Exit fullscreen mode

This happens because we have uncommitted changes on our current branch. We need to close all changes on our current branch before we can merge. To do that, you can run git stash (I covered git stash in more detail here), or you can simply commit your changes on this branch to stabilize it:

git add -A
git commit -m "My commit message"
Enter fullscreen mode Exit fullscreen mode

Now that our branch is stabilised, we can try to run git merge my-branch (where my-branch is your branch) to merge our branch back into main. If we run into merge conflicts now, we need to resolve them.

How to resolve merge conflicts in git via GUI

If you're using a modern code editor like Visual Studio Code, then merge conflicts will appear in the GUI where you can accept incoming changes or accept the current changes on your branch. This provides a pretty easy way to fix your conflicts. In Visual Studio Code, this is represented in conflicting files via the GUI shown below. You have a number of options:

  • "Accept Current Changes" - this will use your current branch's content.
  • "Accept Incoming Changes" - this will use the content from the branch you are merging in.
  • "Accept Both Changes" - this will keep both, one under the other.
  • "Compare Changes" - this will bring up an additional screen to compare changes side by side.

Resolving Git Merge Conflicts via Visual Studio Code

Once you review all conflicts, you can then add your files and commit them as normal:

git add -A
git commit -m "My commit message"
Enter fullscreen mode Exit fullscreen mode

How to resolve merge conflicts in git via Terminal

In some cases, you may not have a GUI to resolve merge conflicts. There are a number of commands you can use.

First, git status will tell you the files affected:

git status
On branch my-branch
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   file.md

no changes added to commit (use "git add" and/or "git commit -a")
Enter fullscreen mode Exit fullscreen mode

As we can see, only file.md is affected. Next, we can use git diff to see the difference between this branch and the one we are merging into:

++<<<<<<< HEAD
 +Some  a
 +Some b
 +Some conflict c 
 +Some conflict d
 +Some e
++=======
+ Some conflict 1
+ Some b
+ Some conflict c
+ Some conflict 2
 -Some f
++Some f
++>>>>>>> main
Enter fullscreen mode Exit fullscreen mode

Here, everything after ++<<<<<<< and before ++======= represents the current branch changes, while anything after ++======= and before ++>>>>>>> main represents the branch you are merging in.

We can also see the conflicting commits by running git log --merge

Aborting your merge during a merge conflict

Sometimes you realise you're in a bit too deep on this merge conflict - in which case you can undo it by running git merge --abort.

git merge --abort
Enter fullscreen mode Exit fullscreen mode

This will bring us back to a non-merge state so we can resolve merge conflicts manually in both branches, before trying to merge again.

Resolving by accepting incoming or current changes

Another easy way to resolve merge conflicts on certain files is to use the --theirs and --ours option on git checkout. Our merge conflict was on file.md. If I want to accept all incoming changes for file.md from the branch I am trying to merge, I could write:

git checkout --theirs file.md
Enter fullscreen mode Exit fullscreen mode

This checks out the file.md from "their" version of code. If I wanted to use the version in my current branch, I'd use --ours. Both --ours and --theirs expects a file as an argument - so this has to be done on a file by file basis.

Easy, right? That's fine for merging in entire files, but what if we have multiple merge conflicts within a file?

Resolving by editing the file itself

Remember, when you create a git merge conflict, we ended up with content that looked like this when we ran git diff?

++<<<<<<< HEAD
 +Some  a
 +Some b
 +Some conflict c 
 +Some conflict d
 +Some e
++=======
+ Some conflict 1
+ Some b
+ Some conflict c
+ Some conflict 2
 -Some f
++Some f
++>>>>>>> main
Enter fullscreen mode Exit fullscreen mode

That content also exists in your file where the conflict happens (without the + and -, of course). The easiest way to resolve a merge conflict when you want to do it without any help from a GUI is to go to the file in question, and simply remove the section you don't want to accept. That just means deleting the text between <<<<<<< HEAD and ======= if you want to accept current, or the text between ======= and >>>>>>> if you want to accept incoming.

Adding and commiting your changes

Once you've resolved your merge conflicts, don't forget to add and commit your changes on the current branch, so they remain saved:

git add -A
git commit -m "Merge message"
Enter fullscreen mode Exit fullscreen mode

Conclusion

We have powerful tools in our code editors to resolve merge conflicts. Having said that, sometimes limitations in environments mean we have to resolve our own git merge errors - or we may just prefer doing this work from terminal. Some specific edge cases may also mean we have to do this work from terminal.

In any case, merge conflicts are a part of development with git, and they can be resolved simply by removing the content we don't want anymore from the files where the conflicts exist. We can also use git checkout --theirs or git checkout --ours to resolve issues more quickly.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .