Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TFS Folders - Getting them to work like Subversion "Trunk/Tags/Branches"

Tags:

svn

tfs

I recently started using Team Foundation Server, and am having some trouble getting it to work the way I want it to.

I've used Subversion for a couple years now, and love the way it works. I always set up three folders under each project, Trunk, Tags, and Branches.

When I'm working on a project, all my code lives under a folder called "C:\dev\projectname". This "projectname" folder can be made to point to either trunk, or any of the branches or tags using Subversion (with the switch command).

Now that I'm using TFS (my client's system), I'd like things to work the same way. I created a "Trunk" folder with my project in it, and mapped "Project/Trunk/Website" to "c:\dev\Website".

Now, I want to make a release under the "tags" folder (located in "Project/Tags/Version 1.0/Website", and TFS is giving me the following error when I execute the branch command:

"No appropriate mapping exists for $Project/tags/Version 1.0/Website"

From what I can find on the internet, TFS expects you to have a mapping to your hard drive at the root of the project (the "Project" folder in my case), and then have all the source code that lives in trunk, tags and branches all pulled down to your hard drive. This sucks because it requires way too much stuff on your hard drive, and even worse, when you are working in a solution in Visual Studio, you won't be able to pull down "Version 2.0" and have all your project references to other projects work, because they'll all be pointing to "trunk" folders under the main folder, not just the main folder itself.

What I want to do is have the root "Project/Website" folder on my hard drive, and be able to have it point to (mapped to) either tags, branches, or trunk, depending on what i'm doing, without having to screw around with fixing Visual Studio project references.

Ideas?

like image 350
Sam Schutte Avatar asked Aug 14 '09 15:08

Sam Schutte


2 Answers

The first thing to understand is that the TFS model of the version tree is different from what you may be used to. It's at an extreme end of the spectrum.‡

I've used Subversion for a couple years now, and love the way it works. I always set up three folders under each project, Trunk, Tags, and Branches.

What you think of as "tags, branches, and trunks" are all represented by folders in TFS. A "trunk" is a folder that was created by a simple add, not as a branch of another folder. A "branch" is a folder that's derived from (and henceforth related to) another folder elsewhere in the system via the Branch & Merge operations. TFS doesn't have "tags" per se; the closest analogue is to create a branch based off of a historical version (via changeset number or label). Once again, this becomes yet another folder.

When it comes to managing your local workspace, the concept that "everything's a folder" is good news. If you want to create arbitrarily complex views on tags/branches/trunks, in the TFS world that task reduces down to the very simple problem of mapping local paths <-> server paths.

Of course, that doesn't mean you should. Disk space is a lot cheaper than network bandwidth or server CPU cycles. More importantly: the more complex your mappings, the more likely you are to accidentally commit the wrong file to the wrong place. My usual recommendation is:

  1. Create one workspace per branch/tag that you actively work on.
  2. In each workspace, create just one recursive mapping to the root of the branch.
    • If this is not possible due to the size of the branches, avoid cloaks. They aren't a good solution because they won't stay in sync with folder adds/renames made by other people. Better to use a one-level mapping to the branch root + additional recursive mappings to the folders you need.
    • If this is not possible because you have build-time dependencies not contained in the branch, shame on you :) Add the fewest # of additional mappings out to common libraries as possible.
  3. Ensure the relative paths are the same within each branch. (And if they change, the changes in structure propagate in lockstep with the changes to your makefiles.)
  4. Create one additional workspace for what I'll call "maintenance" tasks.§

This strategy provides:

  • Zero resource overhead. No redownloading required when you context-switch.
  • Low mental overhead. To start working on another branch, just open the other copy of the solution from disk. Or change the Workspace dropdown in Source Control Explorer.
  • Low chance of screwups. Because branches are confined to their own workspace, clicking Checkin All will never commit changes that were pending in another branch...or worse, you can't accidentally make changes to the "release" branch that were intended to go against the "unstable" version.

What I want to do is have the root "Project/Website" folder on my hard drive, and be able to have it point to (mapped to) either tags, branches, or trunk, depending on what i'm doing, without having to screw around with fixing Visual Studio project references.

This is possible too. As mentioned you're just trading disk space for bandwidth/CPU. No big deal if your infrastructure supports it. I personally would find it too limiting in terms of parallel development, plus a higher chance of screwups -- but it's only natural a person raised on SVN might feel differently.

Here are the steps:

  1. Create one workspace. Map it to the branch/tag/trunk you intend to work on next.
  2. Follow all the other guidelines above.
  3. When it's time to change branch/tag/trunk, reopen the Workspace dialog and point the same local folder to the new server path.
  4. Get Latest.
    • Starting in TFS 2008, the client will automatically prompt you to run Get anytime you make a change like this.
    • Starting in TFS 2008 SP1, there is an even better way. Click "no" on the prompt, then run tf get /remap. This will only download the diffs between the two branches. This can be a huge bandwidth/CPU savings depending on folder size and how closely related they are. (Could actually take more server CPU on folders that are very small, very distantly related, or [of course] not related at all; use good judgment. Should always take strictly less bandwidth, though.)

