在 TFS 中的项目之间移动工作项

发布于 2024-09-07 05:25:03 字数 312 浏览 3 评论 0原文

是否可以将工作项从一个项目移动到 TFS 内的另一个项目?我看到了复制选项,但没有移动。另外,如果可能的话,对 WI 历史有何影响?

我发现这个 2008 年的文章 似乎说事实并非如此,但我想知道从那时起是否有任何进展。

Is it possible to move a work item from one project to another inside TFS? I’ve seen a copy option, but no move. Also, if it is possible, what’s the implication for any of the WI history?

I found this article from 2008 that seem to say it's not, but I wondered if there'd been any progress since then.

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

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

发布评论

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

评论(2

最好是你 2024-09-14 05:25:03

无法移动,只能复制。我们这样做的方式是复制副本,链接原始文件,然后将原始文件视为已过时而关闭。您也可以创建副本并销毁原始副本,但您将丢失所有历史记录。

如果您愿意,您可以非常喜欢并创建自己的“移动”实用程序,该实用程序复制工作项和所有历史记录,然后关闭(或销毁)旧的。对于您可能不需要经常做的事情来说,这似乎有点矫枉过正。

It isn't possible to move, just copy. The way we do it, is we do the copy, link the original, then close the original as obsolete. You could also create the copy and TF Destroy the original, but you will lose all history.

If you wanted to, you could get very fancy and create your own "move" utility that copies the workitem and all of the history, then closes out (or destroys) the old one. Seems like overkill for something that you probably shouldn't need to do all that often.

相思故 2024-09-14 05:25:03

Lars Wilhelmsen 编写了一个 WorkItemMigrator -> http://larsw.codeplex.com/SourceControl/list/changesets

很好的起点您可以根据需要自定义的实用程序。我们用它来将 100 个左右的工作项拆分到一个新项目中。这是我最终得到的程序。修改查询以对要迁移的项目进行子集化。

namespace WorkItemMigrator
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Framework.Common;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

class Program
{

    #region Members
    private static readonly Dictionary<Uri, TfsTeamProjectCollection> Collections = new Dictionary<Uri, TfsTeamProjectCollection>();
    private static readonly Uri SourceCollectionUri = new Uri("http://your.domain.com:8080/tfs/DefaultCollection");
    private static readonly Uri TargetCollectionUri = new Uri("http://your.domain.com:8080/tfs/DefaultCollection");
    private const String Areas = "ProjectModelHierarchy";
    private const string Iterations = "ProjectLifecycle";
    private const string TargetProjectName = "TargetProject";
    private const string MicrosoftVstsCommonStackRankFieldName = "Microsoft.VSTS.Common.StackRank";
    private const string MicrosoftVstsCommonPriority = "Microsoft.VSTS.Common.Priority";
    private const string TargetWorkItemType = "User Story";
    private const string Wiql = "SELECT [System.Id], [System.State], [System.Title], [System.AssignedTo], [System.WorkItemType], [Microsoft.VSTS.Common.Priority], " +
                        "[System.IterationPath], [System.AreaPath], [System.History], [System.Description] " +
                        "FROM WorkItems WHERE [System.TeamProject] = 'SourceProject' AND " + 
                        "[System.State] = 'Active' " +
                        "ORDER BY [System.Id]";

    private static WorkItemTypeCollection WorkItemTypes;
    private static Dictionary<int, int> WorkItemIdMap = new Dictionary<int, int>();

    #endregion
    static void Main()
    {
    var createAreasAndIterations = GetRunMode();

    var sourceWorkItemStore = GetSourceWorkItemStore();

    var sourceWorkItems = sourceWorkItemStore.Query(Wiql);

    var targetWorkItemStore = GetTargetWorkItemStore();
    var targetProject = targetWorkItemStore.Projects[TargetProjectName];


    WorkItemTypes = targetProject.WorkItemTypes;

    foreach (WorkItem sourceWorkItem in sourceWorkItems)
    {
        if (createAreasAndIterations)
        {
        Console.WriteLine();
        EnsureThatStructureExists(TargetProjectName, Areas, sourceWorkItem.AreaPath.Substring(sourceWorkItem.AreaPath.IndexOf("\\") + 1));
        EnsureThatStructureExists(TargetProjectName, Iterations, sourceWorkItem.IterationPath.Substring(sourceWorkItem.IterationPath.IndexOf("\\") + 1));
        }
        else
        {
        MigrateWorkItem(sourceWorkItem);
        }
    }

    if (!createAreasAndIterations)
    {
        var query = from WorkItem wi in sourceWorkItems where wi.Links.Count > 0 select wi;
        foreach (WorkItem sourceWorkItem in query)
        {
        LinkRelatedItems(targetWorkItemStore, sourceWorkItem);
        }
    }

    TextWriter tw = File.CreateText(@"C:\temp\TFS_MigratedItems.csv");
    tw.WriteLine("SourceId,TargetId");
    foreach (var entry in WorkItemIdMap)
    {
        tw.WriteLine(entry.Key + "," + entry.Value);
    }
    tw.Close();
    Console.WriteLine();
    Console.WriteLine("Done! Have a nice day.");
    Console.ReadLine();
    }

    private static bool GetRunMode()
    {
    bool createAreasAndIterations;
    while (true)
    {
        Console.Write("Create [A]reas/Iterations or [M]igrate (Ctrl-C to quit)?: ");
        var command = Console.ReadLine().ToUpper().Trim();
        if (command == "A")
        {
        createAreasAndIterations = true;
        break;
        }
        if (command == "M")
        {
        createAreasAndIterations = false;
        break;
        }
        Console.WriteLine("Unknown command " + command + " - try again.");
    }
    return createAreasAndIterations;
    }

    private static void MigrateWorkItem(WorkItem sourceWorkItem)
    {

    var targetWIT = WorkItemTypes[sourceWorkItem.Type.Name];
    var newWorkItem = targetWIT.NewWorkItem();
    //var newWorkItem = targetWorkItemType.NewWorkItem();

    // Description (Task) / Steps to reproduce (Bug)

    if (sourceWorkItem.Type.Name != "Bug")
    {
        newWorkItem.Description = sourceWorkItem.Description;
    }
    else
    {
        newWorkItem.Fields["Microsoft.VSTS.TCM.ReproSteps"].Value = sourceWorkItem.Description;
    }

    // History
    newWorkItem.History = sourceWorkItem.History;
    // Title
    newWorkItem.Title = sourceWorkItem.Title;
    // Assigned To
    newWorkItem.Fields[CoreField.AssignedTo].Value = sourceWorkItem.Fields[CoreField.AssignedTo].Value;
    // Stack Rank - Priority
    newWorkItem.Fields[MicrosoftVstsCommonPriority].Value = sourceWorkItem.Fields[MicrosoftVstsCommonPriority].Value;
    // Area Path
    newWorkItem.AreaPath = FormatPath(TargetProjectName, sourceWorkItem.AreaPath);
    // Iteration Path
    newWorkItem.IterationPath = FormatPath(TargetProjectName, sourceWorkItem.IterationPath);
    // Activity
    if (sourceWorkItem.Type.Name == "Task")
    {
        newWorkItem.Fields["Microsoft.VSTS.Common.Activity"].Value = sourceWorkItem.Fields["Microsoft.VSTS.Common.Discipline"].Value;
    }
    // State
    //newWorkItem.State = sourceWorkItem.State;
    // Reason
    //newWorkItem.Reason = sourceWorkItem.Reason;

    // build a usable rendition of prior revision history
    RevisionCollection revisions = sourceWorkItem.Revisions;
    var query = from Revision r in revisions orderby r.Fields["Changed Date"].Value descending select r;
    StringBuilder sb = new StringBuilder(String.Format("Migrated from work item {0}<BR />\n", sourceWorkItem.Id));
    foreach (Revision revision in query)
    {
        String history = (String)revision.Fields["History"].Value;
        if (!String.IsNullOrEmpty(history))
        {
        foreach (Field f in revision.Fields)
        {
            if (!Object.Equals(f.Value, f.OriginalValue))
            {
            if (f.Name == "History")
            {
                string notation = string.Empty;
                if (revision.Fields["State"].OriginalValue != revision.Fields["State"].Value)
                {
                notation = String.Format("({0} to {1})", revision.Fields["State"].OriginalValue, revision.Fields["State"].Value);
                }
                //Console.WriteLine("<STRONG>{0} Edited {3} by {1}</STRONG><BR />\n{2}", revision.Fields["Changed Date"].Value.ToString(), revision.Fields["Changed By"].Value.ToString(), f.Value, notation);
                sb.Append(String.Format("<STRONG>{0} Edited {3} by {1}</STRONG><BR />\n{2}<BR />\n", revision.Fields["Changed Date"].Value.ToString(), revision.Fields["Changed By"].Value.ToString(), f.Value, notation));
            }
            }
        }
        //Console.WriteLine("Revision {0}: ", revision.Fields["Rev"].Value);
        //Console.WriteLine("  ChangedDate: " + revision.Fields["ChangedDate"].Value);
        //Console.WriteLine("  History: " + sb.ToString());
        }
    }
    newWorkItem.History = sb.ToString();

    // Attachments
    for (var i = 0; i < sourceWorkItem.AttachedFileCount; i++)
    {
        CopyAttachment(sourceWorkItem.Attachments[i], newWorkItem);
    }

    // Validate before save
    if (!newWorkItem.IsValid())
    {
        var reasons = newWorkItem.Validate();
        Console.WriteLine(string.Format("Could not validate new work item (old id: {0}).", sourceWorkItem.Id));
        foreach (Field reason in reasons)
        {
        Console.WriteLine("Field: " + reason.Name + ", Status: " + reason.Status + ", Value: " + reason.Value);
        }
    }
    else
    {
        Console.Write("[" + sourceWorkItem.Id + "] " + newWorkItem.Title);
        try
        {
        newWorkItem.Save(SaveFlags.None);
        Console.ForegroundColor = ConsoleColor.Cyan;
        Console.WriteLine(string.Format(" [saved: {0}]", newWorkItem.Id));
        WorkItemIdMap.Add(sourceWorkItem.Id, newWorkItem.Id);
        Console.ResetColor();
        }
        catch (Exception ex)
        {
        Console.WriteLine(ex.Message);
        throw;
        }
    }
    }

    private static void CopyAttachment(Attachment attachment, WorkItem newWorkItem)
    {
    using (var client = new WebClient())
    {
        client.UseDefaultCredentials = true;
        client.DownloadFile(attachment.Uri, attachment.Name);
        var newAttachment = new Attachment(attachment.Name, attachment.Comment);
        newWorkItem.Attachments.Add(newAttachment);
    }
    }

    private static void LinkRelatedItems(WorkItemStore targetWorkItemStore, WorkItem sourceWorkItem)
    {
    int newId = WorkItemIdMap[sourceWorkItem.Id];
    WorkItem targetItem = targetWorkItemStore.GetWorkItem(newId);
    foreach (Link l in sourceWorkItem.Links)
    {
        if (l is RelatedLink)
        {
        RelatedLink sl = l as RelatedLink;
        switch (sl.ArtifactLinkType.Name)
        {
            case "Related Workitem":
            {
                if (WorkItemIdMap.ContainsKey(sl.RelatedWorkItemId))
                {
                int RelatedWorkItemId = WorkItemIdMap[sl.RelatedWorkItemId];
                RelatedLink rl = new RelatedLink(sl.LinkTypeEnd, RelatedWorkItemId);
                // !!!!
                // this does not work - need to check the existing links to see if one exists already for the linked workitem.  
                // using contains expects the same object and that's not what I'm doing here!!!!!!
                //if (!targetItem.Links.Contains(rl))
                // !!!!
                var query = from RelatedLink qrl in targetItem.Links where qrl.RelatedWorkItemId == RelatedWorkItemId select qrl;
                if (query.Count() == 0)
                {
                    targetItem.Links.Add(rl); ;
                    // Validate before save
                    if (!targetItem.IsValid())
                    {
                    var reasons = targetItem.Validate();
                    Console.WriteLine(string.Format("Could not validate work item (old id: {0}) related link id {1}.", sourceWorkItem.Id, sl.RelatedWorkItemId));
                    foreach (Field reason in reasons)
                    {
                        Console.WriteLine("Field: " + reason.Name + ", Status: " + reason.Status + ", Value: " + reason.Value);
                    }
                    }
                    else
                    {
                    try
                    {
                        targetItem.Save(SaveFlags.None);
                        Console.ForegroundColor = ConsoleColor.Cyan;
                        Console.WriteLine(string.Format(" [Updated: {0}]", targetItem.Id));
                        Console.ResetColor();
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                        throw;
                    }
                    }

                }
                }
                break;
            }
            default:
            { break; }
        }

        }
    }
    }

    private static void EnsureThatStructureExists(string projectName, string structureType, string structurePath)
    {
    var parts = structurePath.Split('\\');

    var css = GetCommonStructureService();
    var projectInfo = css.GetProjectFromName(projectName);
    var parentNodeUri = GetCssStructure(GetCommonStructureService(), projectInfo.Uri, structureType).Uri;
    var currentPath = FormatPath(projectName, structureType == Areas ? "Area" : "Iteration");
    foreach (var part in parts)
    {
        currentPath = FormatPath(currentPath, part);
        Console.Write(currentPath);

        try
        {
        var currentNode = css.GetNodeFromPath(currentPath);
        parentNodeUri = currentNode.Uri;
        Console.ForegroundColor = ConsoleColor.DarkGreen;
        Console.WriteLine(" [found]");
        }
        catch
        {
        parentNodeUri = css.CreateNode(part, parentNodeUri);
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine(" [created]");
        }
        Console.ResetColor();
    }
    }

    private static string FormatPath(string currentPath, string part)
    {
    part = part.Substring(part.IndexOf("\\") + 1);
    currentPath = string.Format(@"{0}\{1}", currentPath, part);
    return currentPath;
    }

    private static TfsTeamProjectCollection GetProjectCollection(Uri uri)
    {
    TfsTeamProjectCollection collection;
    if (!Collections.TryGetValue(uri, out collection))
    {
        collection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(uri);
        collection.Connect(ConnectOptions.IncludeServices);
        collection.Authenticate();
        Collections.Add(uri, collection);
    }
    return Collections[uri];
    }

    private static WorkItemStore GetSourceWorkItemStore()
    {
    var collection = GetProjectCollection(SourceCollectionUri);
    return collection.GetService<WorkItemStore>();
    }

    private static WorkItemStore GetTargetWorkItemStore()
    {
    var collection = GetProjectCollection(TargetCollectionUri);
    return collection.GetService<WorkItemStore>();
    }

    public static NodeInfo GetCssStructure(ICommonStructureService css, String projectUri, String structureType)
    {
    return css.ListStructures(projectUri).FirstOrDefault(node => node.StructureType == structureType);
    }

    private static ICommonStructureService GetCommonStructureService()
    {
    var collection = GetProjectCollection(TargetCollectionUri);
    return collection.GetService<ICommonStructureService>();
    }
}
}

Lars Wilhelmsen wrote a WorkItemMigrator -> http://larsw.codeplex.com/SourceControl/list/changesets

Good starting point for a utility you can customize for your needs. We used it to split off a 100 or so work items to a new project. Here's the program I ended up with. Modify the query to subset the items to migrate.

namespace WorkItemMigrator
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Framework.Common;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

class Program
{

    #region Members
    private static readonly Dictionary<Uri, TfsTeamProjectCollection> Collections = new Dictionary<Uri, TfsTeamProjectCollection>();
    private static readonly Uri SourceCollectionUri = new Uri("http://your.domain.com:8080/tfs/DefaultCollection");
    private static readonly Uri TargetCollectionUri = new Uri("http://your.domain.com:8080/tfs/DefaultCollection");
    private const String Areas = "ProjectModelHierarchy";
    private const string Iterations = "ProjectLifecycle";
    private const string TargetProjectName = "TargetProject";
    private const string MicrosoftVstsCommonStackRankFieldName = "Microsoft.VSTS.Common.StackRank";
    private const string MicrosoftVstsCommonPriority = "Microsoft.VSTS.Common.Priority";
    private const string TargetWorkItemType = "User Story";
    private const string Wiql = "SELECT [System.Id], [System.State], [System.Title], [System.AssignedTo], [System.WorkItemType], [Microsoft.VSTS.Common.Priority], " +
                        "[System.IterationPath], [System.AreaPath], [System.History], [System.Description] " +
                        "FROM WorkItems WHERE [System.TeamProject] = 'SourceProject' AND " + 
                        "[System.State] = 'Active' " +
                        "ORDER BY [System.Id]";

    private static WorkItemTypeCollection WorkItemTypes;
    private static Dictionary<int, int> WorkItemIdMap = new Dictionary<int, int>();

    #endregion
    static void Main()
    {
    var createAreasAndIterations = GetRunMode();

    var sourceWorkItemStore = GetSourceWorkItemStore();

    var sourceWorkItems = sourceWorkItemStore.Query(Wiql);

    var targetWorkItemStore = GetTargetWorkItemStore();
    var targetProject = targetWorkItemStore.Projects[TargetProjectName];


    WorkItemTypes = targetProject.WorkItemTypes;

    foreach (WorkItem sourceWorkItem in sourceWorkItems)
    {
        if (createAreasAndIterations)
        {
        Console.WriteLine();
        EnsureThatStructureExists(TargetProjectName, Areas, sourceWorkItem.AreaPath.Substring(sourceWorkItem.AreaPath.IndexOf("\\") + 1));
        EnsureThatStructureExists(TargetProjectName, Iterations, sourceWorkItem.IterationPath.Substring(sourceWorkItem.IterationPath.IndexOf("\\") + 1));
        }
        else
        {
        MigrateWorkItem(sourceWorkItem);
        }
    }

    if (!createAreasAndIterations)
    {
        var query = from WorkItem wi in sourceWorkItems where wi.Links.Count > 0 select wi;
        foreach (WorkItem sourceWorkItem in query)
        {
        LinkRelatedItems(targetWorkItemStore, sourceWorkItem);
        }
    }

    TextWriter tw = File.CreateText(@"C:\temp\TFS_MigratedItems.csv");
    tw.WriteLine("SourceId,TargetId");
    foreach (var entry in WorkItemIdMap)
    {
        tw.WriteLine(entry.Key + "," + entry.Value);
    }
    tw.Close();
    Console.WriteLine();
    Console.WriteLine("Done! Have a nice day.");
    Console.ReadLine();
    }

    private static bool GetRunMode()
    {
    bool createAreasAndIterations;
    while (true)
    {
        Console.Write("Create [A]reas/Iterations or [M]igrate (Ctrl-C to quit)?: ");
        var command = Console.ReadLine().ToUpper().Trim();
        if (command == "A")
        {
        createAreasAndIterations = true;
        break;
        }
        if (command == "M")
        {
        createAreasAndIterations = false;
        break;
        }
        Console.WriteLine("Unknown command " + command + " - try again.");
    }
    return createAreasAndIterations;
    }

    private static void MigrateWorkItem(WorkItem sourceWorkItem)
    {

    var targetWIT = WorkItemTypes[sourceWorkItem.Type.Name];
    var newWorkItem = targetWIT.NewWorkItem();
    //var newWorkItem = targetWorkItemType.NewWorkItem();

    // Description (Task) / Steps to reproduce (Bug)

    if (sourceWorkItem.Type.Name != "Bug")
    {
        newWorkItem.Description = sourceWorkItem.Description;
    }
    else
    {
        newWorkItem.Fields["Microsoft.VSTS.TCM.ReproSteps"].Value = sourceWorkItem.Description;
    }

    // History
    newWorkItem.History = sourceWorkItem.History;
    // Title
    newWorkItem.Title = sourceWorkItem.Title;
    // Assigned To
    newWorkItem.Fields[CoreField.AssignedTo].Value = sourceWorkItem.Fields[CoreField.AssignedTo].Value;
    // Stack Rank - Priority
    newWorkItem.Fields[MicrosoftVstsCommonPriority].Value = sourceWorkItem.Fields[MicrosoftVstsCommonPriority].Value;
    // Area Path
    newWorkItem.AreaPath = FormatPath(TargetProjectName, sourceWorkItem.AreaPath);
    // Iteration Path
    newWorkItem.IterationPath = FormatPath(TargetProjectName, sourceWorkItem.IterationPath);
    // Activity
    if (sourceWorkItem.Type.Name == "Task")
    {
        newWorkItem.Fields["Microsoft.VSTS.Common.Activity"].Value = sourceWorkItem.Fields["Microsoft.VSTS.Common.Discipline"].Value;
    }
    // State
    //newWorkItem.State = sourceWorkItem.State;
    // Reason
    //newWorkItem.Reason = sourceWorkItem.Reason;

    // build a usable rendition of prior revision history
    RevisionCollection revisions = sourceWorkItem.Revisions;
    var query = from Revision r in revisions orderby r.Fields["Changed Date"].Value descending select r;
    StringBuilder sb = new StringBuilder(String.Format("Migrated from work item {0}<BR />\n", sourceWorkItem.Id));
    foreach (Revision revision in query)
    {
        String history = (String)revision.Fields["History"].Value;
        if (!String.IsNullOrEmpty(history))
        {
        foreach (Field f in revision.Fields)
        {
            if (!Object.Equals(f.Value, f.OriginalValue))
            {
            if (f.Name == "History")
            {
                string notation = string.Empty;
                if (revision.Fields["State"].OriginalValue != revision.Fields["State"].Value)
                {
                notation = String.Format("({0} to {1})", revision.Fields["State"].OriginalValue, revision.Fields["State"].Value);
                }
                //Console.WriteLine("<STRONG>{0} Edited {3} by {1}</STRONG><BR />\n{2}", revision.Fields["Changed Date"].Value.ToString(), revision.Fields["Changed By"].Value.ToString(), f.Value, notation);
                sb.Append(String.Format("<STRONG>{0} Edited {3} by {1}</STRONG><BR />\n{2}<BR />\n", revision.Fields["Changed Date"].Value.ToString(), revision.Fields["Changed By"].Value.ToString(), f.Value, notation));
            }
            }
        }
        //Console.WriteLine("Revision {0}: ", revision.Fields["Rev"].Value);
        //Console.WriteLine("  ChangedDate: " + revision.Fields["ChangedDate"].Value);
        //Console.WriteLine("  History: " + sb.ToString());
        }
    }
    newWorkItem.History = sb.ToString();

    // Attachments
    for (var i = 0; i < sourceWorkItem.AttachedFileCount; i++)
    {
        CopyAttachment(sourceWorkItem.Attachments[i], newWorkItem);
    }

    // Validate before save
    if (!newWorkItem.IsValid())
    {
        var reasons = newWorkItem.Validate();
        Console.WriteLine(string.Format("Could not validate new work item (old id: {0}).", sourceWorkItem.Id));
        foreach (Field reason in reasons)
        {
        Console.WriteLine("Field: " + reason.Name + ", Status: " + reason.Status + ", Value: " + reason.Value);
        }
    }
    else
    {
        Console.Write("[" + sourceWorkItem.Id + "] " + newWorkItem.Title);
        try
        {
        newWorkItem.Save(SaveFlags.None);
        Console.ForegroundColor = ConsoleColor.Cyan;
        Console.WriteLine(string.Format(" [saved: {0}]", newWorkItem.Id));
        WorkItemIdMap.Add(sourceWorkItem.Id, newWorkItem.Id);
        Console.ResetColor();
        }
        catch (Exception ex)
        {
        Console.WriteLine(ex.Message);
        throw;
        }
    }
    }

    private static void CopyAttachment(Attachment attachment, WorkItem newWorkItem)
    {
    using (var client = new WebClient())
    {
        client.UseDefaultCredentials = true;
        client.DownloadFile(attachment.Uri, attachment.Name);
        var newAttachment = new Attachment(attachment.Name, attachment.Comment);
        newWorkItem.Attachments.Add(newAttachment);
    }
    }

    private static void LinkRelatedItems(WorkItemStore targetWorkItemStore, WorkItem sourceWorkItem)
    {
    int newId = WorkItemIdMap[sourceWorkItem.Id];
    WorkItem targetItem = targetWorkItemStore.GetWorkItem(newId);
    foreach (Link l in sourceWorkItem.Links)
    {
        if (l is RelatedLink)
        {
        RelatedLink sl = l as RelatedLink;
        switch (sl.ArtifactLinkType.Name)
        {
            case "Related Workitem":
            {
                if (WorkItemIdMap.ContainsKey(sl.RelatedWorkItemId))
                {
                int RelatedWorkItemId = WorkItemIdMap[sl.RelatedWorkItemId];
                RelatedLink rl = new RelatedLink(sl.LinkTypeEnd, RelatedWorkItemId);
                // !!!!
                // this does not work - need to check the existing links to see if one exists already for the linked workitem.  
                // using contains expects the same object and that's not what I'm doing here!!!!!!
                //if (!targetItem.Links.Contains(rl))
                // !!!!
                var query = from RelatedLink qrl in targetItem.Links where qrl.RelatedWorkItemId == RelatedWorkItemId select qrl;
                if (query.Count() == 0)
                {
                    targetItem.Links.Add(rl); ;
                    // Validate before save
                    if (!targetItem.IsValid())
                    {
                    var reasons = targetItem.Validate();
                    Console.WriteLine(string.Format("Could not validate work item (old id: {0}) related link id {1}.", sourceWorkItem.Id, sl.RelatedWorkItemId));
                    foreach (Field reason in reasons)
                    {
                        Console.WriteLine("Field: " + reason.Name + ", Status: " + reason.Status + ", Value: " + reason.Value);
                    }
                    }
                    else
                    {
                    try
                    {
                        targetItem.Save(SaveFlags.None);
                        Console.ForegroundColor = ConsoleColor.Cyan;
                        Console.WriteLine(string.Format(" [Updated: {0}]", targetItem.Id));
                        Console.ResetColor();
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                        throw;
                    }
                    }

                }
                }
                break;
            }
            default:
            { break; }
        }

        }
    }
    }

    private static void EnsureThatStructureExists(string projectName, string structureType, string structurePath)
    {
    var parts = structurePath.Split('\\');

    var css = GetCommonStructureService();
    var projectInfo = css.GetProjectFromName(projectName);
    var parentNodeUri = GetCssStructure(GetCommonStructureService(), projectInfo.Uri, structureType).Uri;
    var currentPath = FormatPath(projectName, structureType == Areas ? "Area" : "Iteration");
    foreach (var part in parts)
    {
        currentPath = FormatPath(currentPath, part);
        Console.Write(currentPath);

        try
        {
        var currentNode = css.GetNodeFromPath(currentPath);
        parentNodeUri = currentNode.Uri;
        Console.ForegroundColor = ConsoleColor.DarkGreen;
        Console.WriteLine(" [found]");
        }
        catch
        {
        parentNodeUri = css.CreateNode(part, parentNodeUri);
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine(" [created]");
        }
        Console.ResetColor();
    }
    }

    private static string FormatPath(string currentPath, string part)
    {
    part = part.Substring(part.IndexOf("\\") + 1);
    currentPath = string.Format(@"{0}\{1}", currentPath, part);
    return currentPath;
    }

    private static TfsTeamProjectCollection GetProjectCollection(Uri uri)
    {
    TfsTeamProjectCollection collection;
    if (!Collections.TryGetValue(uri, out collection))
    {
        collection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(uri);
        collection.Connect(ConnectOptions.IncludeServices);
        collection.Authenticate();
        Collections.Add(uri, collection);
    }
    return Collections[uri];
    }

    private static WorkItemStore GetSourceWorkItemStore()
    {
    var collection = GetProjectCollection(SourceCollectionUri);
    return collection.GetService<WorkItemStore>();
    }

    private static WorkItemStore GetTargetWorkItemStore()
    {
    var collection = GetProjectCollection(TargetCollectionUri);
    return collection.GetService<WorkItemStore>();
    }

    public static NodeInfo GetCssStructure(ICommonStructureService css, String projectUri, String structureType)
    {
    return css.ListStructures(projectUri).FirstOrDefault(node => node.StructureType == structureType);
    }

    private static ICommonStructureService GetCommonStructureService()
    {
    var collection = GetProjectCollection(TargetCollectionUri);
    return collection.GetService<ICommonStructureService>();
    }
}
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文