Discovering TFS merge history using the client API - Part 2 (calling QueryMerges)

Last time I laid out a basic framework for describing merge history in C# (the MergeInfo type) and showed the outer loop we’ll use.  Also we discussed the basic algorithm which I’ll summarize here:

 

targetItem = item merged to

potentialContributors = GetBranchHistory

FOR EACH potentialSourceItem IN potentialContributors

DO

      mergeInfo = QueryMerges(potentialSourceItem, targetItem)

      IF (merge happened)

            return potentialSourceItem

      END

END

Error: Contributor not found

 

Let’s start by looking at VersionControlServer.QueryMerges (think “tf merges”)

 

public ChangesetMerge[] QueryMerges(

string sourcePath,

VersionSpec sourceVersion,

string targetPath,

VersionSpec targetVersion,

VersionSpec versionFrom,

VersionSpec versionTo,

RecursionType recursion);

 

We’re taking a source path and version, a target path and version and a version range (from and to) and a recursion type.

 

If we want to find out what merged to the item “$/teamproject/team1/file4.txt;C5” we could do the following:

 

ChangesetMerge[] merged = tfsClient.QueryMerges(

    null,

    null,

    "$/teamproject/team1/file4.txt",

    new ChangesetVersionSpec(5),

    null,

    null,

    RecursionType.None);

 

This is effectively the same as running the following command:

 

>tf merges $/teamproject/team1/file4.txt;C5

Changeset Merged in Changeset Author Date

--------- ------------------- -------------------------------- ----------

       3* 4 rhorvick 2/7/2006

 

In this case we got back an array of one ChangesetMerge object.

 

Don’t remember what ChangesetMerge looks like?  I got your back …

 

public sealed class ChangesetMerge

{

    public bool Partial { get; }

    public int SourceVersion { get; }

    public Changeset TargetChangeset { get; }

    public int TargetVersion { get; }

    public override string ToString();

}

 

In the above example the object looked like this:

 

merged[0]

  +-Partial = true

  +-SourceVersion = 3

  +-TargetVersion = 4

  +-TargetChangeset = <changeset 4>

 

So we know that there was a merge whose target was "$/teamproject/team1/file4.txt" which occurred in changeset 4 (we also know it was a partial merge – meaning all of changeset 3 was not merged).  But that doesn’t tell us what was merged to that item.

 

That’s where we have to start looking at all the potential contributors.

 

Remember that the contributors are the output of a “tf branches” command..

 

> tf branches $/teamproject/team1/file4.txt

$/teamproject/main/file4.txt

>> $/teamproject/team1/file4.txt Branched from version 3 <<

                $/teamproject/devteams/dev1a/file4.txt Branched from version 4

                $/teamproject/devteams/dev1b/file4.txt Branched from version 4

        $/teamproject/team2/file4.txt Branched from version 3

                $/teamproject/devteams/dev2a/file4.txt Branched from version 4

                $/teamproject/devteams/dev2b/file4.txt Branched from version 4

 

So in this case we have 3 potential contributors – main and devteams dev1a and dev1b.

 

So if we enumerate the call to QueryMerges over each of them we should find that one, and exactly one, is a contributor.

 

Here’s how you would do it manually:

 

>tf merges $/teamproject/devteams/dev1a/file4.txt $/teamproject/team1/file4.txt

No merge history entries were found for the target item.

>tf merges $/teamproject/devteams/dev1b/file4.txt $/teamproject/team1/file4.txt

No merge history entries were found for the target item.

>tf merges $/teamproject/main/file4.txt $/teamproject/team1/file4.txt

Changeset Merged in Changeset Author Date

--------- ------------------- -------------------------------- ----------

       3 4 rhorvick 2/7/2006

 

The merge came from main.

 

Using the client OM the QueryMerges call would be:

 

ChangesetMerge[] merged = tfsClient.QueryMerges(

    "$/teamproject/main/file4.txt",

    new ChangesetVersionSpec(3),

    "$/teamproject/team1/file4.txt",

    new ChangesetVersionSpec(4),

    null,

    null,

    RecursionType.None);

Not so hard, eh?

 

But there is pain beyond this point.  What if the source or target file has been renamed since the merge occurred?  What if it has been deleted?  Try passing a deletion id to a “tf merges” command … go ahead.  Try it.

 

We’re not done yet.  Next time – dealing with deleted items.