Tuesday, October 31, 2006

How to handle paths with TFS Version Control object model

After I have been developing with TFS Version Control object model for quite a while, I have come across very helpful class. I wish I did that couple of months before, as it would have saved me quite a bit of time in writing them string parsing functions.
I am talking about VersionControlPath class. This is a static class located in Microsoft.TeamFoundation.VersionControl.Common assembly, and it contains ton of routines you might need when working with version control items.

To give you a small sampling:


// Returns folder name from item path ("$/Project/Folder" from "$/Project/Folder/File.txt")


public static string GetFolderName(string item)


// Returns project name from item path ("Project" from "$/Project/Folder/File.txt")


public static string GetTeamProjectName(string item)


// Check whether specified path conforms to Windows or TFS path syntax (basically contains / or \ delimiter)


public static bool IsServerItem(string path)


// Prepends path with root $ char if required


public static string PrependRootIfNeeded(string folder)


// Parses item path and returns folder and file item paths


public static void Parse(string item, out string parent, out string name)




There is a dearth of other methods in VersionControlPath. Most of them are self-explanatory named, but may be a bit tricky to work with. For example, IsValidPath method will return true both for "$/Project/Folder" and "/Project/Folder" paths.

I wish I could direct you to MSDN, but documentation there is pretty thin (to be diplomatic about it).

If you find any interesting gotchas in that class, I would be delighted to know. Drop me a line.

Tuesday, October 24, 2006

HasTrailingSlash in MSBuild scripts

There is useful undocumented function that may be used in MSBuild scripts conditions, called HasTrailingSlash. As its name implies, the function checks its only argument for trailing backward slash:

Sample updated at 01-Fev-2008 (recently somebody complained that while function works my old sample does not :):

<!-- Initial target will be always called first to verify properties -->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
InitialTargets="VerifyInputParameters">
 
    <!-- Target verifies that the property is not empty (throws error) -->
    <!-- and has trailing slash (adds one if no slash provided)        -->
    <Target Name="VerifyInputParameters">

        <Error Condition="'$(ExternalPath)' == ''" Text="ExternalPath is empty" />

        <CreateProperty Condition="!HasTrailingSlash('$(ExternalPath)')" 
        Value="$(ExternalPath)\">

              <Output TaskParameter="Value" PropertyName="ExternalPath" />

        </CreateProperty>
    </Target>
    <!-- Here goes the rest of the project -->
</Project>


<PropertyGroup>


 <OutputPath Condition=" !HasTrailingSlash('$(OutputPath)') ">


  $(OutputPath)\


 </OutputPath>


</PropertyGroup>



The thing is, the function is used extensively in Microsoft system targets files (for example, in Microsoft.TeamFoundation.Build.targets file) and still does not appear in MSBuild documentation. Beats me...

See also new TFSBuild site for more details.

Monday, October 16, 2006

Getting associated work items for changeset

Lately we have been working at next version of our Team Foundation Sidekicks application (more specifically at labels-related Sidekick), and as part of the development it was required to retrieve all work items associated with changeset.
Initially, the following simple code was used:

Changeset changeset = server.GetChangeset(changesetId);


Once Changeset object is instantiated, its WorkItems property readily makes all associated work items available.

The problem was the method performance; so after some search I found Naren Datha's post solving the same task. The code there uses WorkItemStore and ILinking objects and looks much more complex (as compared to one liner we used before). Heck, it must work quicker (or so I thought)!

But after some benchmarks, it turned out that those two approaches have very similar performance. I did not perform exhaustive research, but the execution time was essentially the same (within 10% delta).

So my guess would be that both methods use same core queries; and as far as complexity goes we shall stay with our previous one liner. Wouldn't you?

Removing iteration hidden goodies

I have just found a new feature in TFS! That still amazes me how after a whole year I am involved with TFS, there are still hidden and unexplored places to go.

In short, I was going to remove iteration path I have created by mistake, and there TFS goes displaying the window shown below:



