Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / DevOps / TFS

TFS SDK: Compare Changesets Programmatically

5.00/5 (1 vote)
22 Aug 2011CPOL2 min read 32.1K  
TFS SDK: Compare changesets programmatically

I will be covering three things in this blog post:

  • Get the history, i.e., all change sets of an item programmatically using the TFS API
  • Download the change sets programmatically using the TFS API
  • Use WinMerge to compare those change sets programmatically

1. How Do I Get the History of a File Using TFS API?

The VersionControlServer Class exposes the QueryHistory method that gets you all changesets that have impacted a file or folder you are querying for.

public IEnumerable QueryHistory(
    // The local path to an item for which history will be queried. 
    // This parameter can include wildcards
    string path, 
    
    // The version of the item for which history will be queried.
    VersionSpec version, 
    
    // The unique deletion ID for the item, if it is deleted. Otherwise, specify 0
    int deletionId, 
    
    // A flag describing whether history will be recursively queried
    RecursionType recursion, 
    
    // The user for whom history will be queried. Specify null for any user
    string user, 
    
    // The earliest version for which history will be queried.
    // Specify null to begin with the first changeset
    VersionSpec versionFrom, 
    
    // The latest version for which history will be queried. 
    // Specify null to end with the latest changeset
    VersionSpec versionTo, 
    
    // The maximum number of history entries to return. 
    // Specify Int32.MaxValue to get all changes
    int maxCount, 
    
    // A flag that describes whether the individual item
    // changes will be included with the changesets. Otherwise,
    // only changeset metadata is included
    bool includeChanges, 
    
    // A flag that describes how history entries are searched.
    bool slotMode 
}

Example: Query the history of file TfsRepository.cs placed in the source control at ‘$/Temp_UK_1/Development/Prod/Source/TfsWorkspaceManager/TfsWorkspaceManager/TfsRepository.cs’.

var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection
(new Uri("https://tfs2010:8080/defaultcollection"));
var vsStore = tfs.GetService<VersionControlServer>();

var histories =
    vsStore.QueryHistory( //filepath
            "$/Temp_UK_1/Development/Prod/Source/
            TfsWorkspaceManager/TfsWorkspaceManager/TfsRepository.cs",
            VersionSpec.Latest, 0, RecursionType.OneLevel, null, 
            null, null, Int32.MaxValue, true, false, true);
 
foreach (Changeset history in histories)
{
    // Break to see what values history has
}

Histories is IEnumerable, as we enumerate we convert history to type changeset. Let’s see the results:

image

There are two overloads:

  • bool includeDownloadInfo: A flag that describes whether to get the information necessary to download the change sets from the server
  • bool sortAscending: A flag that describes whether to sort the results in ascending order. Specify false to not sort the results.

2. Download the Files in the Changeset Programmatically

The Item Class exposes the DownloadFile method that downloads the content for this version of the item.

Example: Download all changesets of the TfsRepository.cs file to Windows Temp folder.

public static void DownloadHistoryChangesetsToTempFolder()
{
    var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection
              (new Uri("https://tfs2010:8080/defaultcollection"));
    tfs.EnsureAuthenticated();
    var vsStore = tfs.GetService<VersionControlServer>();

    var histories =
           vsStore.QueryHistory( //filepath
                    "$/Temp_UK_1/Development/Prod/Source/
                    TfsWorkspaceManager/TfsRepository.cs",
                    VersionSpec.Latest, 0, RecursionType.OneLevel, 
                    null, null, null, Int32.MaxValue, true, false, true);

    foreach (Changeset history in histories)
    {
       foreach (Change change in history.Changes)
       {
           change.Item.DownloadFile(System.IO.Path.GetTempPath() + change.Item.ChangesetId + 
                       change.Item.ServerItem.Split('/')
                       [change.Item.ServerItem.Split('/').Length - 1]);
       }
    }
}

Screen shot of the windows temp folder:

image

3. Compare the Downloaded Versions Programmatically using WinMerge

Download and Install WinMerge. WinMerge is an Open Source differencing and merging tool for Windows. WinMerge can compare both folders and files, presenting differences in a visual text format that is easy to understand and handle. You can read more about how to change the default Compare and Merge tool in Team Foundation Server in my earlier blog post here.

Example: Compare the earlier downloaded history versions of TfsRepository.cs from the windows temp folder.

WinMerge command line access with a bunch of useful arguments. You can do WinMergeU.exe /? to get the details of the arguments.

WinMerge[U] [/r] [/e] [/f filter] [/x] [/s] [/ul] [/ur] [/u] [/wl] [/wr] [/minimize] [/maximize] [/dl leftdesc] [/dr rightdesc] leftpath rightpath [outputpath]

WinMerge[U] conflictfile

public static void CompareTwoChangeSets()
{
    var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection
    (new Uri("https://tfs2010:8080/defaultcollection"));
    tfs.EnsureAuthenticated();
    var vsStore = tfs.GetService<VersionControlServer>();

    var histories =
               vsStore.QueryHistory( //filepath
                   "$/Temp_UK_1/Development/Prod/Source/TfsWorkspaceManager/
                   TfsWorkspaceManager/TfsWorkspaceManager/TfsRepository.cs",
                   VersionSpec.Latest, 0, RecursionType.OneLevel, null, 
                   null, null, Int32.MaxValue, true, false, true);

        foreach (Changeset history in histories)
        {
            foreach (Change change in history.Changes)
            {
                change.Item.DownloadFile(System.IO.Path.GetTempPath() +
                                            change.Item.ChangesetId +
                                            change.Item.ServerItem.Split('/')[
                                                change.Item.ServerItem.Split('/').Length - 1]);
        }
    }

    var winmerge = Process.Start(@"C:\Program Files 
                   (x86)\WinMerge\WinMergeU.exe",
                   String.Format("{0}{1} {0}{2}", 
                   System.IO.Path.GetTempPath(),
                   @"\36TfsRepository.cs", 
                   @"\37TfsRepository.cs"));
}

So, I am starting a Process to consume the WinMergeU.exe and passing the 2 versions of the file that I want to compare. You can also use the following arguments:

  • /r compares all files in all subfolders (recursive compare). Unique folders (occurring only on one side) are listed in the compare result as separate items. Note that including subfolders can increase compare time significantly. Without this parameter, WinMerge lists only files and subfolders at the top level of the two target folders. It does not compare the subfolders.
  • /dl specifies a description in the left side title bar, overriding the default folder or filename text. For example: /dl "Version 1.0" or /dl WorkingCopy. Use quotation marks around descriptions that contain spaces.
  • /dr specifies a description in the right side title bar, just like /dl.

Screenshot of execution of the above method:

image

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)