├── .gitattributes ├── .gitignore ├── AutoFix-VisualStudioFiles ├── AutoFix-VisualStudioFiles.Tests.ps1 ├── AutoFix-VisualStudioFiles.ps1 └── pre-commit ├── License.md └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | *.bmp binary 2 | *.dll binary 3 | *.gif binary 4 | *.jpg binary 5 | *.png binary 6 | *.snk binary 7 | *.exe binary 8 | *.wmv binary 9 | *.mp4 binary 10 | *.ismv binary 11 | *.isma binary 12 | 13 | *.ascx text 14 | *.cmd text 15 | *.config text 16 | *.cs text diff=csharp 17 | *.csproj text merge=union 18 | *.edmx text 19 | 20 | *.htm text 21 | *.html text 22 | 23 | *.js text 24 | *.json text 25 | 26 | *.msbuild text 27 | *.nuspec text 28 | 29 | *.resx text 30 | *.ruleset text 31 | *.StyleCop text 32 | *.targets text 33 | *.txt text 34 | *.xml text 35 | 36 | *.sln text eol=crlf merge=union -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studo 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | *_i.c 42 | *_p.c 43 | *_i.h 44 | *.ilk 45 | *.meta 46 | *.obj 47 | *.pch 48 | *.pdb 49 | *.pgc 50 | *.pgd 51 | *.rsp 52 | *.sbr 53 | *.tlb 54 | *.tli 55 | *.tlh 56 | *.tmp 57 | *.tmp_proj 58 | *.log 59 | *.vspscc 60 | *.vssscc 61 | .builds 62 | *.pidb 63 | *.svclog 64 | *.scc 65 | 66 | # Chutzpah Test files 67 | _Chutzpah* 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | *.cachefile 76 | 77 | # Visual Studio profiler 78 | *.psess 79 | *.vsp 80 | *.vspx 81 | 82 | # TFS 2012 Local Workspace 83 | $tf/ 84 | 85 | # Guidance Automation Toolkit 86 | *.gpState 87 | 88 | # ReSharper is a .NET coding add-in 89 | _ReSharper*/ 90 | *.[Rr]e[Ss]harper 91 | *.DotSettings.user 92 | 93 | # JustCode is a .NET coding addin-in 94 | .JustCode 95 | 96 | # TeamCity is a build add-in 97 | _TeamCity* 98 | 99 | # DotCover is a Code Coverage Tool 100 | *.dotCover 101 | 102 | # NCrunch 103 | _NCrunch_* 104 | .*crunch*.local.xml 105 | 106 | # MightyMoose 107 | *.mm.* 108 | AutoTest.Net/ 109 | 110 | # Web workbench (sass) 111 | .sass-cache/ 112 | 113 | # Installshield output folder 114 | [Ee]xpress/ 115 | 116 | # DocProject is a documentation generator add-in 117 | DocProject/buildhelp/ 118 | DocProject/Help/*.HxT 119 | DocProject/Help/*.HxC 120 | DocProject/Help/*.hhc 121 | DocProject/Help/*.hhk 122 | DocProject/Help/*.hhp 123 | DocProject/Help/Html2 124 | DocProject/Help/html 125 | 126 | # Click-Once directory 127 | publish/ 128 | 129 | # Publish Web Output 130 | *.[Pp]ublish.xml 131 | *.azurePubxml 132 | # TODO: Comment the next line if you want to checkin your web deploy settings 133 | # but database connection strings (with potential passwords) will be unencrypted 134 | *.pubxml 135 | *.publishproj 136 | 137 | # NuGet Packages 138 | *.nupkg 139 | # The packages folder can be ignored because of Package Restore 140 | **/packages/* 141 | # except build/, which is used as an MSBuild target. 142 | !**/packages/build/ 143 | # Uncomment if necessary however generally it will be regenerated when needed 144 | #!**/packages/repositories.config 145 | 146 | # Windows Azure Build Output 147 | csx/ 148 | *.build.csdef 149 | 150 | # Windows Store app package directory 151 | AppPackages/ 152 | 153 | # Others 154 | *.[Cc]ache 155 | ClientBin/ 156 | [Ss]tyle[Cc]op.* 157 | ~$* 158 | *~ 159 | *.dbmdl 160 | *.dbproj.schemaview 161 | *.pfx 162 | *.publishsettings 163 | node_modules/ 164 | bower_components/ 165 | 166 | # RIA/Silverlight projects 167 | Generated_Code/ 168 | 169 | # Backup & report files from converting an old project file 170 | # to a newer Visual Studio version. Backup files are not needed, 171 | # because we have git ;-) 172 | _UpgradeReport_Files/ 173 | Backup*/ 174 | UpgradeLog*.XML 175 | UpgradeLog*.htm 176 | 177 | # SQL Server files 178 | *.mdf 179 | *.ldf 180 | 181 | # Business Intelligence projects 182 | *.rdl.data 183 | *.bim.layout 184 | *.bim_*.settings 185 | 186 | # Microsoft Fakes 187 | FakesAssemblies/ 188 | 189 | # Node.js Tools for Visual Studio 190 | .ntvs_analysis.dat 191 | 192 | # Visual Studio 6 build log 193 | *.plg 194 | 195 | # Visual Studio 6 workspace options file 196 | *.opt 197 | -------------------------------------------------------------------------------- /AutoFix-VisualStudioFiles/AutoFix-VisualStudioFiles.Tests.ps1: -------------------------------------------------------------------------------- 1 | $here = Split-Path -Parent $MyInvocation.MyCommand.Path 2 | $sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path).Replace(".Tests.", ".") 3 | . "$here\$sut" 4 | 5 | $originalContent1 = @' 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {170967A8-EA91-4E41-B4B3-EFC0831A8FB6} 20 | CCC 21 | 22 | 23 | {170967A8-EA91-4E41-B4B3-EFC0831A8FB6} 24 | AAA 25 | 26 | 27 | 28 | '@ 29 | 30 | $referenceContent1 = @' 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | {170967A8-EA91-4E41-B4B3-EFC0831A8FB6} 45 | AAA 46 | 47 | 48 | {170967A8-EA91-4E41-B4B3-EFC0831A8FB6} 49 | CCC 50 | 51 | 52 | 53 | '@ 54 | 55 | Describe "AutoFix-CsProj" { 56 | Context "the csproj contains item groups with single element types of Reference Compile and ProjectReference" { 57 | $testPath = "TestDrive:\test.csproj" 58 | Set-Content $testPath -value $originalContent1 59 | $modifiedFiles = AutoFix-CsProj $testPath 60 | $modifiedContent = Get-Content $testPath -Raw 61 | 62 | It "should sort all child elements" { 63 | $modifiedContent | Should Be $referenceContent1 64 | } 65 | } 66 | } 67 | 68 | $originalContent2 = @' 69 | 70 | 71 | 72 | False 73 | $(SolutionDir)packages\Antlr.3.4.1.9004\lib\Antlr3.Runtime.dll 74 | 75 | 76 | 77 | 78 | 79 | '@ 80 | 81 | $referenceContent2 = @' 82 | 83 | 84 | 85 | 86 | 87 | False 88 | $(SolutionDir)packages\Antlr.3.4.1.9004\lib\Antlr3.Runtime.dll 89 | 90 | 91 | 92 | '@ 93 | 94 | Describe "AutoFix-CsProj" { 95 | Context "the csproj contains item groups with mixed child elements" { 96 | $testPath = "TestDrive:\test.csproj" 97 | Set-Content $testPath -value $originalContent2 98 | $modifiedFiles = AutoFix-CsProj $testPath 99 | $modifiedContent = Get-Content $testPath -Raw 100 | 101 | It "should sort and keep all child elements" { 102 | $modifiedContent | Should Be $referenceContent2 103 | } 104 | } 105 | } 106 | 107 | $originalContent3 = @' 108 | 109 | 110 | 111 | 112 | 113 | 114 | '@ 115 | 116 | $referenceContent3 = @' 117 | 118 | 119 | 120 | 121 | 122 | '@ 123 | 124 | Describe "AutoFix-CsProj" { 125 | Context "the csproj contains item groups with duplicate children" { 126 | $testPath = "TestDrive:\test.csproj" 127 | Set-Content $testPath -value $originalContent3 128 | $modifiedFiles = AutoFix-CsProj $testPath 129 | $modifiedContent = Get-Content $testPath -Raw 130 | 131 | It "should remove the duplicate item group children" { 132 | $modifiedContent | Should Be $referenceContent3 133 | } 134 | } 135 | } 136 | 137 | $originalContent4 = @' 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | '@ 153 | 154 | $referenceContent4 = @' 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | '@ 170 | 171 | Describe "AutoFix-WebConfig" { 172 | Context "the web.config contains multiple bindingRedirects with equal name" { 173 | $testPath = "TestDrive:\web.config" 174 | Set-Content $testPath -value $originalContent4 175 | $modifiedFiles = AutoFix-WebConfig $testPath 176 | $modifiedContent = Get-Content $testPath -Raw 177 | 178 | It "should sort the duplicate bindingRedirect by name and publicKeyToken" { 179 | $modifiedContent | Should Be $referenceContent4 180 | } 181 | } 182 | } 183 | 184 | $originalContent5 = @' 185 | 186 | 187 | 188 | ccc 189 | 190 | 191 | aaa 192 | 193 | 194 | '@ 195 | 196 | $referenceContent5 = @' 197 | 198 | 199 | 200 | aaa 201 | 202 | 203 | ccc 204 | 205 | 206 | '@ 207 | 208 | Describe "AutoFix-Resx" { 209 | Context "the resx contains multiple data with different name" { 210 | $testPath = "TestDrive:\Strings.resx" 211 | Set-Content $testPath -value $originalContent5 212 | $modifiedFiles = AutoFix-Resx $testPath 213 | $modifiedContent = Get-Content $testPath -Raw 214 | 215 | It "should sort the data by name" { 216 | $modifiedContent | Should Be $referenceContent5 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /AutoFix-VisualStudioFiles/AutoFix-VisualStudioFiles.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .SYNOPSIS 3 | 4 | Scans the solution folder (the parent of the .git folder) for all app.config, web.config and *.csproj files and 5 | auto-formats them to minimise the possibility of getting merge conflicts based on the ordering of elements 6 | within these files. 7 | N.B. Use of this script is entirely at your own risk. We shall not be liable for any damage which may result from using it. 8 | 9 | .DESCRIPTION 10 | 11 | app.config & web.config files - sorts appSettings elements by key, in alphabetic order, sorts assemblyBinding.dependentAssembly 12 | elements alphabetically based on the assemblyIdentity.name attribute 13 | .csproj files - sorts appSettings elements by key, in alphabetic order, sorts Reference, ProjectReference & Compile elements 14 | 15 | .NOTES 16 | 17 | File Name : AutoFix-VisualStudioFiles.ps1 18 | Author : Howard van Rooijen 19 | Requires : PowerShell v3 20 | 21 | .LINK 22 | 23 | #> 24 | 25 | Function AutoFix-WebConfig([string] $rootDirectory) 26 | { 27 | $files = Get-ChildItem -Path $rootDirectory -Filter web.config -Recurse 28 | 29 | return Scan-ConfigFiles($files) 30 | } 31 | 32 | Function AutoFix-AppConfig([string] $rootDirectory) 33 | { 34 | $files = Get-ChildItem -Path $rootDirectory -Filter app.config -Recurse 35 | 36 | return Scan-ConfigFiles($files) 37 | } 38 | 39 | Function Scan-ConfigFiles([System.IO.FileInfo[]] $files) 40 | { 41 | $modifiedfiles = @() 42 | 43 | foreach($file in $files) 44 | { 45 | Try 46 | { 47 | $original = [xml] (Get-Content $file.FullName) 48 | $workingCopy = $original.Clone() 49 | 50 | if($workingCopy.configuration.appSettings -ne $null -and $workingCopy.configuration.appSettings.ChildNodes.Count -gt 1) 51 | { 52 | $sorted = $workingCopy.configuration.appSettings.add | sort { [string]$_.key } 53 | $lastChild = $sorted[-1] 54 | $sorted[0..($sorted.Length-2)] | foreach {$workingCopy.configuration.appSettings.InsertBefore($_, $lastChild)} | Out-Null 55 | } 56 | 57 | if ($workingCopy.configuration.runtime.assemblyBinding -ne $null -and $workingCopy.configuration.runtime.assemblyBinding.ChildNodes.Count -gt 1){ 58 | $sorted = $workingCopy.configuration.runtime.assemblyBinding.dependentAssembly | sort { [string]$_.assemblyIdentity.name; [string]$_.assemblyIdentity.publicKeyToken } 59 | $lastChild = $sorted[-1] 60 | $sorted[0..($sorted.Length-2)] | foreach {$workingCopy.configuration.runtime.assemblyBinding.InsertBefore($_,$lastChild)} | Out-Null 61 | } 62 | 63 | $differencesCount = (Compare-Object -ReferenceObject (Select-Xml -Xml $original -XPath "//*") -DifferenceObject (Select-Xml -Xml $workingCopy -XPath "//*")).Length 64 | 65 | if ($differencesCount -ne 0) 66 | { 67 | $workingCopy.Save($file.FullName) | Out-Null 68 | $modifiedfiles += $file.FullName 69 | } 70 | 71 | } 72 | Catch 73 | { 74 | $ErrorMessage = $_.Exception.Message 75 | Write-Host "Scan-ConfigFiles::: Reorder error en: " $file.FullName "=>>>" $ErrorMessage 76 | } 77 | } 78 | 79 | return $modifiedfiles 80 | } 81 | 82 | Function AutoFix-CsProj([string] $rootDirectory) 83 | { 84 | $files = Get-ChildItem -Path $rootDirectory -Filter *.csproj -Recurse 85 | $modifiedfiles = @() 86 | 87 | foreach($file in $files) 88 | { 89 | $original = [xml] (Get-Content $file.FullName) 90 | $workingCopy = $original.Clone() 91 | 92 | foreach($itemGroup in $workingCopy.Project.ItemGroup) 93 | { 94 | # Sort the ItemGroup elements 95 | $sorted = $itemGroup.ChildNodes | sort Name, Include -Unique 96 | 97 | if ($itemGroup -isnot [System.String]) # skip empty ItemGroups 98 | { 99 | $itemGroup.RemoveAll() | Out-Null 100 | 101 | foreach($item in $sorted){ 102 | $itemGroup.AppendChild($item) | Out-Null 103 | } 104 | } 105 | } 106 | 107 | $differencesCount = (Compare-Object -ReferenceObject (Select-Xml -Xml $original -XPath "//*") -DifferenceObject (Select-Xml -Xml $workingCopy -XPath "//*")).Length 108 | 109 | if ($differencesCount -ne 0) 110 | { 111 | $workingCopy.Save($file.FullName) | Out-Null 112 | $modifiedfiles += $file.FullName 113 | } 114 | } 115 | 116 | return $modifiedfiles 117 | } 118 | 119 | Function AutoFix-Resx([string] $rootDirectory) 120 | { 121 | $files = Get-ChildItem -Path $rootDirectory -Filter *.resx -Recurse 122 | $modifiedfiles = @() 123 | 124 | foreach($file in $files) 125 | { 126 | Try 127 | { 128 | $original = [xml] (Get-Content $file.FullName) 129 | $workingCopy = $original.Clone() 130 | 131 | if($workingCopy.root -ne $null -and $workingCopy.root.data.Count -gt 1) 132 | { 133 | $sorted = $workingCopy.root.data | sort { [string]$_.name } 134 | $lastChild = $sorted[-1] 135 | $sorted[0..($sorted.Length-2)] | foreach {$workingCopy.root.InsertBefore($_, $lastChild)} | Out-Null 136 | } 137 | 138 | $differencesCount = (Compare-Object -ReferenceObject (Select-Xml -Xml $original -XPath "//*") -DifferenceObject (Select-Xml -Xml $workingCopy -XPath "//*")).Length 139 | 140 | if ($differencesCount -ne 0) 141 | { 142 | $workingCopy.Save($file.FullName) | Out-Null 143 | $modifiedfiles += $file.FullName 144 | } 145 | } 146 | Catch 147 | { 148 | $ErrorMessage = $_.Exception.Message 149 | Write-Host "AutoFix-Resx::: Reorder error en: " $file.FullName "=>>>" $ErrorMessage 150 | } 151 | } 152 | 153 | return $modifiedfiles 154 | } 155 | 156 | $rootDirectory = Join-Path (Split-Path -Parent $MyInvocation.MyCommand.Path) "\..\..\" 157 | 158 | $exitCode = 0; 159 | 160 | $changedfiles = @() 161 | $changedfiles += AutoFix-AppConfig($rootDirectory) 162 | $changedfiles += AutoFix-CsProj($rootDirectory) 163 | $changedfiles += AutoFix-WebConfig($rootDirectory) 164 | $changedfiles += AutoFix-Resx($rootDirectory) 165 | 166 | if ($changedfiles.Count -gt 0) 167 | { 168 | Write-Host "=== endjin git hooks ===" 169 | Write-Host "The following files have been auto-formatted" 170 | Write-Host "to reduce the likelyhood of merge conflicts:" 171 | 172 | foreach($file in $changedfiles) 173 | { 174 | Write-Host $file 175 | } 176 | 177 | $exitCode = 1; 178 | } 179 | 180 | exit $exitcode 181 | -------------------------------------------------------------------------------- /AutoFix-VisualStudioFiles/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo 3 | exec powershell -NoProfile -ExecutionPolicy Bypass -File ".git\hooks\AutoFix-VisualStudioFiles.ps1" 4 | exit -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 endjin limited 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | All licensing enquiries should be directed in the first instance to licensing@endjin.com -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # endjin's git hooks 2 | 3 | This repo contains a series of git hook in use at endjin. 4 | 5 | To use a git hook, take the contents of one of the folder and copy it to the .git\hook folder of the repo you wish to install it in. 6 | 7 | For more information please read the original blog post "[Reduce Merge Conflicts in .NET Solutions with PowerShell based Git Hooks](https://blogs.endjin.com/2015/05/reduce-merge-conflicts-in-dot-net-solutions-with-powershell-based-git-hooks/)" 8 | 9 | **Contributors:** 10 | 11 | - Paco Rodriguez 12 | - Omid Rad 13 | - Martin Meixger 14 | - Laurent Kempe 15 | 16 | --- 17 | 18 | @[HowardvRooijen](http://twitter.com/howardvrooijen) | @[endjin](http://twitter.com/endjin) 19 | --------------------------------------------------------------------------------