Get List of Files, and for each file get a list of Changesets it is part of for a specific work item in TFS


If you are like me doing code review all the time using TFS, you would like to have a tool, by which you enter a work item number, the tool will provide you with a list of files, and for each of the files, it offers a list of changeset number.

Well, out-of-box TFS explorer does not offer the tool I am draming of. However, TFS does provide a very rich set of of API, by which you can write the tool yourself.

This is how I did:

1)      Started my solution with a class libarary project with the language you like ( I selected C#). Name it in the way you want it, (I named it as  TFSAdapter).
2)      Add referecne to the following assembilies:
a.       Microsoft.TeamFoundation.Client
b.      Microsoft.TeamFoundation.Common
c.       Microsoft.TeamFoundation.VersionControl.Client
d.      Microsoft.TeamFoundation.WorkItemTracking.Client
3)      Rename the class1.cs to a meaningful name, ( I named it as WorkItemAgent)
4)      Add a small entity class to the project
public class FileTouched
    {
       public string  FileName { get; set;}
       public List<int> ChangeSets { get; set; }


       public FileTouched()
       {
           ChangeSets = new List<int>();
       }

       public FileTouched(string fileName) : this()
       {
           FileName = fileName;
       }
    }

5)      Add following private variables to the WorkItemAgent class:
        private Uri collectionUri;      

        private TfsTeamProjectCollection projectCollection;
        private WorkItemStore workItemStore;
        private VersionControlServer vcs;
6)      Add a constrcutor as following:
  public WorkItemAgent(string tfsServerUrl )
        {
            collectionUri = new Uri(tfsServerUrl);
                   projectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(collectionUri);
                   vcs = (VersionControlServer)projectCollection.GetService(typeof(VersionControlServer));
                   workItemStore = projectCollection.GetService<WorkItemStore>();
        }

7)      Add first private method, named GetWorkItem
public WorkItem GetWorkItem(int workItemNumber)
        {
            return workItemStore.GetWorkItem(workItemNumber);
        }

8)      Add another private method GetChangeSets
public List<Changeset> GetChangeSets(WorkItem workitem)
        {
            List<Changeset> associatedChangesets = new List<Changeset>();

            ExternalLink extLink ;        


            foreach (Link link in workitem.Links)
            
            {
                if (link.BaseType == BaseLinkType.ExternalLink)
                {
                    extLink = link as ExternalLink;
                    if (extLink != null)
                    {
                        ArtifactId artifact = LinkingUtilities.DecodeUri(extLink.LinkedArtifactUri);
                        if (String.Equals(artifact.ArtifactType, "Changeset", StringComparison.Ordinal))
                        {
                            associatedChangesets.Add(vcs.ArtifactProvider.GetChangeset(new Uri(extLink.LinkedArtifactUri)));
                        }
                    }                  
                }
            }
            return associatedChangesets;
        }

9)      Add last private method GetFilesAssociatedWithChangeSet
public List<string> GetFilesAssociatedWithChangeSet(Changeset changeSet)
        {
            List<string> files = new List<string>();

            foreach (var changedItem in changeSet.Changes)
            {
                string item = changedItem.Item.ServerItem;
                if (!files.Contains(item))
                {
                    files.Add(item);
                }
            }

            return files;

        }

10)   Now, we have done enough ground work, let’s add a  a public method named GetFileList as following:

public List<FileTouched> GetFileList(int workItemNumber)
        {
            Dictionary<string, FileTouched> result = new Dictionary<string, FileTouched>();

            WorkItem myWorkitem = this.GetWorkItem(workItemNumber);

            List<Changeset> ChangeSets = this.GetChangeSets(myWorkitem);

            List<string> filelist;

            foreach (Changeset cs in ChangeSets)
            {
                filelist = GetFilesAssociatedWithChangeSet(cs);
                foreach (string fileName in filelist)
                {
                    if (!result.ContainsKey(fileName))
                    {
                        result.Add(fileName, new FileTouched(fileName));
                    }
                    result[fileName].ChangeSets.Add(cs.ChangesetId);
                }
            }
            List<FileTouched> listResult = new List<FileTouched>();
            foreach (string key in result.Keys)
            {
                listResult.Add(result[key]);
            }
            var sortedresult = from file in listResult orderby file.ChangeSets.Count select file;
            return sortedresult.ToList();
        }

11)   If you like me is a big fun of unit testing, you wouild add a test project to test these methods, let me show you one of the test cases I have made:
[TestMethod()]
        public void GetFileListTest()
        {
            WorkItemAgent target = new WorkItemAgent(“the URL for the TFS Server”); // something like http://newtoy:8080/tfs/defaultcollection
            int workItemNumber = 77675; // TODO: the work item number you have  to an appropriate value
         
            List<FileTouched> actual;
            actual = target.GetFileList(workItemNumber);

            foreach (FileTouched file in actual)
            {
                Console.WriteLine(file.FileName);
                foreach (int cs in file.ChangeSets)
                {
                    Console.Write(cs.ToString() + ",");
                }
                Console.WriteLine();
            }
          
        }

If you are as luck as I am, you would get the list of files and for each of the file, you get a list of changeset number. With this class library, you can easily put a UI to make it useful for you. Either Window form or an ASP site would not be too difficult for anybody with reasonable experience in .Net framework. A window form works for me. My next target is to make it a Visual Studio add-in… I will let you know when get that done. If you are interested to get the complete source code of this tool, please feel free to drop me an email, I will zip them up and send it over.  Happy working with TFS.

No comments:

Post a Comment