How to Find TFS Changesets Not Linked to Work Items - tfs

Is there a way, either via a query or programmatically, to identify all TFS Changesets that are NOT linked to a Work Item?

Using the TFS PowerToy's PowerShell module:
From whatever folder in your workspace you're interested in:
Get-TfsItemHistory . -Recurse | Where-Object { $_.WorkItems.Length -eq 0 }
This will get the history for the current folder and all subfolders, and then filter for empty workitem lists.

Sure, you can use the TFS API to do this very easily.
public static void GetAllChangesetsWithNoWorkItems()
{
var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://tfs2010/tfs/default"));
var service = tfs.GetService<VersionControlServer>();
var histories = service.GetBranchHistory(new ItemSpec[] { new ItemSpec(#"$/ProjectName/MAIN/BUILD", RecursionType.OneLevel) }, VersionSpec.Latest);
foreach (BranchHistoryTreeItem history in histories[0])
{
var change = service.GetChangeset(history.Relative.BranchToItem.ChangesetId, true, true);
if(change.WorkItems.ToList().Count == 0)
{
Debug.Write(String.Format("Work Item Missing for Changeset {0}", change.ChangesetId));
}
}
}
You can read this blog post on how to connect to TFS API Programmatically http://geekswithblogs.net/TarunArora/archive/2011/06/18/tfs-2010-sdk-connecting-to-tfs-2010-programmaticallyndashpart-1.aspx

I don't know about Richard's Answer but the accepted answer took almost 2 minutes to run from the root of my team project collection. This runs in 10 seconds if you are looking for a specific user, 47 seconds if you aren't.
service.QueryHistory("$/TeamProject/", VersionSpec.Latest,0, RecursionType.Full,userName,null,null, Int32.MaxValue,true,false)
.Cast<Changeset>()
.Where(cs=>cs.AssociatedWorkItems.Length==0)
if you aren't looking for a specific user just set userName to null
http://share.linqpad.net/6sumno.linq

Related

TFS item unique identifier

During working with TFS API, I faced the following problem.
I have two repository changesets, say A and B:
In changeset A the file 1.cs was changed
In changeset B the file 1.cs was renamed
Is there a way to find out that files with different names from changeset A and B are in fact the same file that was renamed? I thought I could use ItemId from Item properties list but for some reason its different.
Here is some sample, which demonstrates the issue:
var changeset = versionControl.GetChangeset(changesetA);
var changeItemA = changeset.Changes.Single(i => i.Item.ServerItem == "1.cs");
var changeset = versionControl.GetChangeset(changesetB);
var changeItemB = changeset.Changes.Single(i => i.Item.ServerItem == "1.cs");
// fails
Assert.Equal(changeItemA.Item.ItemId, changeItemB.Item.ItemId);
I use Microsoft.TeamFoundationServer.Client version="14.102.0" and Microsoft.TeamFoundationServer.ExtendedClient version="14.102.0" to interact with TFS (.Net 4.6.2)
Is there a unique identifier that is assigned to TFS item and that lives with it during the whole life regardless of renaming?

Access the Kanban Column (a Team-Specific Field) for a Work Item

Is there a way to programmatically access the "Kanban Column" for a WorkItem using the TFS 2012 API?
Using the Scrum 2.2 template, the history of a Bug or Product Backlog Item shows "[MyProject\MyTeam] Kanban Column" as a changed field whenever a work item is dragged between Kanban columns on the Board, but the field is not accessible when specifically retrieving a work item through the TFS API.
It also shows up as a changed field in the WorkItemChangedEvent object when implementing the ProcessEvent method on the Microsoft.TeamFoundation.Framework.Server.ISubscriber interface.
Workaround:
A coworker found a blogpost about creating a read-only custom field to persist the value of the Kanban Column, taking advantage of the WorkItemChangedEvent to capture the latest value. It is then possible to query on this column. One problem with this approach is that only a single team's Kanban Column can be tracked.
Update:
According to this blogpost, the Kanban Column is not a field, rather a "WIT Extension". This may help lead to an answer.
I've found a way to read the value using the TFS 2013 API, inside the ISubscriber.ProcessEvent method:
var workItemId = 12345;
var extService = new WorkItemTypeExtensionService();
var workItemService = new WorkItemService();
var wit = workItemService.GetWorkItem(requestContext, workItemId);
foreach (var wef in extService.GetExtensions(requestContext, wit.WorkItemTypeExtensionIds))
{
foreach (var field in wef.Fields)
{
if (field.LocalName == "Kanban Column" || field.LocalName == "Backlog items Column")
{
// Access the new column name
var columnName = wit.LatestData[field.Field.FieldId];
}
}
}
If you are prepared to dig into the database you can mine this information out. I don't fully understand the modelling of the teams in TFS yet but first you need to work out which field id the team of interest is storing the Kanban state in as follows (TFS 2012):
USE Tfs_DefaultCollection
SELECT TOP(10)
MarkerField + 1 as FieldId,*
FROM tbl_WorkItemTypeExtensions with(nolock)
JOIN tbl_projects on tbl_WorkItemTypeExtensions.ProjectId = tbl_projects.project_id
WHERE tbl_projects.project_name LIKE '%ProjectName%
Then replace XXXXXXXX below with the FieldId discovered above
SELECT TOP 1000
wid.Id,
wia.State,
wid.StringValue as Kanban,
wia.[Work Item Type],
wia.Title,
tn.Name as Iteration
FROM tbl_WorkItemData wid with(nolock)
JOIN WorkItemsAre wia on wia.ID = wid.Id
JOIN TreeNodes tn on wia.IterationID = tn.ID
WHERE FieldId = XXXXXXXX and RevisedDate = '9999-01-01 00:00:00.000'
ORDER BY Id
I am not familiar with the Scrum 2.2 template, but the works are the same for CMMI or Scrum templates when it comes to TFS Work Item tracking.
Try something like this:
public string GetKanbanColumn(WorkItem wi)
{
if (wi != null)
{
return wi["Kanban"].ToString();
}
return string.Empty;
}
Depending on the actual name of the column, specified in the Work Item Template XML file. Hope this helps.

What tfs 2010 object references are available for Tfs_DefaultCollection.dbo.tbl_ReleaseNoteDetails

We have a Team Project Collection Source Control Setting for Check-in Notes that requires each check-in to capture a "Tracking Number". This number is external to TFS. I need to search for all the changesets that have a specific Tracking number.
The resulting changeset list tells me what to GetLatest on, for our monthly deployment.
-- We don't use Work Items
This .SQL gives me the list I'm looking for. I want to access this in code from Visual Studio.
SELECT ReleaseNoteId, FieldName, BaseValue
from Tfs_DefaultCollection.dbo.tbl_ReleaseNoteDetails
where ReleaseNoteId in (SELECT ReleaseNoteId
FROM Tfs_DefaultCollection.dbo.tbl_ReleaseNote
where DateCreated between '2013-01-01' and '2013-01-31')
and FieldName = 'Tracker #'
and BaseValue = '18570'
What object references are available for Tfs_DefaultCollection.dbo.tbl_ReleaseNoteDetails?
There is no 1to1 object reference in the TFS API, because you usually don't work on the structure like the database looks like.
What I understand from your description, you have the information you need in the changesets. In that case you could use the VersionControlServer to get the changesets and get the information from there.
TfsTeamProjectCollection tfsConnection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://TFS:8080/TFS/DefaultCollection"));
VersionControlServer sourceControl = (VersionControlServer)tfsConnection.GetService(typeof(VersionControlServer));
IEnumerable changesets = sourceControl.QueryHistory(ServerPath, VersionSpec.Latest, 0, RecursionType.Full, null, new DateVersionSpec (new DateTime(2013,1,1)), new DateVersionSpec (new DateTime(2013,1,31)), Int32.MaxValue, true, false);
foreach (Changeset change in changesets)
{
// check where the information is stored in the changeset, may change.Changes
}
Just an idea to get in the right direction.
If you want easy querying and searching, you're better off creating a new work item field and associate the work item during checkin. Work items have complete querying capabilities in the UI and are even transported to the Reporting warehouse.
You could retrieve the history for a specific folder, date range etc using a QueryHistoryParameters object and then iterate over the CheckinNotes to filter:
var projectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(
new Uri("http://localhost:8080/tfs/DefaultCollection"));
var versionControlServer = projectCollection.GetService<VersionControlServer>();
var query = new QueryHistoryParameters("$/Scrum/**", RecursionType.Full);
var changesets = server.QueryHistory(query);
changesets.Where(cs => cs.CheckinNotes.Any(note => note.PropertyName == "Tracker #"
&& note.Value == "18570"))
This is not going to be terribly fast. To get a quick solution, use work item association.

how to group resources involved in a specific set of changesets?

How can I query the tfs history to have this result: group the resources involved in a set of changesets?
What I'm trying to do is understanding which files changed in the last 2 months for example.
I tried on TFS Explorer, but I can obtain the details just from a single changeset. It's the same for TFS Sidekicks. I had no luck with the command line and not even connecting directly to the database.
Does someone knows a smart way to get that result?
I am not sure how to get the VersionTo and VersionFrom so I am just doing from version 100 to Latest in here. You could you Source Control Explorer to do a View History and get your VersionFrom and VersionTo.
Here's a snippet of code that uses the TFS API. You will need to add some references to Microsoft.TeamFoundation.* assemblies to get it to build.
using (var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(CollectionAddress)))
{
var server = tfs.GetService<VersionControlServer>();
var changes =
server.QueryHistory(
"$/Project/Main",
VersionSpec.Latest,
0,
RecursionType.Full,
"",
VersionSpec.ParseSingleSpec("100", ""), //From ??
VersionSpec.Latest, //To ??
100,
true,
true)
.Cast<Changeset>()
.SelectMany(changeset => changeset.Changes.Select(change => change.Item.ServerItem));
}

How to find all changes below a certain point in the TFS source control tree

I need to know what changes (if any) have happened at a particular level in our source control tree. Is there some way to make such a query of TFS?
Using Team Explorer:
Open Source Control Explorer
Navigate to desired source control folder
Right-click and choose View History
Shows you all of the changesets that have been checked in at that level in the tree or below.
Using the tf utility:
tf history c:\localFolder -r -format:detailed
Here's a link to the tf history documentation for more details on usage: link
Using the TFS SDK to do it programatically:
Here's a sample method based on some of our code. It takes a path, start time and end time and gives you all of the changeset details below that path in between the two specified times:
private StringBuilder GetTfsModifications(string tfsPath, DateTime startTime, DateTime endTime)
{
StringBuilder bodyContent = new StringBuilder();
TeamFoundationServer tfs = new TeamFoundationServer("YourServerNameHere");
VersionControlServer vcs = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));
// Get collection of changesets below the given path
System.Collections.IEnumerable changesets = vcs.QueryHistory(
tfsPath,
VersionSpec.Latest,
0,
RecursionType.Full,
null,
new DateVersionSpec(startTime),
new DateVersionSpec(endTime),
int.MaxValue,
true,
false);
// Iterate through changesets and extract any data you want from them
foreach (Changeset changeset in changesets)
{
StringBuilder changes = new StringBuilder();
StringBuilder assocWorkItems = new StringBuilder();
// Create a list of the associated work items for the ChangeSet
foreach (WorkItem assocWorkItem in changeset.WorkItems)
{
assocWorkItems.Append(assocWorkItem.Id.ToString());
}
// Get details from each of the changes in the changeset
foreach (Change change in changeset.Changes)
{
changes.AppendLine(string.Format(CultureInfo.InvariantCulture, "\t{0}\t{1}",
PendingChange.GetLocalizedStringForChangeType(change.ChangeType),
change.Item.ServerItem));
}
// Get some details from the changeset and append the individual change details below it
if (changes.Length > 0)
{
bodyContent.AppendLine(string.Format(CultureInfo.InvariantCulture, "{0}\t{1}\t{2}\t{3}\t{4}",
changeset.ChangesetId,
changeset.Committer.Substring(changeset.Committer.IndexOf('\\') + 1),
changeset.CreationDate,
changeset.Comment,
assocWorkItems.ToString()));
bodyContent.Append(changes.ToString());
}
}
return bodyContent;
}
If I understand correctly, the answer could be as simple as:
tf history c:\some\subdir -r -format:detailed -noprompt
Reply if that's not good enough.

Resources