‡In TFS, when you create a branch, it appears to the user as a brand new folder hierarchy. Put another way: when you look at the repository a priori, there's no clear way to distinguish "real" files/folders from branches. And frankly there isn't much difference. A "branch" is just another item, one that happens to have >=1 pieces of merge history metadata associated with it. Plenty of TFS commands depend on said metadata, and you can certainly query for it directly, but it won't show up in a simple tf dir. Meanwhile, because every branch occupies a unique position in path space, that means it's no more or less complex to uniquely specify a branched versionspec. $/path;changeset is sufficient, just like any other item.

CVS takes the opposite approach. When you branch, the path doesn't change. What you've done instead is bifurcate the versions along another dimension. This makes simple cases very easy to visualize: there's just one tree. Of course, you've only shifted the complexity elsewhere. When you want to uniquely specify a version of an item, knowing a path and a revision number isn't enough anymore; you need to know the branch too. And what if you rename a file in one branch but not another? In TFS nobody would care until it was time to merge the branches; in CVS just viewing the repository raises the issue. I'm sure you can think of other subtleties -- I'm not familiar enough with it to know how it handles every edge case.

Most SCC systems lie somewhere between these extremes. Let's call TFS 2005/2008 the far "left" and CVS the opposite "right."

Subversion sits basically atop TFS at the "left" pole. While the implementations are very different, the user's view of branching is almost identical now that merge tracking is finally implemented. (One could argue that prior to v1.5, it was even a little bit farther left than TFS. Branches were simply copies with low-level optimizations; the user had no way to query for relational metadata. SourceSafe falls into this same category, if not even farther left due to lack of systemwide version numbers.) Users coming from the SVN world shouldn't have a hard time once they understand the client/server workspace model and rejigger their terminology. (SVN has a lot of CVS baggage in its terms, eg the word "tag"; in fairness TFS inherits some verbal crud from VSS, eg the pervasive use of "checkin/checkout" despite being edit-merge-commit by default.)

Perforce is one notch to the right of TFS. Their underlying model is identical. They have the additional concept of branch specifications which meet some common user scenarios -- eg quickly knowing "which folders represent branches", shortcuts for specifying branched versionspecs w/o needing the full path -- but it's just syntax sugar.

TFS 2010 lies another couple notches to the right. Like Perforce they've created a store of "branch objects" that exist independently from (but map back to) the repository tree. Each branch also knows its relationships (eg parent, child, baseless) within a user defined branch hierarchy.

I'd place ClearCase about 2/3 of the way to the right. Complex branching scenarios fundamentally happen in version space from the server's point of view. However they have an extremely powerful system of "views" layered on top. As a result, the structure the user actually sees might be manipulated to resemble path space, or some hybrid. A similar level of customizability applies to their local workspace mappings.

Most other "enterprise" SCMs are about 3/4 of the way to the right. (eg AccuRev, MKS, StarTeam) Users can typically view the repository + branch tree in a variety of powerful ways, but can't configure system itself as flexibly as CC. This is probably a good thing :)

CVS is on the far right as described earlier. Ditto its ancestors RCS and SCCS.

Classifying distributed systems like Monotone, BitKeeper, and their derivatives is beyond the scope of this answer :)


§A couple newer operations in TFS can be done without a workspace at all:

  • Creating a branch (tf branch /checkin - requires 2008 SP1)
  • Destroying items (requires 2008)

A few others require mappings into a workspace, but don't require you to download any files:

  • Deleting items
  • Locking items
  • Creating a branch the old way (tf branch /noget - available since 2005 early betas)

A few more require partial mappings and/or partial downloads:

  • Merge requires only the target path to be mapped & downloaded.
  • Rename requires both paths to be mapped and the target to be downloaded.
  • Undelete /newname works like Rename.

I prefer to do these kinds of operations in their own "maintenance" workspace, isolated from my day-to-day development. Having their own workspace also means I can map huge parts of the repository in one fell swoop without actually downloading them. (On the contrary, in your "development" workspaces it's a best practice to run Get w/o any restrictive path scopes.) And as I've mentioned a few times now keeping the local <-> server mapping broad & simple means relative paths stay constant, references don't break, files don't get accidentally committed or committed to the wrong place; everyone is generally happier.

YMMV.

like image 185
Richard Berg Avatar answered Oct 21 '22 18:10

Richard Berg


You could map the root folder to a location on your hard drive and not do a get latest version. Then in addition to the root being mapped you can map the sub folder to the location you want. When you do the branch just be sure to uncheck Create local working copies for the new branch.

$/Project -> C:\temp
$/Project/trunk -> C:\dev\projectname

When you switch the workspace bindings you have to manually do a get latest version. for the new folder and then TFS will update the old folder to not downloaded and the new folder to latest.

like image 29
Ryan Avatar answered Oct 21 '22 17:10

Ryan