Frankly, I was not thinking about work items at the time as I knew the iteration path was without any items assigned, but TFS thinking about that on my part - that is all goodness. Sure, if I had some work items I would like to migrate them, and the thoughtful dialog displayed is not a bad way of doing it.

I guess the possible usage of the feature (besides deleting iterations created by mistake :) would be for moving work items in iterative development. For example, in one setup we frequently assign all strange low priority tasks to iteration "Backlog" in milestone; so it would be convenient to delete that backlog and move all leftover backlog work items to next milestone.

It would be interesting to see if someone out there is in fact using that feature.

Wednesday, October 11, 2006

Renaming Team Project

When you create new Team project, give some serious consideration to its name. The Team project cannot be renamed in TFS v1 (No comment about it - you may find some opinions in MSDN usergroups).

The rename operation seems so obvious to the folks that I thought it is worth to post about it. You may well imagine with what incredulity the absense of the feature is met when need to rename project arises (there you have some really colorful language :).

If you already named your project and looking at ways to rename it, you may use the following workaround (sort of workaround, because you won't really end up with renamed project with identical contents):
1) Create new project with desired name
2) Copy work items from old project to a new one (one-by-one, as there is no bulk copy option).
3) Move all source control folders from under old project folder to a new project folder
4) Sharepoint portal documents cannot be moved in bulk (as far as I know), so you do that manually
Move of source code will retain the files history, and work items will also have partial history, but overall I would say the workaround does not worth the labour (and Sharepoint docs will not have their history). At any rate, selecting right name in the beginning beats any workaround by far.

So be circumspect when you name your Team project!

Update: I stand corrected, as there exist freeware utility for moving work items between Team Projects (written by Eric Lee); see posts here and here.

Saturday, September 16, 2006

Cached TeamFoundationServer

Yesterday I have read Buck Hodges blog post on how to get instance of TeamFoundationServer. Getting TeamFoundationServer is the first thing one does when writing code that utilizes TFS Version Control object model, so naturally it is worth to know. Frankly, I did not think I will discover something new, but you live and you learn...

Two choices available are TeamFoundationServer class constructor or TeamFoundationServerFactory GetServer method. Buck covers the usage quite nicely in his post. The point of interest for me was that TeamFoundationServerFactory method will actually return same object in two different calls if given same URL as GetServer parameter.

That means if you use GetService method of returned TeamFoundationServer instance, it will essentially be the same service! So for example, if you retrieve VersionControlServer and hook up onto some event, you will need to do it only once; the second instance of TeamFoundationServer returned by factory will be the same and will return the same VersionControlServer with event handler set (below is pseudo code just to visualize the idea; no chance it will compile):

// first place
tfs1 = TeamFoundationServerFactory.GetServer(url);
vc1 = tfs1.GetService();
vc1.NewPendingChange += event1;
...
// second place
tfs2 = TeamFoundationServerFactory.GetServer(url);
vc2 = tfs2.GetService();
vc2.NewPendingChange += event1; // not required! already set

So that is something you'd want to keep in mind while writing your applications.

P.S. And some additional piece of wizdom from commentaries to the post:
"I recommend obtaining services from TFS OM for all services except the WorkItemStore. It is not thread safe, where as all other services you obtain from TFS OM are. To work around this issue, create a new WorkItemStore object and pass the credentials that you get from the TFS OM."
It is not official and I did not check that, but I love to assemble those bits of information. You never know when it may come in handy ...

Friday, September 15, 2006

Merging Visual Studio solutions

Recently, I have read a post in MSDN, and that reminded me of important issue I meant to raise for quite some time.
The scenario is rather simple - let us say that you branch folder that contain Visual Studio solution (or project); then you perform development in both branches. At some stage you merge one branch onto another.

