TFS 2010:如何使用标签在两个版本的应用程序之间生成更改日志(即工作项列表)?

我正在寻找一种方法来自动生成我的应用程序的两个版本之间的更改日志(实际上是工作项列表)。 我有两个版本的应用程序,v1和v2,每个版本都由TFS 2010(LABEL1和LABEL2)中的标签标识,我在构建应用程序的设置之前手动创建了该标签。 我有一个分支系统,这意味着我有一个主干,大多数错误是固定的,并且一个分支,其中补丁主要使用来自主干的合并(但是在分支上也有一些修复只与主干无关) 。 我的应用程序的两个版本(v1和v2)是分支的版本。

我希望TFS 2010能够返回这两个标签之间已修复的错误列表(即,类型= Bug的工作项列表已关闭并validation)。

我尝试使用TFS 2010的Web UI或使用Visual Studio实现此目的,但我没有找到任何方法。

然后我尝试使用以下命令行向tf.exe询问历史记录:

tf history /server:http://server_url/collection_name "$/project_path" /version:LLABEL1~LLABEL2 /recursive /noprompt /format:brief 

其中LABEL1是与应用程序的v1的源代码关联的标签,LABEL2是与应用程序的v2的源代码关联的标签。 它实际上以两种方式失败: – 命令行只返回变更集列表,而不是关联的已关闭工作项列表 – 变更集列表仅包含我在分支本身上应用的变更集,而不是我也应用的变更集然后将主干合并到分支机构。 设置与否“/ slotmode”参数不会改变任何内容。

在那里,我尝试编写一段C#代码来检索工作项列表(而不是变更集列表):

 var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://server_url/collection_name")); VersionControlServer controlServer = tfs.GetService(); VersionControlServer vcs = tfs.GetService(); VersionSpec sFrom = VersionSpec.ParseSingleSpec("LLABEL1", null); VersionSpec sTo = VersionSpec.ParseSingleSpec("LLABEL2", null); var changesets = vcs.QueryHistory( "$/project_path", sTo, 0, RecursionType.Full, null, sFrom, sTo, int.MaxValue, true, false); // Slotmode to false Dictionary dico = new Dictionary(); foreach (Changeset set in changesets) { foreach (WorkItem zz in set.WorkItems) { if (!dico.ContainsKey(zz.Id)) { dico.Add(zz.Id, zz); } } } foreach (KeyValuePair pair in dico.OrderBy(z => z.Key)) { Console.WriteLine(string.Format("ID: {0}, Title: {1}", pair.Key, pair.Value.Title)); } 

这实际上有效,我得到了我的两个标签之间的工作项列表,这实际上就是我想要的。 但是只考虑与在分支本身上提交的变更集相关联的工作项:在主干上求解然后合并到分支的“Bug”类型的工作项不会出现。 Slotmode不会改变任何东西。

然后我终于尝试用由changeset定义的VersionSpecs替换由标签定义的VersionSpecs:

 VersionSpec sFrom = VersionSpec.ParseSingleSpec("C5083", null); VersionSpec sTo = VersionSpec.ParseSingleSpec("C5276", null); 

我的代码终于有效了。

所以我的问题是:我怎么能用标签获得相同的结果,标签是我用来识别版本的TFS对象? 如果不可能,我该如何识别TFS 2010中的版本? 谢谢。

顺便说一句,我在stackoverflow上发现了一些问题,但没有一个给我带标签的答案。 例如: 问题示例

我认为http://tfschangelog.codeplex.com/可以在这里帮助你。

TFS ChangeLog applicatoin允许用户从TFS自动生成发行说明。 用户必须提供有关项目,分支和变更集范围的信息,然后TFS ChangeLog应用程序将从给定范围内的每个变更集中提取信息,并将所有相关工作项提取到此类变更集。 即它将从开始变更集到结束变更集,并将提取有关每个变更集的数据以及XML文件中的相关工作项。

然后,用户可以使用他们自己的转换逻辑,包括过滤,排序,样式,输出格式等,以生成发行说明报告。

我想在这里添加的另一件事与TFS中的标签有关。 标签基本上与变更集分配/关联。 目前,TFS ChangeLog应用程序不支持标签来定义起点和终点,但它支持变更集,可用作变通解决方案。

希望这很有用。

通常,在任何SCM中定义时间点的绝对方法显然是签入。
使用标签来抽象这一点,TFS并不是这里和这里讨论的最佳选择。 更好的方法是使用构建,尤其是在现代CI环境中。

