The Team Explorer is a wonderful view of the position of your version control in the team foundation server. However, there is no one window of such truth available for the workspace. I am a very active member of the MSDN community and have come across questions on:
- How do I see a list of all files that are not mapped to TFS but are in my workspace? (Yes, you can use the TFS power tools tfpt.exe /scorch or /treeclean to achieve this.)
- I would like to see all pending changes in just one folder and not recursively in the subfolders.
- I would like to analyze my workspace, what file types I have, what is the size of file by type, etc.
There are workarounds available, but because TFS does not provide these features natively, the experience is nothing as compared to browsing server artifacts through Team Explorer.
Welcome to Workspace Explorer – A solution using TFS API
- Get a list of all workspaces, all workspace and multiple folder mappings under a workspace. I have seen developers map multiple folders under a workspace and then complain it takes too long to do get latest, not all go to the workspace folder mapping to verify for what they are requesting the get latest?
- Explorer Style Navigation, along with a column to indicate the pending change type and a column to indicate whether the file in workspace is mapped to version control. When peer reviews are carried using shelve sets, reviewers have new files from the shelve sets left in their workspace.
- Workspace Statistics – A chart to break down the files by extension and size, this can be done both at the current directory level or recursively for all files in directory and subdirectories.
- Pending Changes – A list of all pending changes, this again can be done both at the current directory level or recursively.
- Missing Mappings – A list of all files in the workspace not mapped to the server, this again can be done at the current directory level or recursively.
1. Get all Workspaces Programmatically using TFS API
I have a blog post on workspaces – Get List of user workspace and checked out files programmatically.
Add a new ListView
and set the property view to ‘Details
’. Add the columns you would like to display to the property ‘Columns
’.
private void PopulateWorkspaceTracker()
{
var tfs =
TfsTeamProjectCollectionFactory.GetTeamProjectCollection
(new Uri("https://tfs2010:8080/defaultcollection"));
var vcs = tfs.GetService<VersionControlServer>();
var workspaces = vcs.QueryWorkspaces(null, vcs.AuthorizedUser,
Environment.MachineName);
lstWorkspaceDetails.Items.Clear();
ListViewItem.ListViewSubItem[] subItems;
ListViewItem item = null;
foreach (var workspace in workspaces)
{
foreach (var wk in workspace.Folders)
{
item = new ListViewItem(workspace.Name, 0);
subItems = new[]
{
new ListViewItem.ListViewSubItem(item, wk.LocalItem),
new ListViewItem.ListViewSubItem(item, wk.ServerItem),
new ListViewItem.ListViewSubItem
(item, workspace.EffectivePermissions.ToString()),
new ListViewItem.ListViewSubItem
(item, wk.IsCloaked.ToString()),
new ListViewItem.ListViewSubItem
(item, workspace.OwnerName)
};
item.SubItems.AddRange(subItems);
lstWorkspaceDetails.Items.Add(item);
}
}
lstWorkspaceDetails.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
}
2. Explorer like Navigation for the Workspace Folders
Well, I have created an explorer style interface using ListView
and TreeView
controls. Click here for a walkthrough on how to do this.
You can use the code below if do not want to follow the walkthrough at this time.
private void PopulateTreeView(string workspaceName)
{
var tfs =
TfsTeamProjectCollectionFactory.GetTeamProjectCollection
(new Uri("https://tfs2010:8080/defaultcollection"));
var vcs = tfs.GetService<VersionControlServer>();
var workspace = vcs.QueryWorkspaces(workspaceName,
vcs.AuthorizedUser, Environment.MachineName)[0];
_workspace = workspace;
tvWksNavigator.Nodes.Clear();
foreach (var folder in workspace.Folders)
{
var info = new DirectoryInfo(folder.LocalItem);
if (info.Exists)
{
var rootNode = new TreeNode(info.Name) { Tag = info };
GetDirectories(info.GetDirectories(), rootNode);
tvWksNavigator.Nodes.Add(rootNode);
}
}
}
private static void GetDirectories(IEnumerable<DirectoryInfo> subDirs,
TreeNode nodeToAddTo)
{
TreeNode aNode;
DirectoryInfo[] subSubDirs;
foreach (var subDir in subDirs)
{
aNode = new TreeNode(subDir.Name, 0, 0);
aNode.Tag = subDir;
aNode.ImageKey = "folder";
subSubDirs = subDir.GetDirectories();
if (subSubDirs.Length != 0)
{
GetDirectories(subSubDirs, aNode);
}
nodeToAddTo.Nodes.Add(aNode);
}
}
3. Check if a File is Pending Change Programmatically using TFS API
Using the method QueryPendingChanges
, it is possible to pass a file path and see if the file is pending change, if so, what is lock type, and also get the download details for the file.
var status = _workspace.QueryPendingSets(new[] { new ItemSpec(
dir.FullName,
RecursionType.None) },
_workspace.Name, vcs.AuthorizedUser,
false);
4. Check if a File in Workspace is Mapped to Version Control using TFS API
Using the method ServerItemExists
, it is possible to pass a file path and see if the file has a server mapping.
var isValid =
_workspace.VersionControlServer.ServerItemExists(
_workspace.GetServerItemForLocalItem(dir.FullName), ItemType.Any);
5. Workspace Statistics
The chart control impressed me, with few customizations, I could easily generate visually attractive graphs to visualize my workspace for files by extension and size. Add a chart control and use the below code. You can also look at the possibilities with chart controls on this interesting MSDN. Read it here.
if (cbRecursionType.SelectedItem == "Recursive")
{
while (stack.Count > 0)
{
var dir = stack.Pop();
filesInformation.AddRange(dir.GetFiles("*.*"));
foreach (var dn in dir.GetDirectories("*.*"))
{
stack.Push(dn);
}
}
}
else
{
while (stack.Count > 0)
{
var dir = stack.Pop();
filesInformation.AddRange(dir.GetFiles("*.*"));
}
}
var extensions = filesInformation.Select(f => f.Extension).Distinct();
var workspaceStatisticsCollection = extensions.Select(extension =>
(from fileInfo in filesInformation
where fileInfo.Extension == extension
select new WorkspaceStatistic()
{ File = fileInfo }).ToList()).ToList();
var yval = new int[extensions.ToList().Count];
var xval = new string[extensions.ToList().Count];
var count = 0;
foreach (var workspaceStats in workspaceStatisticsCollection)
{
var extension = extensions.ToArray()[count];
yval[count] = workspaceStats.Count;
xval[count] = extension;
count += 1;
}
Series series1 = chart1.Series[0];
chart1.Series[0].ChartType = SeriesChartType.Bar;
chart1.Series[0]["PointWidth"] = "0.6";
chart1.Series[0].IsValueShownAsLabel = true;
chart1.Series[0]["BarLabelStyle"] = "Center";
chart1.ChartAreas[0].Area3DStyle.Enable3D = true;
chart1.Series[0]["DrawingStyle"] = "Emboss";
chart1.Series[0].Points.DataBindXY(xval, yval);
chart1.Series[0].Name = "FilesByExtension";
chart1.Visible = true;
chart1.DataBind();
lblTotal.Text = String.Format("Total {0} files, compute to {1} bytes",
filesInformation.Count,
filesInformation.Sum(f => f.Length));
And done! I have a working solution, but I am yet to test various scenarios. I will upload the code to CodePlex once tested (yes, I am a responsible developer). Anyways, if you find this interesting and want a copy of the working solution, please feel free to email me and I’ll be happy to share the code with you.
What do you think, good bad ugly, share your feedback?