-->

TFS 2010 API - Get work items from merge

2019-03-21 12:15发布

问题:

I need to send an email on completion of a build in TFS 2010 which details the work items associated with check-ins that have been compiled as part of this build. This works no problem via use of the associatedChangesets variable available in the build workflow.

However, in a production situation, we will merge changes from our Development branch into a Release branch. At this point, the build considers there to have only been one change - which is the aforementioned merging of Development into Release. Obviously this is fairly useless as we need to find out which changes where made in the branch that was merged in, and the work items associated.

Does anyone know how to accomplish this using the TFS 2010 API? It seems to be fairly poorly documented from an API perspective. I know you can expand the merge history node in VS2010 but obviously this is no good as this data needs to be collected programatically so a report email can be sent.

回答1:

OK... I think I found a solution to this although it's clunky and truth be told I'm not exactly sure how it works. But here goes - maybe it will point someone in the right direction.

var associatedWorkItems = new List<WorkItem>();

//Passed in from the build workflow (this variable is available under the 'Run On Agent' sequence as 'associatedChangesets'
IList<Changeset> associatedChangesets = context.GetValue(BuildAssociatedChangesets);

if (associatedChangesets.Count > 0)
{
    var projectCollection =
        new TfsTeamProjectCollection(new Uri("http://localhost:8080/tfs/DefaultCollection"));
    VersionControlServer versionControlServer = projectCollection.GetService<VersionControlServer>();

    foreach (var changeset in associatedChangesets)
    {
        //In order to view the individual changes, load the changeset directly from the VCS.
        Changeset localChangeset = versionControlServer.GetChangeset(changeset.ChangesetId);

        foreach (Change change in localChangeset.Changes)
        {
            //Find out what was merged in.
            ChangesetMerge[] mergedChangesets = versionControlServer.QueryMerges(
                null,
                null,
                change.Item.ServerItem,
                new ChangesetVersionSpec(localChangeset.ChangesetId),
                new ChangesetVersionSpec(localChangeset.ChangesetId),
                null,
                RecursionType.Full);

            //Extract work item information from changesets being identified as merged.
            foreach (var changesetMerge in mergedChangesets)
            {
                Changeset actualChange = versionControlServer.GetChangeset(changesetMerge.SourceVersion);

                foreach (WorkItem item in actualChange.WorkItems)
                {
                    if (!associatedWorkItems.Exists(w => w.Id == item.Id))
                    {
                        associatedWorkItems.Add(item);
                    }
                }
            }
        }
    }
}

Don't ask me exactly how QueryMerges works but all I'm doing here it saying show me what what merged as a part of a changeset checked in. You'll notice that the parameters ChangesetVersionSpec are the same - this means we're just looking at merges from this one changeset.

You'll get back an array of ChangesetMerge objects from QueryMerges(). In the ChangesetMerge class there is a property called SourceVersion - this is the ChangesetId of the original changeset merged in. Once we've got that we can use the VersionControlServer.GetChangeset() method to load the individual set and extract the WorkItem. This is then added to a list of WorkItems which can be manipulated in any way you want (in my case an email). I also used the .Exists() check to make sure the same WorkItem doesn't get recorded twice.

Note that even though you have the collection associatedChangesets from the build workflow, for some reason (for me at least), the Changes[] property inside associatedChangesets was never populated (hence loading each individual changeset using the VersionControlServer.GetChangeset() method as this seems to actually populate all the fields we need.

Like I say, 1. this is a clunky solution (lots of looping - some of which is probably unecessary), 2. I don't fully understand how this works although it seems to produce the required results - I came to this conclusion by doing a lot testing and debugging) and finally - it's the best I could come up with based on the woeful documentation provided by Microsoft.

Hope it helps someone!