TFS API Part 55– Source Control Get History

10/09/2014

no comments

This post will demonstrate how to retrieve Source Control Items, placing those items into TreeView with dynamic search for children items.

Also I’ll show how to query Server Item history.

image

Download Demo Project

I’ve created WPF Application and added the following assemblies into my project:

  • Microsoft.TeamFoundation.Client
  • Microsoft.TeamFoundation.Common
  • Microsoft.TeamFoundation.VersionControl.Client.dll
  • Microsoft.TeamFoundation.VersionControl.Common

Step 1: Connect TFS

As always we need to connect TFS before doing anything.

public bool Connect()
{
    TeamProjectPicker tpp = new TeamProjectPicker(TeamProjectPickerMode.SingleProject, false);
    if (tpp.ShowDialog() == System.Windows.Forms.DialogResult.OK && tpp.SelectedProjects.Length > 0)
    {
        Tfs = tpp.SelectedTeamProjectCollection;
        ProjectInfo = tpp.SelectedProjects[0];
        Vcs = Tfs.GetService<VersionControlServer>();
        return true;
    }
    return false;
}

Step 2: Get Source Items

Using GetItems method from VersionControlServer we can search source control items, I recommend search just one level for better performance.

private async void GetSourceControlFirstLevel(RecursionType recursionType = RecursionType.OneLevel)
{
    SourceControlItems.Clear();
    string serverItem = "$/" + TfsShared.Instance.ProjectInfo.Name;
    ItemSet itemSet = null;
    Working = true;
    await Task.Run(() =>
    {
        ItemSpec spec = new ItemSpec(serverItem, recursionType);
        itemSet = TfsShared.Instance.Vcs.GetItems(spec, VersionSpec.Latest, DeletedState.Any, ItemType.Any, false);
    });

    foreach (var item in itemSet.Items.Where(i => !i.ServerItem.Equals(serverItem)))
        SourceControlItems.Add(new SourceControlItem(item));

    Working = false;
}

Step 3: Create SourceControlItem Class

Before we can start getting source control item history we need to display them in Tree View, an instead of drawing the entire tree lets do it based on items you’re interested in.

Each ItemSpec in from Source Control will be converted into SourceControlItem, and once LoadChildren method is invoked we’ll display that item children (Of course just for Folders)

Notice that in order to display items we’re using ItemSpec specifying OneLevel (Again we don’t want to download the entire tree)

public class SourceControlItem : TreeViewItemViewModel
    {
        public SourceControlItem(Item _item)
            : base(null, true)
        {
            this.Item = _item;
            this.Name = _item.ServerItem.Substring(_item.ServerItem.LastIndexOf(@"/", System.StringComparison.Ordinal) + 1);
        }

        public SourceControlItem()
            : base(null, true)
        {
            this.Item = null;
            this.Name = "Dummy";
        }

        public Item Item { get; set; }

        public string Name { get; set; }

        public string ServerItem
        {
            get { return Item.ServerItem; }
        }

        protected override void LoadChildren()
        {
            if (Item == null) return;
            ItemSpec spec = new ItemSpec(Item.ServerItem, RecursionType.OneLevel);
            ItemSet itemSet = TfsShared.Instance.Vcs.GetItems(spec, VersionSpec.Latest, DeletedState.Any, ItemType.Any, false);
            foreach (var item in itemSet.Items)
            {
                if (item.ItemId == this.Item.ItemId) continue;
                SourceControlItem srcItem = new SourceControlItem(item);

                if (item.ItemType == ItemType.File)
                    srcItem.IsExpanded = true;

                base.Children.Add(srcItem);
            }
        }

        public ItemType Type
        {
            get { return Item.ItemType; }
        }

        public string Size
        {
            get
            {
                return Item.ItemType == ItemType.File
                    ? string.Format(new FileSizeFormatProvider(), " - Size: {0:fs}", Item.ContentLength)
                    : string.Format(new FileSizeFormatProvider(), " - Size: {0:fs} (first level)", base.Children.Sum(i => i.Item.ContentLength));
            }
        }

        public string ToolTip
        {
            get { return string.Format("Checkin Date: {0}\nIs Branch: {1}\nChangeset Id:{2}", Item.CheckinDate, Item.IsBranch, Item.ChangesetId); }
        }

        public int ID
        {
            get { return Item.ItemId; }
        }
    }

Step 4: Get Server Item History

To receive server item history we’ll call QueryHistory method from VersionControlServer, there are several ways for getting source item history but the following method we return the changes types while the rest we only return the changesets.

The query history is very simple, passing the Server Item Path, From Version, To Version)

private async void GetHistory()
{
    if (SelectedSourceControlItem == null) return;
    Working = true;
    SelectedItemHistory.Clear();
    await Task.Run(() =>
    {
        string serverItem = SelectedSourceControlItem.ServerItem;
        List<Changeset> changesets = new List<Changeset>();
        ItemSpec spec = new ItemSpec(serverItem, RecursionType.None);

        ChangesetVersionSpec version = new ChangesetVersionSpec(SelectedSourceControlItem.Item.ChangesetId);
        ChangesetVersionSpec versionFrom = new ChangesetVersionSpec(1);

        changesets = TfsShared.Instance.Vcs.QueryHistory(serverItem,
        version, 0, RecursionType.None, null,
        versionFrom, VersionSpec.Latest, int.MaxValue, true, false).Cast<Changeset>().ToList();

        foreach (var change in changesets)
            SelectedItemHistory.AddOnUi(change);
    });
    Working = false;
}

Step 5: Display Changes Types

Once you get the Changeset object we might notice he contains the list of Changes happened in that Changeset, I wrote a tiny converter to display just the change types.

public class ChangetsetChangesStringConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value == null) return null;
        string changesStr = string.Empty;
        if (value is Change[])
        {
            Change[] changes = value as Change[];
            changesStr = changes.Aggregate(changesStr, (current, change) => current + (change.ChangeType.ToString() + ", "));
        }
        return changesStr.TrimEnd(',', ' ');
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Enjoy

Download Demo Project

Add comment
facebook linkedin twitter email

Leave a Reply