using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.VersionControl.Common;
class CheckShelvesets
{
private static VersionControlServer m_vcs;
private static string m_tempBase;
private static string m_tempLatest;
private static string m_tempModified;
private static string m_tempOutput;
static void Main(string[] args)
{
if (args.Length == 0)
{
Console.Error.WriteLine("Usage: CheckShelvesets <server> [regex_to_match]");
Environment.Exit(1);
}
TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer(args[0]);
m_vcs = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));
Regex regex = args.Length == 1 ? new Regex(".*") : new Regex(args[1]);
// Get some temp filenames for 3-way merge calculations
m_tempBase = Path.GetTempFileName();
m_tempLatest = Path.GetTempFileName();
m_tempModified = Path.GetTempFileName();
m_tempOutput = Path.GetTempFileName();
// Fetch all shelvesets, we will filter using Regex instead
Shelveset[] shelvesets = m_vcs.QueryShelvesets(null, m_vcs.AuthenticatedUser);
Console.WriteLine("User {0} has {1} shelveset(s)", m_vcs.AuthenticatedUser, shelvesets.Length);
foreach (Shelveset shelveset in shelvesets)
{
if (!regex.IsMatch(shelveset.Name)) continue;
ExamineShelveset(shelveset);
}
// delete those temp files
File.Delete(m_tempBase);
File.Delete(m_tempLatest);
File.Delete(m_tempModified);
File.Delete(m_tempOutput);
}
private static void ExamineShelveset(Shelveset shelveset)
{
Console.WriteLine();
Console.WriteLine("Examining shelveset: {0} [created {1}]",
shelveset.DisplayName,
shelveset.CreationDate.ToString("F"));
PendingChange[] changes = m_vcs.QueryShelvedChanges(shelveset)[0].PendingChanges;
int shelvedTotal = 0, latestTotal = 0, bothTotal = 0, conflictingTotal = 0;
foreach (PendingChange change in changes)
{
if (!change.IsEdit || change.IsAdd)
{
Console.WriteLine("Skipping non-edit change: {0} [{1}]",
change.ServerItem,
change.ChangeTypeName);
continue; // TODO: handle others
}
// do a GetItem call in case it's been renamed
Item item = m_vcs.GetItem(change.ItemId, int.MaxValue);
if (item == null ||
item.ChangesetId == 0) // Beta3 bug - should have gotten null here
{
Console.WriteLine("Skipping non-committed item: {0}", change.ServerItem);
continue;
}
if (item.DeletionId > 0)
{
Console.WriteLine("Skipping deleted item: {0}", item.ServerItem);
continue;
}
if (item.Encoding == RepositoryConstants.EncodingBinary)
{
Console.WriteLine("Skipping binary file: {0}", item.ServerItem);
continue;
}
string path;
if (VersionControlPath.Equals(change.ServerItem, item.ServerItem))
{
// item has not changed name
path = item.ServerItem;
}
else
{
path = String.Format("{0} [was {1}]", item.ServerItem, change.ServerItem);
}
// Fetch the 3 input files
change.DownloadBaseFile(m_tempBase);
change.DownloadShelvedFile(m_tempModified);
m_vcs.DownloadFile(item.ServerItem, m_tempLatest);
// Detect the encodings - anything binary and we'll skip
int baseCodePage = FileType.Detect(m_tempBase, null);
int latestCodePage = FileType.Detect(m_tempLatest, null);
int modifiedCodePage = FileType.Detect(m_tempModified, null);
if (baseCodePage == RepositoryConstants.EncodingBinary ||
latestCodePage == RepositoryConstants.EncodingBinary ||
modifiedCodePage == RepositoryConstants.EncodingBinary)
{
Console.WriteLine("Skipping merge with a binary input: {0}", item.ServerItem);
continue;
}
ThreeWayMerge threeWayMerge = new ThreeWayMerge();
threeWayMerge.UseExternalMergeTool = false;
threeWayMerge.LatestFileName = m_tempLatest;
threeWayMerge.LatestFileEncoding = Encoding.GetEncoding(latestCodePage);
threeWayMerge.ModifiedFileName = m_tempModified;
Encoding modifiedEncoding = Encoding.GetEncoding(modifiedCodePage);
threeWayMerge.ModifiedFileEncoding = modifiedEncoding;
threeWayMerge.OriginalFileName = m_tempBase;
threeWayMerge.OriginalFileEncoding = Encoding.GetEncoding(baseCodePage);
threeWayMerge.MergedFileName = m_tempOutput;
threeWayMerge.MergedFileEncoding = modifiedEncoding;
threeWayMerge.Run();
Console.WriteLine("{0} [{1}]: {2} shelved, {3} latest, {4} both, {5} conflicting",
path,
change.ChangeTypeName,
threeWayMerge.ContentMergeSummary.TotalModified,
threeWayMerge.ContentMergeSummary.TotalLatest,
threeWayMerge.ContentMergeSummary.TotalBoth,
threeWayMerge.ContentMergeSummary.TotalConflicting);
shelvedTotal += threeWayMerge.ContentMergeSummary.TotalModified;
latestTotal += threeWayMerge.ContentMergeSummary.TotalLatest;
bothTotal += threeWayMerge.ContentMergeSummary.TotalBoth;
conflictingTotal += threeWayMerge.ContentMergeSummary.TotalConflicting;
}
Console.WriteLine("Shelveset totals: {0} shelved, {1} latest, {2} both, {3} conflicting",
shelvedTotal, latestTotal, bothTotal, conflictingTotal);
if (bothTotal > 0 && bothTotal >= shelvedTotal)
{
Console.Write("Shelveset {0} is likely already committed - go ahead and delete it? (Y/N)",
shelveset.DisplayName);
ConsoleKeyInfo keyInfo = Console.ReadKey();
Console.WriteLine();
if (keyInfo.KeyChar == 'y' || keyInfo.KeyChar == 'Y')
{
Console.WriteLine("Deleting shelveset {0}", shelveset.DisplayName);
m_vcs.DeleteShelveset(shelveset);
}
}
else
{
Console.WriteLine("Shelveset is likely NOT already committed");
}
}
}