FAQ: Is shelving just a fancy word for branching?

I've had some feedback from various sources lately about shelving, and how some people are perceiving it. Some think we're just putting a new name on an old concept - that shelving is just a form of branching. Others seem to think we're just giving you a shared filesystem that risks obviating the point of using version control in the first place.

To me, shelving is really more of a mental shift than a new mechanic in version control, and we're trying to present a feature that supports that mindset as directly as possible. So, let me talk a little about how a user shelves and unshelves changes, what is stored in a shelveset, and what I think it all means. Hopefully at least the first two of those is interesting.

Just to clarify, we have a full branch/merge system, which creates and tracks merge history between the branch source and target (in either direction). Shelving is something different.

If I have one or more items with pending changes, I can *shelve* them with the shelve command. What happens when you shelve?

The command creates a shelveset on the server, which has the following data for each item:

  • The version the change is pended against
  • The type of change pended
  • Metadata required for changes like renames and undeletes
  • File contents, if the changes on the item include an edit.

The shelveset may also contain:

  • A comment
  • Checkin notes (based on the configured methology)
  • Work item actions (resolve, associate, etc. -- which actions are based on the configured methodology)

Second, if the user elects it, the shelve operation will UNDO changes on the items shelved (if the shelveset is successfully created, naturally). Again, this is OPTIONAL. You're free to shelve changes and keep on trucking.

Unshelving is basically the same thing in reverse. Each item in the shelveset is re-pended in the "target" workspace (more on that target in a moment). This means the workspace version is set to the version of the shelveset. However, we 'remember' enough data that undo will get you back to your workspace version rather than the shelved version (if different), which is handy. The contents and item metadata are updated as appropriate.

Users can view and unshelve shelvesets from any other user - normal permissions still apply to the items in the shelveset, however, so you won't be able to circumvent any read or change permissions, for example, via shelving. There's a specific permission which governs the ability to delete a shelveset for which you are not the owner. So, you can shelve from any workspace, and unshelve to any workspace (including the original workspace, naturally), barring any limitations on the unshelving end due to permissions.

If the user elects it, and has permission, the shelveset will be deleted if all items were unshelved successfully (meaning if not all items were unshelved, or if there were any failures, the shelveset will persist anyway).

In version 1, you cannot "modify" shelvesets per se. There's a user option to replace a shelveset when shelving, which tells the system to (wait for it) replace an existing shelveset with the same name, if one exists. In the ... 75% or so case, this is sufficient if you're (for example) massaging a shelveset that encapsulates a Change Request until it's ready. Now, if that shelveset is bouncing back and forth between users, it's not quite so simple, since you can only create shelvesets with yourself as the owner (even with AdminShelvesets permission). The idea of mutable shelvesets, and the obvious implications that it's just versioning a layer removed, has come up, and we're thinking about those issues. If you *want* that kind of capability, then the standard branch-and-promotion model is probably what you want anyway. Shelving is intended for more...not really "ad-hoc" usage, but at any rate not quite that formal of a usage model. We use it to manage changes which we can't check in yet - bugs or DCRs needing approval, or a buddy build, or a QA signoff, etc. The lightweight, accessible nature of shelvesets is a merit for this kind of use, it just "feels" easier to deal with than branches would for that kind of thing. Maybe you're working on a change, but want to do some heavy refactoring, and you want to get back to the state of your workspace if you mess the thing up and want to restart from "just" the original, basic changes. Creating, potentially sharing or using, and deleting a shelveset is easy. Managing a branch, all too often, isn't. Your typical shelveset has a lifespan measured in hours or days, not release cycles.

So, while version information is stored in the shelveset, the usage of shelvesets doesn’t really turn shelving into a poor-man's shared file system (if that was the goal, we've done a lousy job in designing to the requirement). It's a way to move specific *work in progress* out of the way, or to another user (and maybe back), and so on. It's typical that a shelveset would correspond to a future changeset, but there's no mandated 1:1 correlation here, obviously. They're intended to hold a small set of files (relative to the entire source tree, at least), not some alternate state of a whole branch or the whole tree. You use shelvesets for sharing to facilitate an action (a checkin) that needs multiple actors (a code reviewer, a buddy builder, a QA signatory, or the other half of a client/server feature change or bugfix, to name some common REAL examples). I've seen, or been a party to, each of these usage scenarios in our dogfooding deployment.

Shelving is intended to be quick and easy. Managing even your own branches just never comes across the same way, even with rich UI to walk you through it (and our VS client has it). That's partly because, on the heavy end, you DO need to be careful when performing a large merge of a lot of history, and you need lots of options as far as selecting what to merge, what not to merge, how to reconcile conflicting changes, and so on. Yes, by and large you can do with branching what we do with shelving. But for various specific uses, shelving turns out to be a much faster, easier way to go about it.

As always, feedback and questions are welcome. I'd be happy to describe or 'storyboard' some specific examples, if there's interest. I can even use some real examples - dogfooding is fun for that :)