2

I'm working on a project team and our application is in TFS. I'm attempting to determine how many lines of code each team member is responsible. In TFS, I'm aware of the Annotate feature in the Visual Studio interface which allows you to see who last modified each line of code so I know TFS has this information.

I've written a small console app which accesses my TFS project and all its files, but I now need to programmatically access annotations so I can see who the owner of each line is. Here is my existing code:

using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;

public class Program
{
    static void Main(string[] args)
    {
        var credentials = new NetworkCredential(username, password, domain);
        var server = new TfsTeamProjectCollection(new Uri(serverUrl), credentials);
        var version = server.GetService(typeof(VersionControlServer)) as VersionControlServer;
        var items = version.GetItems(projectPath, RecursionType.Full);
        var fileItems = items.Items.Where(x => x.ItemType == ItemType.File);            
        foreach (var fileItem in fileItems)
        {
            var serverItem = fileItem.ServerItem;
            //TODO: retrieve and parse annotations
        }
    }
}

I can't seem to figure out how to retrieve annotations once I have the TFS item. This link explains how to do it by calling TFPT, but after implementing it (tfpt annotate /noprompt <filename>), you are only give the last changeset and code per line, not the owner.

I also found a Microsoft.TeamFoundation.VersionControl.Server namespace that has an Annotation class. I installed TFS on my machine to have access to that DLL, but it doesn't seem like it is of any help to this problem.

How can you programmatically access TFS annotations to determine the owner of a line of code for a file?

Community
  • 1
  • 1
mellis481
  • 4,332
  • 12
  • 71
  • 118
  • http://stackoverflow.com/questions/74526/tfs-annotate-blame-summary-report-for-a-project – CodeCaster May 06 '16 at 13:09
  • @CodeCaster: Ha. I just updated my question as you commented. The answer isn't really an answer... – mellis481 May 06 '16 at 13:10
  • Can you explain how it isn't helpful? If it's true, then there's no built-in way to do it and no tool for it either (note that directly asking for tools is off-topic). You can however use the TFS API and write a small program that does this. – CodeCaster May 06 '16 at 13:15
  • 1
    @CodeCaster: I guess the real problem is the seemingly almost complete lack of documentation for the TFS VersionControl library. – mellis481 May 06 '16 at 13:24

1 Answers1

1

You may have to query the branch when a Item's change type is Branch. For a simple example, there is a scenario

$/Project
  /Main`
   /a.txt
  /Develop
   /a.txt (branched from main)

When you query the history of $/project/Develop/a.txt, you can also get the history of $/project/Main/a.txt using following code

void GetAllHistory(string serverItem)
    {                    
        var changesets=vcs.QueryHistory(serverItem,
            Microsoft.TeamFoundation.VersionControl.Client.VersionSpec.Latest,
            0,
             Microsoft.TeamFoundation.VersionControl.Client.RecursionType.None,
             null,
             new Microsoft.TeamFoundation.VersionControl.Client.ChangesetVersionSpec(1),
              Microsoft.TeamFoundation.VersionControl.Client.VersionSpec.Latest,
              int.MaxValue,
              true,
              false);

        foreach (var obj in changesets)
        {
            Microsoft.TeamFoundation.VersionControl.Client.Changeset cs = obj as Microsoft.TeamFoundation.VersionControl.Client.Changeset;
            if (cs == null)
            {
                return;
            }
            foreach (var change in cs.Changes)
            {
                if (change.Item.ServerItem != serverItem)
                {
                    return;
                }

                Console.WriteLine(string.Format("ChangeSetID:{0}\tFile:{1}\tChangeType:{2}", cs.ChangesetId,change.Item.ServerItem, change.ChangeType));
                if ((change.ChangeType & Microsoft.TeamFoundation.VersionControl.Client.ChangeType.Branch) == Microsoft.TeamFoundation.VersionControl.Client.ChangeType.Branch)
                {
                    var items=vcs.GetBranchHistory(new Microsoft.TeamFoundation.VersionControl.Client.ItemSpec[]{new Microsoft.TeamFoundation.VersionControl.Client.ItemSpec(serverItem, Microsoft.TeamFoundation.VersionControl.Client.RecursionType.None)},
                        Microsoft.TeamFoundation.VersionControl.Client.VersionSpec.Latest);

                    GetAllHistory(items[0][0].Relative.BranchToItem.ServerItem);
                }
            }

        }
    }
Ye Peng
  • 183
  • 6
  • No where in this code do I see where I can get the owner of each line of code. I implemented it and the only owner I can see is the owner of a changeset (`obj.Owner`). – mellis481 May 12 '16 at 22:39
  • 1
    I found a working online project for you that explains how to get annotations by TFS API. Actually it is using same things presented above - get recursion data of Item[0][0] from GetBranchHistory() results. However the implemetations are more complicated. You may want to include that GitHub module into your project if license is all right. https://github.com/SonarQubeCommunity/sonar-scm-tfvc – Ye Peng May 13 '16 at 16:37
  • Thanks for linking me to the full project. It is quite involved/complicated, but I was able to use some of the classes in that repo to annotate files per line! I'm accepting your answer, but when I have some time, I'll post my completed code with all I needed. Thanks again! – mellis481 May 16 '16 at 12:47
  • Cool. Glad to hear the problem is solved. I also learned annotation has more than I thought that may be why MS doesn't expose an API for it. – Ye Peng May 17 '16 at 05:42