TFS2010:检索与分支关联的所有变更集(完全递归)

发布于 2025-01-05 12:12:38 字数 3261 浏览 1 评论 0原文

这遵循我之前的问题是关于 TFS 2010 以及创建变更日志的可能性。

我以前使用标签来标识程序的版本,但由于标签不是固定的时间点,现在我使用分支。

分支层次结构如下所示:

branch hierarchy

正如您所看到的,有两个不同的应用程序是主干的分支:APP_A(应用程序 A)和 APP_B(应用程序 B)。两者几乎相同,但存在一些功能差异。

以下是创建新版本应用程序(例如版本 1.3)的过程:

  1. 修改 Main trunk(添加新功能、修复错误...)
  2. 来自修改后的 Main trunk,创建一个新分支:Main trunk 1.3
  3. APP_A 分支可能会被修改,因此 APP_A 的独特功能将在修改后使用v1.3 的
  4. APP_B 分支可能会被修改,因此 APP_B 的独特功能将与 v1.3 的修改一起使用
  5. Main trunk 1.3 已合并到 APP_A ​​ Main trunk 接收修改
  6. APP_B,因此 APP_AAPP_B 应用程序都会从 修改的APP_A 分支,创建一个新分支:APP_A_1.3
  7. 从修改后的 APP_B 分支,创建一个新分支:APP_B_1。 3

我的目标是能够生成 APP_A_1.3APP_A_1.2 之间的变更日志。

我所说的变更日志是指工作项列表。签入的每个变更集都与一个或多个工作项(例如 Bug 项)相关联。 我希望能够获取链接到影响 APP_A_1.3 变更集的所有工作项的列表:这些变更集可能来自 Main trunk(上面的步骤 1)、APP_A 分支(上面的步骤 3)甚至 APP_A_1.3 分支本身(如果在分行有已创建)。

为了获取此工作项列表,我尝试获取“链接”到APP_A_1.2的所有变更集的列表(“链接”=已在变更集中签入的内容现在位于分支 APP_A_1.2 上)以及“链接”到 APP_A_1.3 的所有变更集的列表。

然后,我将能够知道哪些变更集“链接”到 APP_A_1.3,而不“链接”到 APP_A_1.2。从变更集的这个子集中,我将获得所有关联的工作项以及我的变更日志。

这是我的问题:如何获取与指定分支“链接”的所有变更集的列表?我正在使用 C# 代码的 TFS 2010 API。

我的程序的输入(将检索指定分支的所有变更集)将是分支的名称(例如 APP_A_1.2),输出将是以下变更集的列表

  • APP_A_1.2 之前,在 APP_A_1.2 分支本身
  • 在创建
  • 应用了在 APP_A 分支上应用的变更集,在 Main trunk 上应用了变更集1.2 分支,然后合并到 APP_A
  • Main trunk 1.2 之前应用于 Main trunk 分支

变更集,在创建我编写的 使用以下代码片段来获取所有这些变更集:

// Gets the list of all changesets ID from APP_A_1.2 branch
var branch1ChangeSets = myVersionControlServer.QueryHistory(
    "$/PATH/APP_A_1.2/",
    VersionSpec.Latest,
    0,
    RecursionType.Full,
    null,
    null,
    null,
    int.MaxValue,
    false,
    false).OfType<Changeset>().Select(z => z.ChangesetId).ToList();

即使指定了 RecursionType.Full,上面的代码也返回在APP_A_1.2 分支本身。这与 Visual Studio 中“源代码资源管理器”视图中的“历史记录”命令相同。

然后我尝试了以下代码:

// Gets the list of all changesets ID from APP_A_1.2 branch
var branch1MergedChangeSets = myVersionControlServer.QueryMerges(
    null,
    null,
    "$/PATH/APP_A_1.2/",
    VersionSpec.Latest,
    null,
    null,
    RecursionType.Full).Select(z => z.SourceVersion).ToList();

这将返回在 APP_A_1.2 分支上签入的变更集 + 在 之前在 APP_A 分支上签入的变更集APP_A_1.2 已创建。好多了,但还不够。我找不到一种方法可以使递归与“上面”APP_A 的分支一起工作(在我的情况下是Main trunk)...

有人有想法吗?

另外,欢迎任何更好的想法来获取两个分支之间的变更日志...... 谢谢。

This follows my previous question about TFS 2010 and the possibility to create a changelog.

I was previously using labels to identify a version of the program, but as labels are not fixed points in time, now I'm using branches.

Here's how the branch hierarchy looks like:

branch hierarchy

As you can see, there are two different applications that are branches of the trunk: APP_A (application A) and APP_B (application B). Both are almost identical, but there are some functional differences.

