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.