Reduce Merge Conflicts in .NET Solutions with PowerShell based Git Hooks
by Howard van Rooijen
If you use Git (and GitFlow) with Visual Studio (which seems like it is going to become a much more popular combination), I’m sure you are well aware of the pain that is the “merge conflict” you often encounter when trying to merge (or rebase) a branch. There are three file types that are particularly prone to causing merge conflicts and will send a shiver up the spine of any .NET Developer; App.config, Web.config and any .csproj file.
App.config & Web.config files are prone to conflicts when appSettings are altered, .csproj files are prone to conflicts when files have been added / removed on separate branches. When you add NuGet into the mix, the level of pain increases by an order of magnitude; in .csproj files references to NuGet packages are changed (and the elements are often randomly reordered), with .config file assembly binding redirects change and are also often reordered.
Automatically formatting these files to ensure that the elements that often conflict are sorted in alphabetical order, should help to minimise conflicts, or at the very least make it much easier to perform a manual merge.
Git has an extensibility mechanism called Hooks. These are files that live in the .git\hooks directory and are scripts which are executed when a particular Git lifecycle event, corresponding the the name of the Hook file, is triggered. Every time you create a Git repo a series of sample Hooks are created in the .git\hooks directory:
The files are all appended with .sample, which means that they won’t execute; to enable a Hook, remove the .sample file extension. It really is a beautifully simple extensibility mechanism.
Although Git Hooks live in the .git directory, they are not committed into the repo, if you want your team to adopt a particular hook you have to distribute them via other means (perhaps a standard repo and use a Symlink to add it into each .git\hooks folder of the repo you want to add it to). Git Hooks can be written in Ruby, Perl & Python, but I’m a PowerShell fan (especially when it comes to manipulating XML files), so the first obstacle to overcome is how to invoke a PowerShell Script from inside a Git Hook. The answer is quite straight forward:
This above Hook will execute AutoFix-VisualStudioFiles.ps1 every time you attempt to commit a change. This script executed the following rules:
For App.config & Web.config, it will sort appSetting element alphabetically, by key and it will sort assemblyBinding.dependentAssembly elements alphabetically based on the assemblyIdentity.name attribute
For .csproj files, it will sort appSetting element alphabetically, by key and it will sort Reference, ProjectReference & Compile elements alphabetically by the Include attribute value.
Every time you commit a file, the script will run and process alll .config & .csprof file it detects and fail the commit so that you can manually check the files to ensure everything is correct. If you need to disable the Hook, simple change the name of pre-commit to precommit.sample
The Hook integrates with any Git IDE (i.e. SmartGit in the screenshot below)
and also works perfectly well from the command prompt:
Howard spent 10 years as a technology consultant helping some of the UK's best known organisations work smarter, before founding endjin in 2010. He's a Microsoft ScaleUp Mentor, and a Microsoft Azure MVP. You can follow him on Twitter via @HowardvRooijen