Is Git really the end to merge conflicts?

9 July 2015

Steve looks into a common myth that Git never suffers from merge conflicts.

Many believe that the merging algorithm used in Git is a cut above the rest, and to some degree they are right - Git uses a different approach when merging the heads of a local and remote branch together - but if you're a .NET developer working with SVN or Mercurial, and believe that migrating to Git will fix all your merge conflicts with your .csproj files, then you've been fed false information.

Today we have taken steps to prove that the enthusiasts who believe Git is the answer to everyone's problems and that it doesn't suffer from merge conflicts are wrong.

The experiment

We've set up a simple dummy C# project using the out-of-the-box .NET MVC 5 pattern, and then simulated two different people (or one person) working on two different features using both git-flow and hg-flow with SourceTree. Here's how we did it:

  1. Create a new project in Visual Studio 2013 using MVC,
  2. Check the project into Git (and HG) and initialise the "flow" workflow patterns on these
  3. Separately, on the Git and HG repos:
    1. Start a new feature (the "news controller" feature)
    2. Create a new controller in the project and create an index view
    3. Commit the changes and switch back to the "develop" branch
    4. Start another new feature (the "products controller" feature) without finishing the news feature
    5. Create a new controller in the project and create an index view
    6. Commit the changes and switch back to the "develop" branch

This has enabled us to effectively simulate two pieces of work being done at the same time - in reality you may have two developers working on separate areas of a functional MVC application or you may be working individually on it, using a branch to contain each feature and not releasing these features back into "develop" until they are completely finished and tested.

Because of how Visual Studio and the .NET compiler work, we know that every time we add a code file to our C# (or VB) project, an element will be added to the project XML file to tell the compiler to include that file in the compilation. Visual Studio will also add "Content" references for any Views, Javascript, CSS and static images.

Let's have a look at how the Source Control has stored the changes:

News Controller - Git Commit

Git Changeset

News Controller - HG Commit

HG Changeset

One of the things you'll notice about these two commits is that they are exactly the same across Git and HG - both Source Control Management Systems (SCMs) read the addition of two files, and the addition of two blocks of text to the .csproj file.

Now we will look at the second branch we created, but since we know the commit details will be the same we will only look at the Git changeset - once again we see the addition of 2 files and the addition of 2 blocks of text to the .csproj file.

Git Changeset

Notice how our Controller files are both added after "ManageController" and before "Global.asax.cs" in the project file?

To continue the experiment:

  1. Using git-flow, finish the "news controller" feature - and notice that this has successfully merged back onto the "develop" branch
  2. Using git-flow, finish the "products controller" feature - and notice that this fails to merge onto "develop" because of a conflict in the .csproj file

SourceTree - Merge Conflict in Git repository

  1. In order to resolve these conflicts you must do the same as you would for HG or SVN - handle the conflicts manually

Merge conflict resolution with KDiff3

  1. In Kdiff3, this is made easy - simply choose to include from Block B followed by Block C - then just check the XML nodes are still closed properly

Our Findings

  • A good workman never blames his tools
  • If you've worked with Git on a project on your own, working on one feature at a time and never suffered from merge conflicts, it's because you've worked on your own on one feature at a time and not because the SCM has a miracle merge algorithm,
  • Git has the same problems as HG and SVN - they can't read people's minds or perform miracles,
  • If you're working on multiple features at the same time it doesn't matter how careful you are you might run into merge conflicts - just deal with them!

So what are the honest differences between Git, HG and SVN?

We thought it would be easiest to put together a diagram showing the differences we noticed between these three SCMs:

Git HG SVN
You can choose what branch(es) to pull from Remote You either pull all branches, or no branches! You can choose which branch to check out
You can have "e;secret" branches that are never pushed to remote You either push all branches, or no branches! A branch isn't a branch if it's not on the remote!
When you merge a branch, it's automatically committed When you merge a branch you can make final alterations before committing When you merge a branch you can make final alterations before committing
You may switch branches while there are uncommitted changes You may switch branches while there are uncommitted changes, if you use the command line You may switch branches while there are uncommitted changes

Closing note: yes you can use Kdiff3 with TortoiseSVN

Later on, we'll be exploring how to work as painlessly as possible with your SCM.