I have just come across this problem as more members of our team commit without their solution file, we add the file to the solution independently, they belatedly commit their solution file and Team Foundation Server/Visual Studio Online completely fails to get the merge right.
Some files added twice, some not added at all, everyone with a file in a different order – merge hell.
I have sort of worked around the inability to sort the Content files within a solution using the following C#, which I execute in the awesome LinqPad.
var documentsLocation = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
var unsortedFilePath = Path.Combine(documentsLocation.ToString(), "UnsortedConfigItems.xml");
var unsortedFile = new FileInfo(unsortedFilePath);
if (!unsortedFile.Exists) {
throw new FileNotFoundException(string.Format("The unsorted file could not be found at path {0}", unsortedFilePath));
}
var sortedFilePath = Path.Combine(documentsLocation, "SortedConfigItems.xml");
const string RootNodeName = "ItemGroup";
const string IncludeAttributeName = "Include";
var contentElementNames = new [] { "Compile", "Content", "None" };
var xmlFile = XDocument.Load(unsortedFile.FullName);
if (xmlFile == null || xmlFile.Root == null || xmlFile.Root.Name != RootNodeName)
{
Console.WriteLine("Invalid file or unexpected file schema");
return;
}
var allElements = xmlFile.Root.Elements();
var contentElements = new List<XElement>();
foreach(var elementName in contentElementNames) {
contentElements.AddRange(xmlFile.Root.Elements(elementName));
}
var elementsWithInclude = contentElements.Where(ce => ce.Attribute(IncludeAttributeName) != null && !string.IsNullOrWhiteSpace(ce.Attribute(IncludeAttributeName).Value));
if (!elementsWithInclude.Any())
{
Console.WriteLine("No content elements to sort");
return;
}
contentElements = null; // Make candidate for garbage collection
var uniqueElements = new List<XElement>(elementsWithInclude.Count());
foreach (var element in elementsWithInclude)
{
if (!uniqueElements.Any(e => e.Attribute(IncludeAttributeName).Value == element.Attribute(IncludeAttributeName).Value))
{
uniqueElements.Add(element);
}
}
var sortedUniqueElements = uniqueElements.OrderBy(ue => ue.Name.LocalName).ThenBy(ue => ue.Attribute(IncludeAttributeName).Value).ToList();
var remainingElements = new List<XElement>();
foreach (var element in allElements.Where(ae => !elementsWithInclude.Contains(ae))) { // This uses elements with include and not sorted unique to avoid re-adding filtered files
remainingElements.Add(element);
}
var sortedFile = new XDocument();
sortedFile.AddFirst(new XElement(RootNodeName));
sortedUniqueElements.ForEach(sue => sortedFile.Root.Add(sue));
remainingElements.ForEach(re => sortedFile.Root.Add(re));
sortedFile.Save(sortedFilePath);
I’m sure it could be compressed slightly and made more elegant, but it does the job for us.
Steps for completeness-
-
Make sure your project file is backed up, either in Source Control or locally.
-
Right click on the Project in Solution Explorer and select “Unload Project” (if the Project file isn’t shown and you only have a single Project in the solution then select “Always show Solution” in Visual Studio options).
-
The project icon should have changed and it will be listed as “(unavailable)”. Right click again and select “Edit ProjectName.csproj”
-
Locate the ItemGroup node containing the content references and either cut or copy and paste into a new file (NotePad++ is ideal for this, but use the editor of your choice) and save at the location of XmlPathName. Ensure that the “ItemGroup” node is the root node of your new XML document.
-
Alter the path in the SortedPathName if wanted, and ensure that your Windows user account has write permission to that location.
-
Run the above C# in LinqPad (or branch it and create your own console application/Powershell script).
-
Inspect the contents of the output file to make sure it looks correct (broadly consistent line count and format) and if possible validate as valid XML (the XML tools in Notepad++ is ideal for this).
-
Copy the contents of the output file back to your solution file, replacing the original ItemGroup node.
-
Close the csproj file, right click on the Project in Solution Explorer and select “Reload project”.
-
If the project was your startup project you may need to right click again and select “Set as StartUp Project”
-
Attempt to build