Generating Diffs was the first feature we needed to add where we had to use the TFS .NET objects.  This is probably the preferable way to do everything (because we can probably count on it being backwards compatible), and maybe the rest should be rewritten (if we ever do, I'll let you know).

We were forced to use it because file contents for older versions are stored as patches (differences), so it's a lot easier to get the full versions of files from TFS than from SQL.

Using the TFS .NET objects is a little hard to deploy in ASP.NET. The issue is that downloading files is not done in the calling thread, which means that the credentials of the call to TFS is that of the worker thread. You have two choices:

1. Give the owner or the worker thread access to TFS (no!) or 2. Run in an AppDomain and give the app domain a user with credentials to TFS (this is what we do).

Getting the file contents is pretty simple:

  1. Add references:

    Microsoft.TeamFoundation
    Microsoft.TeamFoundation.Client Microsoft.TeamFoundation.VersionControl.Client

  2. Add these two lines to your code behind:

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

  3. Here's the code to get File contents for a changeset (cs) and the one right before it (for the diff)

                    Microsoft.TeamFoundation.Client.TeamFoundationServer tfs = new TeamFoundationServer("http://"+YOUR_TFS_SERVER+":8080", System.Net.CredentialCache.DefaultCredentials);
                    tfs.Authenticate();               

                    VersionControlServer vcs = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));
                    GetFileContents(vcs, path, cs, File1);
                    GetFileContents(vcs, path, cs-1, File2);


    protected void GetFileContents(VersionControlServer vcs, string path, int cs, TextBox fileBox)
        {
            ItemSet itemSetCurr =
                        vcs.GetItems(path, VersionSpec.ParseSingleSpec("C" + cs, ""), RecursionType.None, DeletedState.NonDeleted, ItemType.Any, true);
            String temp = Path.GetTempFileName();
            itemSetCurr.Items[0].DownloadFile(temp);
            using (StreamReader sr = new StreamReader(new FileStream(temp, FileMode.Open, FileAccess.Read)))
            {
                fileBox.Text = sr.ReadToEnd();
            }
            File.Delete(temp);
        }



    File1 and File2 are ASP.NET TextBox objects -- I am going to store them there and do all of the diffing with JavaScript using jsdifflib from Snowtide, which is open source.

    If you get permission errors on       

    itemSetCurr.Items[0].DownloadFile(temp);

    It's probably because the actual work is being done in the background (but the call is synchronous).  Put your ASP.NET website in an appdomain with access to TFS

  4. I attached my ASPX and ASPX.cs file -- you will need to download jsdifflib files and put in server info the cs file.

  5. In Fogbugz, set the URL with ?path=^FILE&cs=^R2
We also link the log from Part II to diffs -- which I think is a good idea as well.