In my previous blog post I wrote about how to get information about a build, the problem with that code was that the code only returned the user who requested the build, not the user who had checked-in a changeset that failed the build. So this blog post will cover that part.
The VersionControlServer class in the assembly Microsoft.TeamFoundation.VersionControl.Client can be used to get Changesets from a specific branch. To get access to the VersionControlServer I use the TfsTeamPorjectCollection's GetService<T> method, where T is set to VersionControlServer. To query a branch changeset history, the QueryHistory method of the VersionControlServer can be used. The QueryHistory method needs the path to the branch and other query parameters to get changessets. To get the path, I created a helper method (GetFirstServerItemFromBuild, it will get the path from the BuildDefinition:
private string GetFirstServerItemFromBuild(string buildName)
var buildService = _teamProjectCollection.GetService<IBuildServer>();
var build = GetBuildDefinition(buildName, buildService);
When setting up a build in TFS, we need to specify Working folders under the Workspace settings, in my case the first working folder has the path to the branch.
By using the BuildDefintion's Workspace and its Mappings property, I can get the first item from the Working folders. The ServerItem property will return the Source Control Folder.
To get the most possible ChangeSet that may cause the build to fail I specify a "from date" (passed as an argument to a helper method, more about that later) and a "to date" to the QueryHistory method. The "from date" will be three weeks back in time from when the latest build was started, and the "to date" will be when the latest build was started. I will then take the First ChangeSet from the QueryHistory's result. QueryHistory don't take a DateTime as argument for the dates, instead a VersionSpec class, so I created a helper method that will parse a DateTime to a VersionSpec.
private static VersionSpec CreateDateVSpec(DateTime date)
//Format is: D2009-11-16T14:32
I created a helper method added to my TfsBuidService class for helping me to get the latest change set, here is the code for the helper method.
public Changeset GetLatestChangeset(DateTime versionTodate, string buildName)
var path = this.GetFirstServerItemFromBuild(buildName);
var vcs = _teamProjectCollection.GetService<VersionControlServer>();
var versionFrom = CreateDateVSpec(versionTodate.AddDays(-21));
var versionTo = CreateDateVSpec(versionTodate);
var results = vcs.QueryHistory(path, VersionSpec.Latest, 0, RecursionType.Full, null, versionFrom, versionTo, int.MaxValue, false, true);
I made some changes to my Web API, so it takes information from the latest change set:
private static BuildDetail GetBuildDetail(string buildName)
var tfsBuildService = new TfsBuildService(
var lastBuild = tfsBuildService.GetLastBuildDetail(buildName);
var changeSet = tfsBuildService.GetLatestChangeset(lastBuild.StartTime, buildName);
return new BuildDetail
RequestedBy = lastBuild.RequestedBy,
Status = lastBuild.Status.ToString(),
FinishTime = lastBuild.FinishTime,
StartTime = lastBuild.StartTime,
LastCheckedInUser = changeSet != null ? changeSet.OwnerDisplayName : lastBuild.RequestedBy,
ChangeSetComment = changeSet != null ? changeSet.Comment : string.Empty,
ChangeSetId = changeSet != null ? changeSet.ChangesetId : -1,
WorkItemTitle = tfsBuildService.GetLatestWorkItemTitle(changeSet)
The resource returned from the Web API will now also include extra information, such as the last work item associated to the last changeset, changeset's id, changeset's comment by the user who check-in the code and the last user who checked-in the code. To get the latest changeset, the last build's StartTime is used to query the build's branch for the latest changeset.
To get the latest work item associated to the changeset, I created a helper method that takes the ChangeSet as an argument, just to extract the Work item title:
public string GetLatestWorkItemTitle(Changeset changeSet)
if (changeSet == null)
if (changeSet.AssociatedWorkItems == null || !changeSet.AssociatedWorkItems.Any())
Even if the TFS API for 2012 had lack of example on MSDN, it was quite easy to understand how to use them.
I hope some of you may found this blog post useful.
If you want to know when I have published a blog post, then feel free to follow me on twitter: @fredrikn