Here is the process to create a new version of the application (say version 1.3):

  1. The Main trunk is modified (new functionalities are added, bug fixes...)
  2. From the modified Main trunk, a new branch is created: Main trunk 1.3
  3. APP_A branch might be modified, so unique functionalities of APP_A will work with modification of v1.3
  4. APP_B branch might be modified, so unique functionalities of APP_B will work with modification of v1.3
  5. Main trunk 1.3 is merged to APP_A and APP_B, so both APP_A and APP_B applications receive the modifications of the Main trunk
  6. From the modified APP_A branch, a new branch is created: APP_A_1.3
  7. From the modified APP_B branch, a new branch is created: APP_B_1.3

My goal is to be able to produce a changelog between APP_A_1.3 and APP_A_1.2.

By changelog I mean a list of WorkItems. Each changeset that is checked-in is associated with one or more WorkItem (for instance a Bug item). I would like to be able to get the list of all workitems that were linked to a changeset that has impacted APP_A_1.3: those changesets might come from the Main trunk (step 1 above), the APP_A branch (step 3 above) or even the APP_A_1.3 branch itself (if hotfixes are checked-in after the branch has been created).

To get this list of workitems, I tried to get the list of all changesets that are "linked" to APP_A_1.2 ("linked" = the code that was checked-in in the changeset is now on the branch APP_A_1.2) and the list of all changesets that are "linked" to APP_A_1.3.

Then, I'll be able to know which changesets are "linked" to APP_A_1.3 and not "linked" to APP_A_1.2. From this subset of changesets, I'll get all associated WorkItems and thus my changelog.

Here's my problem: how could I get the list of ALL changesets that are "linked" with a specified branch? I'm using the TFS 2010 API for C# code.

The input of my program (that would retrieve all changesets for a specified branch) would be the name of the branch (say APP_A_1.2), and the output would be the list of following changesets:

  • changesets applied on APP_A_1.2 branch itself
  • changesets applied on APP_A branch before APP_A_1.2 was created
  • changesets applied on Main trunk 1.2 branch before it has been merged to APP_A
  • changesets applied on Main trunk branch before Main trunk 1.2 was created

I've wrote the following pieces of code to get all those changesets:

// Gets the list of all changesets ID from APP_A_1.2 branch
var branch1ChangeSets = myVersionControlServer.QueryHistory(
    "$/PATH/APP_A_1.2/",
    VersionSpec.Latest,
    0,
    RecursionType.Full,
    null,
    null,
    null,
    int.MaxValue,
    false,
    false).OfType<Changeset>().Select(z => z.ChangesetId).ToList();

Even if RecursionType.Full is specified, the above code only returns changesets that were checked-in on the APP_A_1.2 branch itself. This is identical to the "History" command on Source Code Explorer view in Visual Studio.

Then I tried the following piece of code:

// Gets the list of all changesets ID from APP_A_1.2 branch
var branch1MergedChangeSets = myVersionControlServer.QueryMerges(
    null,
    null,
    "$/PATH/APP_A_1.2/",
    VersionSpec.Latest,
    null,
    null,
    RecursionType.Full).Select(z => z.SourceVersion).ToList();

This returns changesets that were checked-in on APP_A_1.2 branch + those that were cheked-in on APP_A branch before APP_A_1.2 was created. Much better, but not sufficient. I can't find a way to make the recursion work with branches that are "above" APP_A (Main trunk in my case)...

Anyone has an idea?

Also, any better ideas to get the changelog between two branches are welcome...
Thx.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

独木成林 2025-01-12 12:12:38

首先让我先问一个问题。在帖子的顶部你写:
“我的目​​标是能够生成 APP_A_1.3 和 APP_A_1.2 之间的变更日志。”

但是当你写下具体的更改时,你正在寻找你的列表:
应用于 APP_A_1.2 分支本身的变更集
创建 APP_A_1.2 之前在 APP_A 分支上应用的变更集
在合并到 APP_A 之前在 Main trunk 1.2 分支上应用的变更集
创建 Main trunk 1.2 之前在 Main trunk 分支上应用的变更集

这不是一个有效的列表,因为它将为您提供对 APP_A_1.3、APP_A_1.2、1.1 等到存储库开头做出贡献的所有更改。

