Introduction
In this post, I am going to demonstrate comparing changeset in C#. I will cover the following topics:
- Get history (list of all changeset)
- Download and compare files
For comparing two files, we use Winmerge.

Take reference of the below DLLs.

<table width="100%">
<tr>
<td>
<asp:Literal ID="litTfsParentFolderPath" runat="server"
Text="TFS Parent Folder Path"></asp:Literal></td>
<td>
<asp:TextBox ID="txtTfsParentFolderPath" runat="server"
Text="$/HBE/HBE/DEV1/Source/Business/Eligibility/*"
Enabled="false" Width="400px"></asp:TextBox>
</td>
</tr>
<tr>
<td>
<asp:Literal ID="litChildFolderName" runat="server"
Text="TFS Child Folder Name"></asp:Literal></td>
<td>
<asp:DropDownList ID="ddlChildFolders" runat="server"
Width="410px" OnSelectedIndexChanged="ddlChildFolders_SelectedIndexChanged"
AutoPostBack="true"></asp:DropDownList>
</td>
</tr>
<tr>
<td colspan="2">
<asp:GridView ID="grvChangeSetData" runat="server"
AutoGenerateColumns="False" CellPadding="4" ForeColor="#333333"
GridLines="None" OnRowDataBound="grvChangeSetData_RowDataBound"
OnRowCommand="grvChangeSetData_RowCommand">
<AlternatingRowStyle BackColor="White" ForeColor="#284775" />
<Columns>
<asp:TemplateField HeaderText="Changeset Number"
HeaderStyle-Width="10%" ItemStyle-Width="10%"
ItemStyle-VerticalAlign="Top">
<ItemTemplate>
<asp:Literal ID="litChangeSetNo" runat="server"
Text='<%#Bind("ChangeSetNumber") %>'> </asp:Literal>
</ItemTemplate>
<HeaderStyle HorizontalAlign="Center" Width="10%"></HeaderStyle>
<ItemStyle VerticalAlign="Top" Width="10%"></ItemStyle>
</asp:TemplateField>
<asp:TemplateField HeaderText="Owner" HeaderStyle-Width="10%"
ItemStyle-Width="10%" ItemStyle-VerticalAlign="Top">
<ItemTemplate>
<asp:Literal ID="litOwnerDisplayName" runat="server"
Text='<%#Bind("OwnerDisplayName") %>'> </asp:Literal>
</ItemTemplate>
<HeaderStyle HorizontalAlign="Center" Width="10%"></HeaderStyle>
<ItemStyle VerticalAlign="Top" Width="10%"></ItemStyle>
</asp:TemplateField>
<asp:TemplateField HeaderText="Created Date" HeaderStyle-Width="10%"
ItemStyle-Width="10%" ItemStyle-VerticalAlign="Top">
<ItemTemplate>
<asp:Literal ID="litCreateDate" runat="server"
Text='<%#Bind("CreateDate") %>'> </asp:Literal>
</ItemTemplate>
<HeaderStyle HorizontalAlign="Center" Width="10%"></HeaderStyle>
<ItemStyle VerticalAlign="Top" Width="10%"></ItemStyle>
</asp:TemplateField>
<asp:TemplateField HeaderText="Comment" HeaderStyle-Width="10%"
ItemStyle-Width="10%" ItemStyle-VerticalAlign="Top">
<ItemTemplate>
<asp:Literal ID="litComment" runat="server"
Text='<%#Bind("Comments") %>'> </asp:Literal>
</ItemTemplate>
<HeaderStyle HorizontalAlign="Center" Width="10%"></HeaderStyle>
<ItemStyle VerticalAlign="Top" Width="10%"></ItemStyle>
</asp:TemplateField>
<asp:TemplateField HeaderText="Files" HeaderStyle-Width="10%"
ItemStyle-Width="10%" ItemStyle-VerticalAlign="Top">
<ItemTemplate>
<asp:Repeater ID="rpFiles" runat="server"
OnItemCommand="rpFiles_ItemCommand">
<HeaderTemplate>
<table width="100%">
</HeaderTemplate>
<ItemTemplate>
<tr>
<td>
<asp:LinkButton ID="lnkFileName" runat="server"
Text='<%# Bind("FileName") %>'
CommandName="DownloadFile" CommandArgument='<%# Bind
("CommandArgument") %>'></asp:LinkButton>
<asp:LinkButton ID="lnkCompare"
runat="server" Text="Compare"
CommandName="CompareFile" CommandArgument='<%# Bind
("CommandArgument") %>'></asp:LinkButton>
</td>
</tr>
</ItemTemplate>
<FooterTemplate>
</table>
</FooterTemplate>
</asp:Repeater>
</ItemTemplate>
<HeaderStyle HorizontalAlign="Center"
Width="10%"></HeaderStyle>
<ItemStyle VerticalAlign="Top"
Width="10%"></ItemStyle>
</asp:TemplateField>
</Columns>
<EditRowStyle BackColor="#999999" />
<FooterStyle BackColor="#5D7B9D"
Font-Bold="True" ForeColor="White" />
<HeaderStyle BackColor="#5D7B9D"
Font-Bold="True" ForeColor="White" />
<PagerStyle BackColor="#284775"
ForeColor="White" HorizontalAlign="Center" />
<RowStyle BackColor="#F7F6F3"
ForeColor="#333333" />
<SelectedRowStyle BackColor="#E2DED6"
Font-Bold="True" ForeColor="#333333" />
<SortedAscendingCellStyle BackColor="#E9E7E2" />
<SortedAscendingHeaderStyle BackColor="#506C8C" />
<SortedDescendingCellStyle BackColor="#FFFDF8" />
<SortedDescendingHeaderStyle BackColor="#6F8DAE" />
</asp:GridView>
</td>
</tr>
</table>
Put the below code in .cs file:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.TeamFoundation;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Framework.Client;
using Microsoft.TeamFoundation.Framework.Common;
using Microsoft.TeamFoundation.VersionControl.Client;
namespace TFSDemo
{
public partial class _Default : Page
{
#region Properties
public TfsTeamProjectCollection teamProjectCollection
{
get { return GetTFS(); }
}
public string GetTFSPath
{
get { return ConfigurationManager.AppSettings["URI"]; }
}
public string GetUser
{
get { return ConfigurationManager.AppSettings["User"]; }
}
public string GetPassword
{
get { return ConfigurationManager.AppSettings["Password"]; }
}
public string GetDomain
{
get { return ConfigurationManager.AppSettings["Domain"]; }
}
#endregion
#region PageLoad
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
BindSubFolders();
}
}
#endregion
#region Events
protected void ddlChildFolders_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
string filePath = ddlChildFolders.SelectedItem.Value.ToStringSafe();
List<SubFolderChangeSet> listChangeSet = GetChangeSetBySubFolderName(filePath);
grvChangeSetData.DataSource = listChangeSet.Distinct().Select(x => x);
grvChangeSetData.DataBind();
}
catch (Exception ex)
{
throw ex;
}
}
protected void grvChangeSetData_RowDataBound(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow)
{
Repeater rpFiles = e.Row.FindControl("rpFiles") as Repeater;
if (rpFiles != null)
{
rpFiles.ItemCommand += new RepeaterCommandEventHandler(rpFiles_ItemCommand);
List<Change> listChangeset = ((e.Row.DataItem) as SubFolderChangeSet).listChange;
List<FileNames> listFileName = new List<FileNames>();
if (listChangeset != null && listChangeset.Count > 0)
{
foreach (Change change in listChangeset)
{
FileNames file = new FileNames();
file.ChangeSetNumber = ((e.Row.DataItem) as SubFolderChangeSet).ChangeSetNumber;
file.FileName = change.Item.ServerItem.Substring
(change.Item.ServerItem.LastIndexOf('/') + 1);
file.CommandArgument = file.ChangeSetNumber + "@" + file.FileName;
listFileName.Add(file);
}
rpFiles.DataSource = listFileName;
rpFiles.DataBind();
}
}
}
}
protected void rpFiles_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if (e.CommandName.Equals("DownloadFile", StringComparison.OrdinalIgnoreCase))
{
string[] args = e.CommandArgument.ToStringSafe().Split('@');
if (args != null && args.Length > 0)
{
int changesetId = args[0].ToIntSafe();
VersionControlServer vcs = GetVersionServer(teamProjectCollection);
if (vcs != null)
{
Changeset cs = vcs.GetChangeset(changesetId);
foreach (Change ch in cs.Changes)
{
string filename = ch.Item.ServerItem.Substring
(ch.Item.ServerItem.LastIndexOf('/') + 1);
if (filename.Equals(args[1].ToStringSafe(), StringComparison.OrdinalIgnoreCase))
{
ch.Item.DownloadFile(System.IO.Path.GetTempPath() +
ch.Item.ChangesetId +
ch.Item.ServerItem.Split('/')[
ch.Item.ServerItem.Split('/').Length - 1]);
}
}
}
}
}
if (e.CommandName.Equals("CompareFile", StringComparison.OrdinalIgnoreCase))
{
string[] args = e.CommandArgument.ToStringSafe().Split('@');
if (args != null && args.Length > 0)
{
int changesetId = args[0].ToIntSafe();
VersionControlServer vcs = GetVersionServer(teamProjectCollection);
if (vcs != null)
{
Changeset cs = vcs.GetChangeset(changesetId);
string matchFile = string.Empty;
string filePath = string.Empty;
filePath = System.IO.Path.GetTempPath();
foreach (Change ch in cs.Changes)
{
string filename = ch.Item.ServerItem.Substring
(ch.Item.ServerItem.LastIndexOf('/') + 1);
if (filename.Equals(args[1].ToStringSafe(), StringComparison.OrdinalIgnoreCase))
{
matchFile = ch.Item.ServerItem.ToStringSafe();
ch.Item.DownloadFile(filePath +
ch.Item.ChangesetId +
ch.Item.ServerItem.Split('/')[
ch.Item.ServerItem.Split('/').Length - 1]);
}
}
if (!string.IsNullOrWhiteSpace(matchFile))
{
Item item = vcs.GetItem(matchFile);
item.DownloadFile(filePath + changesetId + "-New-" +
matchFile.Split('/')[matchFile.Split('/').Length - 1]);
var winmerge = Process.Start(@"C:\Program Files (x86)\WinMerge\WinMergeU.exe",
String.Format("{0}{1} {0}{2}", filePath,
@"\" + changesetId + matchFile.Substring
(matchFile.LastIndexOf('/') + 1),
@"\" + changesetId + "-New-" +
matchFile.Substring(matchFile.LastIndexOf('/') + 1)));
}
}
}
}
}
protected void grvChangeSetData_RowCommand(object sender, GridViewCommandEventArgs e)
{
}
#endregion
#region PrivateMethods
private TfsTeamProjectCollection GetTFS()
{
TfsTeamProjectCollection tpc = null;
try
{
Uri tfsUri = new Uri(GetTFSPath);
TfsConfigurationServer configurationServer =
TfsConfigurationServerFactory.GetConfigurationServer(tfsUri);
configurationServer.Credentials = new NetworkCredential(GetUser, GetPassword, GetDomain);
configurationServer.Authenticate();
ReadOnlyCollection<CatalogNode> collectionNodes =
configurationServer.CatalogNode.QueryChildren(new[]
{ CatalogResourceTypes.ProjectCollection }, false, CatalogQueryOptions.None);
foreach (CatalogNode collectionNode in collectionNodes)
{
Guid collectionId = new Guid(collectionNode.Resource.Properties["InstanceId"]);
tpc = configurationServer.GetTeamProjectCollection(collectionId);
}
}
catch (Exception ex)
{
throw ex;
}
return tpc;
}
private VersionControlServer GetVersionServer(TfsTeamProjectCollection teamProjectCollection)
{
return teamProjectCollection.GetService<VersionControlServer>();
}
private void BindSubFolders()
{
try
{
if (teamProjectCollection != null)
{
VersionControlServer vcs = GetVersionServer(teamProjectCollection);
var subFolders = vcs.GetItems(txtTfsParentFolderPath.Text.ToStringSafe());
List<string> listSubFolders = new List<string>();
foreach (var item in subFolders.Items)
{
listSubFolders.Add(item.ServerItem);
}
ddlChildFolders.DataSource = listSubFolders;
ddlChildFolders.DataBind();
}
}
catch (Exception ex)
{
throw ex;
}
}
private List<SubFolderChangeSet> GetChangeSetBySubFolderName(string filePath)
{
List<SubFolderChangeSet> listChangeset = new List<SubFolderChangeSet>();
try
{
var vsStore = teamProjectCollection.GetService<VersionControlServer>();
var histories =
vsStore.QueryHistory(
filePath,
VersionSpec.Latest, default(int),
RecursionType.Full, null, null, null, Int32.MaxValue, true, false, true);
foreach (Changeset history in histories)
{
SubFolderChangeSet sbfChangeSet = new SubFolderChangeSet();
sbfChangeSet.ChangeSetNumber = history.ChangesetId;
sbfChangeSet.Owner = history.Owner;
sbfChangeSet.OwnerDisplayName = history.OwnerDisplayName;
sbfChangeSet.Comments = history.Comment;
listChangeset.Add(sbfChangeSet);
sbfChangeSet.listChange = new List<Change>();
sbfChangeSet.listChange.AddRange(history.Changes);
foreach (Change change in history.Changes)
{
sbfChangeSet.CreateDate = change.Item.CheckinDate;
}
}
}
catch (Exception ex)
{
throw ex;
}
return listChangeset;
}
#endregion
}
#region Classes
public class SubFolderChangeSet
{
public int ChangeSetNumber { get; set; }
public string Owner { get; set; }
public string OwnerDisplayName { get; set; }
public string Comments { get; set; }
public DateTime CreateDate { get; set; }
public List<Change> listChange { get; set; }
}
public class FileNames
{
public int ChangeSetNumber { get; set; }
public string FileName { get; set; }
public string CommandArgument { get; set; }
}
#endregion
}