为了检索给定构建中包含的最大变更集,您必须执行以下操作:

 using System; using System.Collections.Generic; using Microsoft.TeamFoundation.Build.Client; using Microsoft.TeamFoundation.Client; namespace GetChangesetsFromBuild { class Program { static void Main() { TfsTeamProjectCollection tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://TFSServer:8080/Name")); IBuildServer bs = (IBuildServer)tpc.GetService(typeof(IBuildServer)); IBuildDetail build = bs.GetAllBuildDetails(new Uri("vstfs:///...")); List associatedChangesets = InformationNodeConverters.GetAssociatedChangesets(build); int idMax = associatedChangesets[0].ChangesetId; } } } 

上述问题的一个难点是检索您感兴趣的构建的BuildUri。为了获得这些信息,您可以执行以下操作:

 IBuildDetail[] builds = bs.QueryBuilds("TeamPorjectName", "yourBuildDefinitionName") 

然后检索对你很重要的Uri。

如果你最终坚持使用标签,这也是一个很好的工具:除了Uri ,每个build[]还有一个LabelName

我和你一样处于同样的境地。 我还想要包含合并变更集的工作项。 我只包括完成的工作项。 此外,如果相同的工作项链接到多个更改集,则仅报告最后一个更改集。 我在CI设置中使用它; 并为每个构建创建一个更改日志。 然后可以将List导出到XML / HTML / TXT文件。 这是我的解决方案:

 namespace TFSChangelog { public class TFSChangelogGenerator { private const string workItemDoneText = "Done"; ///  /// This class describes a change by: /// Changeset details /// and /// WorkItem details ///  public class ChangeInfo { #region Changeset details public DateTime ChangesetCreationDate { get; set; } public int ChangesetId { get; set; } #endregion #region WorkItem details public string WorkItemTitle { get; set; } public int WorkItemId { get; set; } #endregion } public static List GetChangeinfo(string tfsServer, string serverPath, string from, string to) { // Connect to server var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsServer)); tfs.Connect(ConnectOptions.None); var vcs = tfs.GetService(); // Create versionspec's VersionSpec versionFrom = null; if (!string.IsNullOrEmpty(from)) versionFrom = VersionSpec.ParseSingleSpec(from, null); VersionSpec versionTo = VersionSpec.Latest; if (!string.IsNullOrEmpty(to)) versionTo = VersionSpec.ParseSingleSpec(to, null); // Internally used dictionary var changes = new Dictionary(); // Find Changesets that are checked into the branch var directChangesets = vcs.QueryHistory( serverPath, VersionSpec.Latest, 0, RecursionType.Full, null, versionFrom, versionTo, Int32.MaxValue, true, false ).Cast(); foreach (var changeset in directChangesets) { foreach (var workItem in changeset.WorkItems.Where(workItem => workItem.State == workItemDoneText)) { if (changes.ContainsKey(workItem.Id)) { if (changeset.ChangesetId < changes[workItem.Id].ChangesetId) continue; } changes[workItem.Id] = new ChangeInfo { ChangesetId = changeset.ChangesetId, ChangesetCreationDate = changeset.CreationDate, WorkItemId = workItem.Id, WorkItemTitle = workItem.Title }; } } // Find Changesets that are merged into the branch var items = vcs.GetItems(serverPath, RecursionType.Full); foreach (var item in items.Items) { var changesetMergeDetails = vcs.QueryMergesWithDetails( null, null, 0, item.ServerItem, VersionSpec.Latest, 0, versionFrom, versionTo, RecursionType.Full ); foreach (var merge in changesetMergeDetails.Changesets) { foreach (var workItem in merge.WorkItems.Where(workItem => workItem.State == workItemDoneText)) { if (changes.ContainsKey(workItem.Id)) { if (merge.ChangesetId < changes[workItem.Id].ChangesetId) continue; } changes[workItem.Id] = new ChangeInfo { ChangesetId = merge.ChangesetId, ChangesetCreationDate = merge.CreationDate, WorkItemId = workItem.Id, WorkItemTitle = workItem.Title }; } } } // Return a list sorted by ChangesetId return (from entry in changes orderby entry.Value.ChangesetId descending select entry.Value).ToList(); } } } 

这个问题让我更接近于解决我遇到的类似问题。

对于标签版本,请使用LabelVersionSpec类型而不是VersionSpec

更换:

 VersionSpec sFrom = VersionSpec.ParseSingleSpec("LLABEL1", null); VersionSpec sTo = VersionSpec.ParseSingleSpec("LLABEL2", null); 

有:

 LabelVersionSpec sFrom = new LabelVersionSpec("LLABEL1"); LabelVersionSpec sTo = new LabelVersionSpec("LLABEL2");