Search code examples
c#gitcsproj

Reduce number of unnecessary merge conflicts in PackageReference in csproj files


I'm working on a (very) large C# project with a significant number of external packages. I'm trying mitigate the problem of unnecessary merge conflicts in *.csproj files caused by package updates.

The new style of PackageReference that has a Version attribute is very prone to merge conflicts if packages are updated on adjacent lines:

<PackageReference Include="Package1" Version="1.0.0" />
<PackageReference Include="Package2" Version="1.0.0" />
<PackageReference Include="Package3" Version="1.0.0" />

Given a csproj file with the above content, if one developer bumps the version of Package1 and Package3 while another bumps Package2, the two conflict. This requires unnecessary manual work although no real conflict has occurred - all three packages have been bumped.

So far, I explored three possible solutions, but haven't found a clear winner:

  1. Setting a diff driver for *.csproj files using .gitattributes. However, none of the built-in diff drivers seem to produce a cleaner diff result (not surprising, given none of them is meant for csproj or even xml files).

  2. Forcing the usage of the old style of PackageReference, in which Version isn't an attribute but an xml element: <Version>1.0.0</Version>. This causes significantly fewer conflicts due to the extra lines. However, I couldn't find a clear way of forcing VS/Rider/Nuget to use the old style, so we'd need to distribute a custom git hook to everyone working on the project to force it.

  3. Distributing the most frequently updated packages to external .targets files, then importing these files using <Import>. This works, but requires custom tooling, especially for adding new references in a consistent manner. This also seems to (partially) break the "Manage NuGet" functionality of both VS (2022) and Rider, though that isn't a big deal.

Can anyone suggest other approaches or ways to improve on the ones above?


Solution

  • For anyone in the same boat, we ended up going with CentralPackageVersions with packages.props being in the "long" format in which Version is an element, not an attribute:

    <PackageReference Update="Foo">
        <Version>1.0.0</Version>
    </PackageReference>
    

    It's also worth pointing out that Microsoft recently introduced Central Package Managemet which is supposed to be supported by Visual Studio and other tooling. However, it requires new versions of just about everything:

    The feature is available across all NuGet integrated tooling.

    • Visual Studio 2022 17.2 and later
    • .NET SDK 6.0.300 and later
    • .NET SDK 7.0.0-preview.4 and later
    • nuget.exe 6.2.0 and later