The question came up during a discussion on our internal mailing list as to why Git’s merge is seen as better than the TFS merge. What better way to explain it than via a worked example:
Consider the following solution in TFS:
Now let’s branch it for two different teams. Team A and Team B.
Now let’s go into TeamA’s code and refactor SomeClass to have a better name. Here’s the changeset that gets created. Note that we’ve changed the .csproj and Program.cs as well since they both had references to SomeClass.
Meanwhile TeamB adds an extra property for some functionality they are adding:
Team A then merges back to trunk. When merging in Visual Studio you can only merge between branches that are directly related to each other.
Team B then wants to merge to trunk, but as Team A checked in first, they decide to follow good practices and pull down the latest changes from Trunk and merge them first. Because the SomeClass file was renamed we now have a merge conflict as seen here:
And until we fix it our solution is broken.
Visual Studio asks us what we want to do with the file name and we need to figure out what the right option is. Giving choice like this to the user is unnecessary and can easily result in a big mess.
Let’s do the same thing using git. We start by creating a new project and making the two team branches. Note how the branches all point to the same commit instead of creating copies of all the files.
Now let’s refactor the TeamA code and add the property to TeamB as we did with the TFS example and commit the changes. We see the branches now have some changes and are diverging from each other.
Now let’s merge the TeamA changes to TeamB. For consistency with the TFS example we’ll do it by merging TeamA to master, and then master to TeamB
Note that the merge from TeamA to master is a fast-forward merge. This happens because trunk hasn’t changed since TeamA branched so master can just be repositioned to the TeamA commit..
You can see that in the second merge from master to TeamB an auto-merge occurred, and git automatically detected that the changed SomeClass.cs file and the new SomeBetterName.cs file are actually the same file (with an 84% certainty), so it performed a rename for us automatically.
When we reload the solution in Visual Studio, everything looks fine, and we can get on with our development activities. Here’s what it looks like in the repository viewer:
As a note, with git we don’t have to go via the master branch to merge TeamA changes to TeamB, we could have just done git merge TeamA as shown here:
For reference, you don’t have to merge from TeamA to TeamB via Trunk in TFS either. TFS has a baseless merge option that you can run via the command line, though when you do the branch history can get a little messed up.
Here’s hoping that the next version of TFS will have a much better story around rename detection and version control in general.