Looking at the world through rose-colored versionspecs

James' post on the DateTime versionspec reminded me that I wanted to post a brief summary of versionspecs in Visual Studio Team Foundation's Version Control.

There are 5 different flavors of version specifications, each with its own syntax. Their one letter identifiers from the command line are ‘T’ for ‘tip’, ‘C’ for ‘changeset’, ‘D’ for ‘date’, ‘L’ for label’, and ‘W’ for ‘workspace’. For the purposes of our discussion, let's suppose we've got a server with a single solution named "Galactic Explorer" containing a file originally named "MyFile.cs". This file may have been edited, renamed, deleted, undeleted, branched, merged, etc. Let's say it's been changed in changesets 307 (add), 310 (edit), 311 (rename to aFile.cs), 312 (rename to AFile.cs), 317 (delete), and 319 (undelete and rename to OldFile.cs). In particular, here's a snapshot of this History dialog on this particular file. How can we access a particular version of this file? Why, by using our handy versionspecs!

First and foremost, we have the "Tip" version specification. This is also called "Latest" in the UI. This versionspec indicates that we want to work with the most recent version of the file in the server. In the case of OldFile.cs, that would equate to changeset number 319. From the command line, we could say "tf get OldFile.cs;T" in order to retrieve the latest version of the file. If you don't specify a versionspec, "T" is assumed. Where this comes in handy, though, is if you want to compare another version to the latest: "tf diff MyFile.cs;C314~T". What's the "C314"? Well...

Changesets, as I've mentioned before, are atomic groups of changes applied to files on the server. The changeset numbers are monotonically increasing and always increment by 1 from one change to the next. Each changeset number is unique among all changes on a given server, even if the affected items are in different team projects. When you specify a changeset versionspec, the server looks for the version of the item that existed when that changeset was checked in. In this case, if we specify changeset 314 of OldFile.cs (e.g. “tf get OldFile.cs;C314”), we’ll get the version that was checked in with changeset 312. It's also important to note that this is the default if you don't specify a letter-- "tf get OldFile;C314" is the same as "tf get OldFile.cs;314".

Another way to specify an exact point in time is with the date versionspec. Since changesets occur at a single point in time, using the date version spec maps to the last changeset submitted before the specified time. For more, read James’ post here.

Labels are basically a collection of item/version pairs. A label may contain an arbitrary set of items on the server, and exist within a certain folder's scope. Users specify which version of the items to label when they apply the label to each specific item. For more on labels, check here. One important thing to note about label versionspecs is that any item not labeled will be considered as non-existent for the purposes of that command. For example, let’s say we have a label “MyLabel” applied to version C312 of OldFile.cs, among other files. Another file in the folder, “OtherFile.cs”, is not labeled by MyLabel but existed in changeset 312. Suppose we run get in that directory: “tf get * /version:LMyLabel”. We’ll see something like this:

D:\Galactic Explorer\Galactic Explorer:Replacing AFile.cs (moved from D:\Galactic Explorer\Galactic Explorer\OldFile.cs)Deleting D:\Galactic Explorer\Galactic Explorer\OtherFile.cs

What happened? We got the pre-rename version of OldFile.cs. Then, the server noted that we’d asked for the version of OtherFile.cs in the label MyLabel. Since no such version exists, it removed the file. Thus, you can specifically exclude certain files from certain operations. This is important for consistency of our commands, as well. If you say "tf get OldFile.cs;C20", we'll remove the file from disk since it didn't exist in that version. The same goes for C317 and C318 since the file was deleted during those changesets.

Lastly, there are workspace versions. At first glance, this may seem somewhat silly. Workspace version indicates the version last gotten in a particular workspace. Hence, if you simply say “tf get * /r /version:W”, you’re telling the server to download the last version you downloaded. This should never do anything, though you can add the "/force" flag to redownload. However, you can append a different workspace here, such as "tf get */r /version:WMyOtherWorkspace" to get the same version in the current workspace as you have in another of your workspaces. Suppose that a coworker has found a bug and you want to reproduce it under a debugger. You can run “tf get * /r /version:WTheirWorkspace;DOMAIN\Coworker” to get the last version of the code which they downloaded. This is also handy for buddy testing before you unshelve someone else’s changes if you want to have the same setup that they have.

I hope that helps clear things up a bit. Personally, I’m most likely to use tip, changeset, and date, though I have used the workspace version a few times.