While it is obvious how code files (C#, C++ etc.) are merged, for Visual Studio project and solution files it is less so. Even if you do not peform advanced changes in those files (for example, specifying different custom pre-/post- build steps), Visual Studio itself may change the file (see the problem is described in the post). And when you merge, usually there is no conflict and changes are merged automatically and thus you can end up with invalid solution or project file!

It appears there is no magic bullet solution for the issue in current version of TFS. What I do is essentially manual procedure: the idea is to check whether any solution/project files were merged. In most cases there are no conflicts to resolve, so I manually review the merged solution/project files before checking them in, to make sure that automatic merge changes make sense. It may be paranoid but is way better than broken solution.

More than that, after some thought on the subject, I do not see how it may be handled (aside from customized merge wizard specifically for Visual Studio solutions and projects). Any thoughts on the subject would be appreciated (I believe Microsoft guys will thank you as well).

Wednesday, September 06, 2006

Copying work items - hidden gotchas

Today I came across Eric Lee's post about copying work items. I also discovered this function quite accidentaly and have been happily using it for several months already.

So you right-click on selected Work Item in Query Results or on open Work Item, and click "Create Copy of Work Item..." - and voila! New item with identical data is displayed for you, so you can modify and save it. It allows one to avoid hassle of copying common fields or easily copy item to another project.

All goodness, but there are some not so obvious features within...

First, the newly created work item will be linked to the source work item (work item you copied a new work item from). If that is not your intention, and you do not glance on "Links" tab contents - you are in for surprise. And if you do that for some time then you have a whole lot of links. For example, if you have Item 1, then created Item 2 (by copying from Item 1) and then created Item 3 (by copying from Item 2) - now, how many linked items you will have in Item 3? You will have two - Item 1 and Item 2. That is surely a feature to be aware of (especially if you do not want to link those items)! I have discovered it only after I created the whole bunch of interlinked items...

Additionally, there is something very interesting in the history of the newly created work item. If you are creating items one after another (as in example above), all that information will be saved in history!

Here you can see copied work item history:


And here the first history entry expanded (and that is only a part of it):


Not that I care much about that information currently. It may be useful if you are trying to propagate bug through several Team projects (say bug found in "Project 1" will be copied to "Project 1.1" and then to "Project 1.2" - the data will be visible in history); but with current implementation of Team projects I doubt it is of much use. On the other hand, if you are copying items only for convenience, I do not see how that information is useful to anyone.

Those two I have discovered in a course of some two months of usage; but I will not be surprised if there are additional goodies in that function. And I wonder - what was the idea of the original author?

Friday, August 18, 2006

Circular references in Active Directory

Recently in user feedback on Team Foundation Sidekicks application we have dealt with issue that may be of importance to people with complex Active Directory dependencies between user groups.

To put it simple - if you have circular relationships between user groups in AD and those groups are used in TFS, IGroupSecurityService ReadIdentity method will fail. I did not see it but on one occasion and do not know how that API is used in TFS core, but my experience with Active Directory tells me that circular relationship is not something very rare to come by.

So if you feel that way also, have a look on the following post.

Shared workspace mappings

For everyone that has used TFS source control it is well known fact that it is impossible to create more than one workspace mapping to same directory (in single or different workspace on same workstation). When you try to do that, TFS error message pops up informing you that the path is already mapped somewhere.

For non-shared computer there is no probem in the situation; you just use the workspace with mapping or create a new path.

Now, on workstations used by several people (for example, integration stations) there may be lot of value in the "shared" workspace, namely so that each user after logging in has mapping in his workspace to the same path. Until today, I did not think that possible, but came across very interesting post in MSDN newsgroup that suggests a solution.

The solution is simple and elegant! You just map disk drive to the directory, and while users Alice and Bob have mappings in their workspaces to G:\ProjectA and F:\ProjectA, they in fact will be working with same project in c:\src\ProjectA. One might note that it is not single shared workspace but instead workspace per user with the same mapping, but heck - that's the best solution we have! Surely beats having separate directory for each user just for mapping.

Kudos to Nate for suggesting the solution.