Git: how to resolve merge conflicts

When working in several people on the same part of code, you will sooner or later end up having merge conflicts. At first merge conflict may seem overwhelming but once you resolve few of them, they will cease to be something difficult or scary. In this article I would like to explain why conflicts occur and how to deal with them in your day-to-day work.

What is merge conflict

When you are merging some changes into your current branch, Git tries to merge them automatically as much as possible. It means that for every change in the source branch (the branch you want to merge into your current branch) Git will try to find the right place in one of the files and will try to apply the change. If everything go smoothly and Git has no problem in finding the right place and applying the changes, the whole merge operation will be fully automatic and will not require any user interaction.

However, if Git has any problems or doubts when merging, it will try to merge automatically as much as it can and then will report the list of all merge conflicts it has found and couldn’t resolve by itself:

$ git merge src-branch
Auto-merging filevisitor/src/main/java/com/example/filevisitor/PrintingFileVisitor.java
CONFLICT (content): Merge conflict in filevisitor/src/main/java/com/example/filevisitor/PrintingFileVisitor.java
Automatic merge failed; fix conflicts and then commit the result.

In this case we have one file which was changed in both current and src-branch branches and which could not be merged automatically.

Of course, merge conflicts happen not only when explicitly merging branches using git merge command but may also occur when pulling the changes from remote (e.g. git pull) or when applying the changes from stash into your working copy.

To make it complete I would like to point several common root causes of merge conflicts:

  • modifying the same parts (lines) of the file in both branches
  • replacing the contents of the file (e.g. replacing image or other binary file) in both branches
  • removing the file in one branch but modifying it in another

Presentation of conflicts

When you open a file with merge conflicts in your favourite text editor or IDE and scroll through it, you will quickly notice special markers <<<<<<<, ======= and >>>>>> in it:

    public PrintingFileVisitor() {
        prefix = new StringBuffer();
        fileCount = 0;
<<<<<<< HEAD
        dirCount = 0;
=======
        allCount = 0;
>>>>>>> src-branch
    }

These markers mark the parts of the code which Git was unable to merge automatically and which must be merged manually. The part above ======= comes from HEAD – the top of your current branch. The bottom part is more interesting because it contains the changes from the source branch and which could not be merged into your current branch. A quick look at the example shows that field dirCount was renamed to allCount in source branch and this is the cause of the conflict.

Of course, this is a very simplistic example and in real cases single conflict may span several lines and there may be several conflicts in the same file.

Resolving the conflict

The idea of resolving the conflict is simple: just modify the files so that everybody will be happy (including you, your boss and the team members who made the changes on the source branch) and commit them. Generally, you should select the right changes from your current branch (top part) and from the source branch (bottom part) and mix them together to form working code.

When you are done, you should remove the markers, make sure that the file compiles successfully (in all programming languages I know leaving markers cause compilation errors) and call git add to mark the conflict as resolved:

$ git add file_with_resolved_conflicts

This will also add the file to the index. After you repeat the same process for other conflicting files, you can safely commit your changes using git commit command:

$ git commit -m 'Merged with branch src-branch and resolved the conflicts.'

If you don’t know how to mix both parts of the code together, you should consult your team members for explanation of their changes and help.

Choosing one version

Sometimes after looking at all conflicts in a file, you decide that you don’t want to mix changes from both branches but instead you want to use the file from one specific branch. In this case you can either choose the file from your current branch and reject any changes from the source branch using command:

$ git checkout --ours file_name

or choose the file from the source branch and reject any changes from your current branch:

$ git checkout --theirs file_name

While very useful these commands should be used carefully because they increase the risk of mistakenly loosing some changes (e.g. very important bug fixes) from one of the branches.

When you have a conflict in a binary file, editing the file manually is not an option and these commands are the best way to resolve the conflict.

Aborting the merge

At any time before you commit the changes, you may decide to abort the whole merge operation using command:

$ git merge --abort

It will revert the state of the working copy to the moment it was before issuing git merge command. Then you may try to merge the same branch again if you want.

This command is especially useful when you have made a lot of wrong decisions when resolving the conflicts and you would like to start merging from scratch.

Merge with deleted files

Merge conflict may also occur when the file was modified in one branch and it was deleted in another branch. Here is a sample situation:

[robert@epsilon filevisitor]$ git merge src-branch 
CONFLICT (modify/delete): filevisitor/src/main/java/com/example/filevisitor/PrintingFileVisitor.java deleted in src-branch and modified in HEAD. Version HEAD of filevisitor/src/main/java/com/example/filevisitor/PrintingFileVisitor.java left in tree.
Automatic merge failed; fix conflicts and then commit the result.
[robert@epsilon filevisitor]$ git status
On branch dest-branch
You have unmerged paths.
  (fix conflicts and run "git commit")

Unmerged paths:
  (use "git add/rm ..." as appropriate to mark resolution)

        deleted by them:    src/main/java/com/example/filevisitor/PrintingFileVisitor.java

Resolving this type of conflict is pretty easy. You just have to tell Git whether you want to keep the file in your current branch using command:

$ git add file_name

or if you want to remove it completely:

$ git rm file_name

Conclusion

The only difficult part when resolving a merge conflict is properly mixing code from both branches so that no feature or bug fix is lost. If you are unsure whether it was done correctly, you can run all test cases or even test the functionality manually before you finally commit the changes. And if needed you may also ask your team members for help.

You may always consult git-merge and git-checkout manual pages for more information.

Advertisement

About Robert Piasecki

Husband and father, Java software developer, Linux and open-source fan.
This entry was posted in Git, Version control and tagged , . Bookmark the permalink.

8 Responses to Git: how to resolve merge conflicts

  1. sazzer says:

    Git has a builtin command for helping to resolve conflicts. This is the “git mergetool” command. What it does is it works through all of the conflicts in order, opening them in the configured editor – if nothing is configured it prompts you – and then automatically does the “git add” at the end. This means that if you get a merge conflict you simply run “git mergetool”, work through the list and you’re done.
    The only thing it fails with is when your merging a rebase and it ends up with all changes being removed, because you then have to do a “git rebase –skip” by hand instead of a “git rebase –continue”, but I think this is more a failing of rebase than mergetool…

  2. Tobias Gierke says:

    If you’re using Linux, I highly recommend using kdiff3 as merge tool , it has a nice ‘Solve simple conflicts/whitespace conflicts automatically’ options. Just install it and add this to your ~/.gitconfig
    [merge]
    tool = kdiff3

  3. Pingback: Git: branching and merging | softwarecave

  4. search says:

    Hiya very cool web site!! Man .. Excellent ..
    Wonderful .. I will bookmark your blog and take the feeds additionally?
    I am happy to seek out so many useful info here within the put up, we’d like develop more techniques in this
    regard, thanks for sharing. . . . . .

  5. Pingback: Git: how to resolve merge conflicts | softwarecave « The Wiert Corner – irregular stream of stuff

  6. Pingback: GIT diff & merge skills (Continuously updated) | Hi, I'm Allex.

  7. I think Merge Conflicts in Git can also be resolved by using git mergetool (as mentioned here: https://www.cloudways.com/blog/manage-branches-and-resolve-conflicts-git/ ). I have found it an easier way of resolving the conflicts.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.