Recently I wrote up a short overview on how TFS source control works for a client. I've reproduced it here as it may hopefully help you understand how TFS works and reduce the number of “wierdnesses” that people experience when using TFS.

For most people the normal behaviour when doing a “Get Latest” is one drilled into us through years of Source Safe (ab)use. Right click the solution file, and select Get Latest Version (Recursive) as shown here:

clip3

However with TFS we really should be doing it like this (via the Source Control Explorer):

clip4

Experienced TFS users will also typically use "Get Specific Version" with the Force Get option turned on.

Similarly when doing a check in through the Visual Studio UI (which is 99.99% of the time) it’s good practice to ALWAYS click the refresh button first to make sure you’re list of pending changes is accurate.

clip5

Why, though?

Problem 2 First

Let’s tackle the second issue (the refresh button) first. When files are changed in Visual Studio an event is raised indicating that the file has been checked out in source control and that it should be treated as a pending change. At various times those events don’t get picked up by the pending changes window in VS which will in turn means that the UI doesn’t refresh automatically. This can then result in a check in that misses required files, simply because the UI didn’t show them. (See this forum entry for background info)

By clicking the refresh button, the UI will re-query the status of the files in the workspace and give you the full list of files available for check in.

Note that it doesn’t ask the TFS server for the status of the files, just the workspace. Also if you have made changes outside of visual studio, or performed offline changes you’ll need to do a refresh of the pending status for all files in the workspace using the team foundation power tool (tfpt).

Other Gotcha's

There are other situations where you change a file but it doesn't show as being any different to the latest version in TFS. This is particularly noticeable with solution files and occurs because Visual Studio keeps the solution file in memory and doesn't write out the change unit you have saved it to disk.

There is also an issue with files that are writeable but not checked out. If you edit one of these writeable files in Visual Studio then VS assumes that the file is already checked out and will not check the file out of source control automatically if you start to edit it. This typically happens when editing files offline, or when using a third party program that overrides the readonly flag.

Understanding Workspaces et al

Now back to the first problem. Why should we do a get latest from Source Control Explorer instead of the solution file? The answer relates to the way in which TFS is designed.

Firstly, TFS Version Control uses the concept of workspaces to track file statuses. A workspace is TFS’s view of what files the server thinks you have on your local machine in a specified path (the local folder as shown here) and is treated as a snapshot of the source repository at a given point in time:

clip6

This way, when you do a get latest, TFS will only send you the updates it thinks you need, based on the changes made since you last updated your workspace. This is meant to help reduce network traffic and improve performance.

The other thing to understand is that TFS treats a workspace as a snapshot of the source repository and therefore each changeset is an atomic change to a know set of files. It expects that all files in a workspace are as at the same point in time.

In fact, this is the reason why doing a checkout of a single file only marks it as editable and doesn’t perform a get latest. Buck Hodges blog entry explains it better (emphasis mine):

Why doesn't Team Foundation get the latest version of a file on checkout?

I've seen this question come up a few times. Doug Neumann, our PM, wrote a nice explanation in the Team Foundation forum (http://forums.microsoft.com/msdn/ShowPost.aspx?PostID=70231).

It turns out that this is by design, so let me explain the reasoning behind it. When you perform a get operation to populate your workspace with a set of files, you are setting yourself up with a consistent snapshot from source control. Typically, the configuration of source on your system represents a point in time snapshot of files from the repository that are known to work together, and therefore is buildable and testable.

As a developer working in a workspace, you are isolated from the changes being made by other developers. You control when you want to accept changes from other developers by performing a get operation as appropriate. Ideally when you do this, you'll update the entire configuration of source, and not just one or two files. Why? Because changes in one file typically depend on corresponding changes to other files, and you need to ensure that you've still got a consistent snapshot of source that is buildable and testable.

This is why the checkout operation doesn't perform a get latest on the files being checked out. Updating that one file being checked out would violate the consistent snapshot philosophy and could result in a configuration of source that isn't buildable and testable. As an alternative, Team Foundation forces users to perform the get latest operation at some point before they checkin their changes. That's why if you attempt to checkin your changes, and you don't have the latest copy, you'll be prompted with the resolve conflicts dialog.

If you do a get latest in Visual Studio by right clicking the solution file, Visual Studio gets the current list of files references by the solution and requests the latest version of each of those files. It doesn’t do a get latest for the entire workspace. Why? Because Visual Studio is designed to work with a source control provider and not all source control systems work like TFS.

Since files are retrieved individually it also helps explain why a new project added to a solution sometimes won’t appear straight away and why you have to manually do another get latest to get it – and why you may have to do a “force update” as well.

If however you use SCE to do the “get latest” (and you do it from the workspace root) then you are updating your local code base with the entire snapshot, not individual files. In the case of a new project being added, your “get latest” would have retrieved the new solution file AND the new project’s source files, so when the solution reloads all the files will be present and you won’t be missing anything.

Hopefully this makes sense, but if not, please let me know so I can flesh out the confusing bits in more detail.