Project Dependencies will break with branching if not done properly

In the February 2011 issue of MSDN Magazine, I wrote an article on Shared Code (or code dependencies as some call it). Often I see a common problem with relative references to dependent assemblies or projects. If relative references are not created properly for a solution that is branched, the relative references may break. Since we may not have fully explained the problem and some possible options in this article, I am writing this blog to clarify this issue:

In order for relative references to work properly, the folder depth relative to your project references should not change when you branch, or your shared project should be in a folder that is subordinate to the branch.

For example, I have created a Main branch with a solution (WindowsCalculator). Under the Main branch I create a binaries folder and copy the DLL (AdvancedOperations.dll) I want to use as an assembly reference into this binaries folder. If I look at the WindowsCalculator.csproj file in notepad I see the reference:

<ItemGroup>
<Reference Include="AdvancedOperations, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\Binaries\AdvancedOperations.dll</HintPath>
</Reference>

Later, when I branch $\DependentTeamProject\WindowsCalculator\Main to $\DependentTeamProject\WindowsCalculator\Development\Dev, the relative reference works in the new branch, because the DLL is in a subfolder of the branch and retains the same relative location to the csproj file:

<Reference Include="AdvancedOperations, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\..\Binaries\AdvancedOperations.dll</HintPath>
</Reference>

However, if I put the dependent assembley in a folder outside of the branch, the relative reference becomes this (in the Main branch):

<Reference Include="AdvancedOperations, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\..\..\Binaries\AdvancedOperations.dll</HintPath>
</Reference>

Later when I merge Main to Dev, the relative reference is brokend in the Dev branch (because the dll is actually at a different folder nesting level):

<ItemGroup>
<Reference Include="AdvancedOperations, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\..\..\Binaries\AdvancedOperations.dll</HintPath>
</Reference>

If I manually fix the reference it becomes:

<ItemGroup>
<Reference Include="AdvancedOperations">
<HintPath>..\..\..\..\..\..\Binaries\AdvancedOperations.dll</HintPath>
</Reference>

As you can see, because the DLL is in a different relative location to the csproj file, I will constantly have to fix the reference or move the DLL to a folder that is subordinate to the branch. A subordinate folder will always be branched (and or merged) to the same relative location in the target branch and the relative reference will not be broken.

Alternatively, you can keep all branches at the same relative folder depth within the Team project. This will allow you to have a dependency folder outside of the branch structure, but will allow you to avoid breaking the reference:

If I had branched $\DependentTeamProject\WindowsCalculator\Main to $\DependentTeamProject\WindowsCalculator\Dev2, the Dev2 branch and the Main branch are at the same folder depth, relative to the binaries folder in the same Team Project.

This would work the same for a project reference. You can either store the dependent project in a folder subordinate to the branch or make sure that all branches are at the same folder depth relative to the source folder (when it isoutside of the branch). Here I have added a project reference to a project that is outside of my branch (Main):

<ItemGroup>
<ProjectReference Include="..\..\..\..\..\Source\AdvancedOperations\AdvancedOperations\AdvancedOperations.csproj">
<Project>{AC1BEBA7-427F-4181-869C-CC21F3A3D058}</Project>
<Name>AdvancedOperations</Name>
</ProjectReference>
</ItemGroup>

If I merge Main to the Dev branch within the Development folder, I get an error (It cannot load the project):

<ItemGroup>
<ProjectReference Include="..\..\..\..\..\Source\AdvancedOperations\AdvancedOperations\AdvancedOperations.csproj">
<Project>{AC1BEBA7-427F-4181-869C-CC21F3A3D058}</Project>
<Name>AdvancedOperations</Name>
</ProjectReference>
</ItemGroup>

If I fix the project reference (delete the project from the solution, add the project from the source folder back to the solution, and then add the project reference, the project is loaded correctly, but the Main branch solution and / or the Dev branch solution will always break when merging back and forth.

<ItemGroup>
<ProjectReference Include="..\..\..\..\..\..\Source\AdvancedOperations\AdvancedOperations\AdvancedOperations.csproj">
<Project>{AC1BEBA7-427F-4181-869C-CC21F3A3D058}</Project>
<Name>AdvancedOperations</Name>
</ProjectReference>
</ItemGroup>

If I merge Main to the Dev2 branch which is at the same folder depth as Main, the project is located and is loaded without a problem:

<ItemGroup>
<ProjectReference Include="..\..\..\..\..\Source\AdvancedOperations\AdvancedOperations\AdvancedOperations.csproj">
<Project>{AC1BEBA7-427F-4181-869C-CC21F3A3D058}</Project>
<Name>AdvancedOperations</Name>
</ProjectReference>
</ItemGroup>

There are two schools of thought here:

  • Keep all dependent assemblies or projects in a folder that is subordinate to the branch, but the downside is that you will be copying the assemblies and / or the project source to each child branch.
  • Keep all dependent assemblies or projects in a folder within the team project that is outside of the branch, but the downside is that all branches must be at the same folder depth within the team project. This may not be a problem if a person who is responsible for creating branches is familiar with this dependency issue. The risk is that a branch might be created at a different folder level (folder depth) within the Team Project. This will then break relative project or assembly references for solutions under that branch.