📜 ⬆️ ⬇️

Conflicts when merging csproj files



In the current version of GitHub for Windows, we have made a small change that has a subtle effect that you probably already noticed. We have changed the default approach for merging *.csproj and similar files.
If you change a .csproj file in a branch and then merge it with another branch, then you will probably encounter more merge conflicts than you might have had before.

Why?

Well, earlier we would use union to merge *.csproj files.
git merge-file documentation describing this function:
')
Instead of leaving the conflict in a file, resolve conflicts in favor of our (or them or both) parties.

In general, when a conflict occurs, the system tries to solve this problem, trying to apply all changes.
In principle, this is a compromise. This approach can be configured in the .gitattributes file, so if you really want to use this behavior for your repository, add the following:
 *.csproj merge=union 

And now let me tell you why maybe you don’t want to do it, and why we eventually changed it.

The merger of the union turned into a disaster


Suppose we start with the following simplified foo.csproj file in our master branch along with the .gitattributes file:
 <?xml version="1.0" encoding="utf-8"?> <Project> <PropertyGroup> <Page Include="AAA.cs"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> <Page Include="DDD.cs"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> </PropertyGroup> </Project> 


After creating this file, let's make sure we commit it.
 git init . git add -A git commit -m "Initial commit of gittattributes and foo.csproj" 


Then we create a branch ( git checkout -b branch ) under the original name “branch” and insert the following slice into foo.csproj between the AAA.cs and DDD.cs .
  <Page Include="BBB.cs"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> 


For those who have weak imagination, below is the result of what we commit to this thread.
 <?xml version="1.0" encoding="utf-8"?> <Project> <PropertyGroup> <Page Include="AAA.cs"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> <Page Include="BBB.cs"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> <Page Include="DDD.cs"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> </PropertyGroup> </Project> 


Do not forget to commit this if you act in order.
 git commit -a "Add BBB.cs element" 


Now let's go back to the master branch
 git checkout master 


And add the following piece:
 <Page Include="CCC.cs"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> 


As a result, the master branch should contain the following:
 <?xml version="1.0" encoding="utf-8"?> <Project> <PropertyGroup> <Page Include="AAA.cs"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> <Page Include="CCC.cs"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> <Page Include="DDD.cs"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> </PropertyGroup> </Project> 


And all this commits:
 git commit -a "Add CCC.cs element" 


Are you still here?
Ok, now let's merge our branch branch into master branch
 git merge branch 


As a result of using merge merge, we get the following:
 <?xml version="1.0" encoding="utf-8"?> <Project> <PropertyGroup> <Page Include="AAA.cs"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> <Page Include="CCC.cs"> <Page Include="BBB.cs"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> <Page Include="DDD.cs"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> </PropertyGroup> </Project> 


Damn (yooo), it turned out not exactly what we wanted. Notice that “BBB.cs” is embedded in “CCC.cs” and we no longer have a closing tag. It is terrible to say the least.
In the absence of a .gitattributes file in the repository and using the standard merge approach, the standard merge command resulted in a merge conflict that needs fixing. In our understanding, this is better than a blatant mistake that will remain in your project.
 <?xml version="1.0" encoding="utf-8"?> <Project> <PropertyGroup> <Page Include="AAA.cs"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> <<<<<<< HEAD <Page Include="CCC.cs"> ======= <Page Include="BBB.cs"> >>>>>>> branch <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> <Page Include="DDD.cs"> <SubType>Designer</SubType> <Generator>MSBuild:Compile</Generator> </Page> </PropertyGroup> </Project> 


Obviously, in some kind of ideal parallel universe, git would place the entire “ccc” element after the “bbb” element without ad-lib and not disturbing us about resolving these nasty conflicts of merger. We do not live in this universe, but perhaps ours may become a little more like the ideal. I heard there cool.

What should be done in Visual Studio?


I recently asked Twitter followers to vote on the question for a request to the Visual Studio team to add support for file templates in project files . MSBuild already supports jokers in .csproj files, but Visual Studio is not okay with them yet.

Reducing the agony of resolving merge conflicts is one of the main reasons for doing so. If I can use the joker symbol to specify a directory, I will not need to add entries to *.csproj every time I add a file to a project.

On the other hand, you can write a high-quality merge XML driver for Git, but this is quite a serious test, and my colleague Marcus Olsson can confirm this. If it were simple, or even difficult but in moderation, it would have been done already. Curiously, if we limit this to the usual problem of .csproj files, can we say that, although it is not an excellent one, it is a good enough solution to control ordinary merge conflicts? Maybe.

Even if we write this merge driver, it can solve the problem of only one specific version control system, which is only important for us. :trollface:

It has been suggested that if Visual Studio first sorts these items, this may help partially solve the problem. This will help reduce non-essential conflicts caused by conditionally non-deterministic sorting of elements in Visual Studio. But this does not solve the problem of merging as such. In the example I presented, each item keeps sorting throughout the entire example. It turns out that every time files in adjacent files are added in two different branches, you risk getting a conflict. What happens quite often.

Joker character support can almost completely solve this problem. Notice, I said almost . There will still be conflicts in this file from time to time, but this will happen very rarely.

ps
By the will of fate, I stumbled upon this article, decided that it would also be interesting for you to know, I personally come across this all the time.
This is my first translation, so please answer the question in the questionnaire, I would like to know whether it is worth further spending your / my time or not.

Source: https://habr.com/ru/post/221999/


All Articles