我现在无法测试我的方法,但这就是我要做的:
- QueryHistory 将所有更改直接签入分支 1.3
- 使用 QueryMergesExtended 来跟踪合并到此分支中。 QueryMergesExtended (http://msdn.microsoft.com/en-us/library/ff736485.aspx) 是专门添加到 TFS 2010 中的,它比 QueryMerges 和 QueryMergesWithDetails 具有更高的性能和健壮性,以支持分支可视化工具
- 据我所知,您不需要在 QueryMergesExtended 中指定选项 FollowRenames,因为您在分支的根上查询合并
- 当您获取源更改列表(来自 APP_A)时,您需要检查每个更改集以查看它是否包含合并更改。如果是这样,您需要在 app_a 上查询合并以获取这些变更集。递归地执行此操作,直到遍历整个分支层次结构。

在副主题上,您可以稍后查看 QueryMergeRelationships (http://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.versioncontrol.client.versioncontrolserver.querymergerelationships.aspx) 为您提供 tfs 2010 中引入的分支对象列表(这是在源代码管理资源管理器中选择文件夹并单击转换为分支时发生的情况)。但是,如果您可以以不同的方式发现您的分支(对它们进行硬编码),则不需要。

希望这有帮助!

first let me first ask one question. At the top of the post you write:
"My goal is to be able to produce a changelog between APP_A_1.3 and APP_A_1.2."

but then when you write what changes specifically you are looking for you list:
changesets applied on APP_A_1.2 branch itself
changesets applied on APP_A branch before APP_A_1.2 was created
changesets applied on Main trunk 1.2 branch before it has been merged to APP_A
changesets applied on Main trunk branch before Main trunk 1.2 was created

This is not a valid list because it will give you all changes that contributed to APP_A_1.3, APP_A_1.2, 1.1 and so on to the beginning of the repository.

I'm not able to test my approach right now, but this is what I would do:
- QueryHistory to get all changes checked in directly into branch 1.3
- use QueryMergesExtended to follow merges into this branch. QueryMergesExtended (http://msdn.microsoft.com/en-us/library/ff736485.aspx) was added in TFS 2010 specifically to be much more performant and robust than QueryMerges and QueryMergesWithDetails, in order to support branch visualization tools
- afaik you don't need to specify option FollowRenames in QueryMergesExtended because you query merges on the root of the branch
- when you get list of source changes (from APP_A) you need to check each changeset to see of it contains merge changes. If so, you need to query merges on app_a for those changesets. Do so recursively until you walk whole branch hierarchy.

On the side topic, you can look later at the QueryMergeRelationships (http://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.versioncontrol.client.versioncontrolserver.querymergerelationships.aspx) which gives you branch object list introduced in tfs 2010 (this is what happens when in Source Control Explorer you select folder and click Convert to branch). However if you can discover your branch in different way (hardcode them) than it's not needed.

Hope this helps!

薆情海 2025-01-12 12:12:38

我终于想出了一个简单的解决方案。我对它并不完全满意,因为它实际上看起来像一个蛮力算法,但至少它有效。

我所做的是:

1)获取应用于我的TFS分支的根每个变更集的列表(即Main的“父路径”)主干):

var allChangesets = vcs.QueryHistory(
    "MySourcePath",
    VersionSpec.Latest,
    0,
    RecursionType.Full,
    null,
    firstPossibleChangeset,
    VersionSpec.Latest,
    int.MaxValue,
    true,
    false).OfType<Changeset>().ToList();

2)对于每个检索到的变更集,我调用TrackMerges来查看变更集是否以某种方式影响我的分支。 TrackMerges 能够告诉我指定的变更集是否应用于我指定为函数参数的分支(它将返回这些分支上的目标变更集 ID)。如果变更集应用于目标分支(在我的例子中 APP_A_1.3)而不是源分支(APP_A_1.2),那么这意味着它肯定是新的在我的 APP_A_1.3 分支上。

List<int> newChangesets = new List<int>();
foreach (var z in allChangesets.Where(y => y.ChangesetId > firstPossibleChangesetId))
{
    var zz = vcs.TrackMerges(
        new int[] { z.ChangesetId },
        new ItemIdentifier("THE TRUNK PATH"),   // The root of all branches
        new ItemIdentifier[] { new ItemIdentifier(fromBranchPath), new ItemIdentifier(toBranchPath) },
        null);

    var targetInFromBranch = zz.Where(t => t.TargetItem.Item == fromBranchPath).FirstOrDefault();
    var targetInToBranch = zz.Where(t => t.TargetItem.Item == toBranchPath).FirstOrDefault();

    if (targetInToBranch != null && targetInFromBranch == null)
    {
        // Then the changeset is only applied on the ToBranch
        newChangesets.Add(z.ChangesetId);
    }
}

3)现在从“新变更集”列表中获取我的变更日志(工作项列表)非常简单:

// Now, gets associated work items!
Dictionary<int, WorkItem> dico = new Dictionary<int, WorkItem>();
foreach (int changesetId in newChangesets)
{
    foreach (WorkItem zz in vcs.GetChangeset(changesetId).WorkItems)
    {
        this.AddWorkItemToDicRecursive(wis, dico, zz);
    }
}

private void AddWorkItemToDicRecursive(WorkItemStore wis, Dictionary<int, WorkItem> dico, WorkItem workItem)
{
    if (!dico.ContainsKey(workItem.Id))
    {
        dico.Add(workItem.Id, workItem);

        foreach (WorkItemLink associatedItem in workItem.WorkItemLinks)
        {
            this.AddWorkItemToDicRecursive(wis, dico, wis.GetWorkItem(associatedItem.TargetId));
        }
    }
}

我不认为这是最好的方法,但它工作正常并且保持简单。另外,我不需要对任何东西(分支名称/层次结构)进行硬编码,所以在我看来这还不错。希望它能帮助某人。

I finally came up with a simple solution. I'm not totally happy with it as it actually looks like a brute-force algorithm, but at least it works.

What I do is:

1) Get the list of every changeset that is applied on the very root of my TFS branches (i.e. the "parent path" of Main Trunk):

var allChangesets = vcs.QueryHistory(
    "MySourcePath",
    VersionSpec.Latest,
    0,
    RecursionType.Full,
    null,
    firstPossibleChangeset,
    VersionSpec.Latest,
    int.MaxValue,
    true,
    false).OfType<Changeset>().ToList();

2) For each retrieved changeset, I call TrackMerges to see if the changeset impacts in some way my branches. TrackMerges is able to tell me if a specified changeset is applied on the branches I specify as parameter of the function (it'll return the target changeset ID on these branches). If a changeset is applied on the destination branch (in my case APP_A_1.3) and not in the source branch (APP_A_1.2), then it means it's definitely something new on my APP_A_1.3 branch.

List<int> newChangesets = new List<int>();
foreach (var z in allChangesets.Where(y => y.ChangesetId > firstPossibleChangesetId))
{
    var zz = vcs.TrackMerges(
        new int[] { z.ChangesetId },
        new ItemIdentifier("THE TRUNK PATH"),   // The root of all branches
        new ItemIdentifier[] { new ItemIdentifier(fromBranchPath), new ItemIdentifier(toBranchPath) },
        null);

    var targetInFromBranch = zz.Where(t => t.TargetItem.Item == fromBranchPath).FirstOrDefault();
    var targetInToBranch = zz.Where(t => t.TargetItem.Item == toBranchPath).FirstOrDefault();

    if (targetInToBranch != null && targetInFromBranch == null)
    {
        // Then the changeset is only applied on the ToBranch
        newChangesets.Add(z.ChangesetId);
    }
}

3) Now it's very simple to get my changelog (the list of workitems) from the list of "new changesets":

// Now, gets associated work items!
Dictionary<int, WorkItem> dico = new Dictionary<int, WorkItem>();
foreach (int changesetId in newChangesets)
{
    foreach (WorkItem zz in vcs.GetChangeset(changesetId).WorkItems)
    {
        this.AddWorkItemToDicRecursive(wis, dico, zz);
    }
}

private void AddWorkItemToDicRecursive(WorkItemStore wis, Dictionary<int, WorkItem> dico, WorkItem workItem)
{
    if (!dico.ContainsKey(workItem.Id))
    {
        dico.Add(workItem.Id, workItem);

        foreach (WorkItemLink associatedItem in workItem.WorkItemLinks)
        {
            this.AddWorkItemToDicRecursive(wis, dico, wis.GetWorkItem(associatedItem.TargetId));
        }
    }
}

I don't think it's the best possible approach, but it works fine and remains simple. Also, I didn't have to hardcode anything (branch names/hierarchy) so it's not too bad IMO. Hope it'll help someone.

一杯敬自由 2025-01-12 12:12:38

是的,我自己也在解决这个问题。无论如何,我找到了一个 codeplex 项目来解决这个问题,当你比较标签时。

看看这是否有帮助: http://tfslabeldiff.codeplex.com/SourceControl/changeset/ view/7075#158224

我很惊讶这很难找到,但 TFS 的文档充其量是缺乏的。看起来应该很明显了!

Yeah, working on this problem myself too. I found a codeplex project that solves it, when you're diff-ing labels, anyhow.

See if this helps: http://tfslabeldiff.codeplex.com/SourceControl/changeset/view/7075#158224

I was pretty surprised how difficult this was to find, but the documentation for TFS is lacking at best. It seemed like it ought to be obvious!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文