【问题标题】:How to copy a work item and its tasks如何复制工作项及其任务
【发布时间】:2025-12-07 10:30:01
【问题描述】:

我想复制一个工作项及其所有任务。

我正在使用这个版本的产品:11.0.50727.1 我正在为项目使用 Scrum 2.0 模板

如果可以,我该怎么做?

【问题讨论】:

    标签: tfs visual-studio-2012 workitem


    【解决方案1】:

    您尝试过 Excel 吗?在对工作项进行大规模编辑时,它是您最好的朋友。您可以复制/粘贴多个工作项。通过选择除 ID 列之外的所有列。复制它们,然后将它们粘贴到 Excel 中打开的查询的底部。

    您需要确保您使用的是基于树的查询,并且您要复制的所有列都是查询列的一部分。

    您可能会丢失这种方式的 HTML 类型字段的格式。

    我很想知道您为什么要批量复制 Product Backlog 项目及其所有任务,从 scrum 的角度来看,我真的不明白这有什么意义。

    【讨论】:

    • 我想知道他是在拆分迭代,还是将不完整的项目从一个迭代移动到下一个迭代?
    • @JohnSaunders,这也是我的猜测......但这种情况不应该经常发生,想要拥有它的功能;)。我不希望在两次迭代中进行相同的工作,除非每次都是“构建功能”、“设计功能”、“测试功能”、“部署功能”,在这种情况下,这些任务没有多大意义。
    • 谢谢大家。老实说,我会尝试 Excel 选项,我已经这样做过一次,再也没有使用过,所以你再次提醒我。关于您的问题,在我们的业务中,您可以使用预定义的任务子集对用户历史进行建模,我们的开发工具可以正常工作,这就是为什么我需要重复相同数量的任务。我没有拆分迭代,我确实必须将 itemos 从一个迭代移动到下一个迭代,但它只是更新了迭代字段。
    • 复制任务也有助于捕获发布步骤,以便您拥有一致、可重复的流程。太糟糕了,这仍然很难。 DRY 用于代码。可重复性对我们世界的其他地方很重要!
    【解决方案2】:

    我看到这个问题已经有 2 年多了。 Excel 不会为我创建父链接。这是我的 Powershell 解决方案:

    if ( (Get-PSSnapin -Name Microsoft.TeamFoundation.PowerShell -ErrorAction SilentlyContinue) -eq $null )
    {
         Add-PSSnapin Microsoft.TeamFoundation.PowerShell
    }
    
    [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Client")
    [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Build.Client")
    [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.Build.Common")
    [void][System.Reflection.Assembly]::LoadWithPartialName("Microsoft.TeamFoundation.WorkItemTracking.Client")
    
    # Connect to TFS and get Work Item Store.
    $tfsCollectionUrl = "https://tfs.CORP.com/tfs/group"
    $tfs = Get-TfsServer -name $tfsCollectionUrl
    $ws = $tfs.GetService([type]"Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore")
    
    $storyID = 15211    # ID of the story you want to copy.
    
    $story = $ws.GetWorkItem($storyID)
    Write-Host "Cloning User Story: " + $story.Title
    
    #Clone User Story
    $cloneStory = $story.Copy()
    ($story, $cloneStory )
    $cloneStory.Title = "COPY : " + $story.Title
    
    # cloneStory will not have links to all the tasks that were linked to the orginal story.
    # cloneStory will have two links, one to the same "feature" that the orginal was linked to, and one to the story it was cloned from.
    $cloneStory.Links
    
    # cloneStory will have 0 for an ID, because it has not yet been saved.
    $cloneStory.Id
    #$cloneStory.Save()
    # cloneStory will now have an ID.
    $cloneStory.Id
    $parentID = $cloneStory.Id  # Parent ID will be used to link new cloned tasks to this story.
    
    $links = $story.Links
    
    # Define a Link Type to be used in the loop.
    $linkType = $ws.WorkItemLinkTypes[[Microsoft.TeamFoundation.WorkItemTracking.Client.CoreLinkTypeReferenceNames]::Hierarchy]
    
    
    foreach ( $link in $links )
    {
    
        $itemID = $link.RelatedWorkItemId
        #$itemID
    
        $item = $ws.GetWorkItem($itemID)
    
        if ( ($item.Type).Name -eq "Task" )
        {
            $reportLine = "Cloning Task ID:{0} {1}" -f $itemId, $item.Title
            Write-Host $reportLine
            # Clone the Task
            # Create the Parent Link object
            # Add the Parent Link to Cloned Task
            # Save New Cloned Task
    
            $cloneTask = $item.Copy()
            $cloneTask.Title = "COPY : " + $item.Title
            $parentLink = new-object Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemLink($linkType.ReverseEnd, $parentID)
            $cloneTask.WorkItemLinks.Add($parentLink)
            $cloneTask.save()
            $cloneTask
        }
        else
        {
            $reportLine = "Skipping: {0} is not a Task, it is a {1}" -f $item.Title, ($item.Type).Name
            Write-Host $reportLine
        }
    }
    

    【讨论】: