Move work item in TFS

Is it possible to move a work item from one project to another inside TFS? I saw the copy option, but did not move. Also, if possible, what are the implications for any WI story?

I found this article from 2008, which doesn't seem to say, but I wondered if there has been progress since then.

+4
source share
2 answers

Unable to move, just copy. As we do this, we make a copy, bind the original, and then close the original as obsolete. You can also create a copy and TF Destroy the original, but you will lose the whole story.

If you wanted to, you could get very imagination and create your own โ€œmoveโ€ utility that copies the work item and the whole story, and then closes (or destroys) the old one. It seems like an overkill of things that you probably don't need to do so often.

+1
source

Lars Wilhelmsen wrote WorkItemMigrator โ†’ http://larsw.codeplex.com/SourceControl/list/changesets

A good starting point for a utility that you can configure for your needs. We used it to split about 100 work items into a new project. Here is the program I got into. Modify the query for a subset of 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 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>(); } } } 
0
source

Source: https://habr.com/ru/post/1314296/


All Articles