Subversion and Shelving

NOTE: ''This is just a copy of a blog article I posted here.

In June 2005, I wrote an article on Subversion for my own blog about a process known as "shelving". It served as a good little primer to Subverion in general, as well as the process of shelving. As shelving is basically just branching, this also covers things like branching and merging under SVN.

Subversion has a somewhat different way of looking at the world than CVS. I've recreated the article here so that anyone unfamiliar with Subversion (be they CVS hold-outs of coders new to version control) can get a feel for some of the standard conventions used.

Shelving and Subversion in General
This post is primarily for my personal archival purposes, however, I post it publicly in the hope that some other people might benefit from a little look inside the development world of the labs.

Recently I've taken to creating my own personal "shelf" space in the fedws2 source tree where I can hack away at my changes while leaving Mick with a stable (well, stable-ish) trunk to work on. Normally, I'd just work on the head and tag stable releases, however, I do a fair amount of switching between various machines and committing unfinished, unstable code to the trunk just so I can get at it on another machine isn't what I would call ideal.

This isn't an uncommon problem and the notion of shelving code solves it really elegantly. Really, a shelf is nothing more than a personal branch of a repository. Rather than committing my unfinished code to the trunk of the tree, I create a shelf with the intention of it being for my private use. Internally, there is nothing about my shelf that is "private" and anyone can check it out or switch to it as their working copy. In this situation it is more of a social contract/understanding thing. My co-workers know that I'm doing stuff on this shelf and that I'm in no way bothered about leaving it in even a compile-able state, so it would be rather foolish to work on it. Given this, I can assume when working with the shelf that I have this little corner of the repository all to myself.

Now, those people who have taken a look at the Red-bean Subversion book (THE reference for Subversion) will know that they suggest a particular "layout" for subversion repository. This layout is designed to fit in with the way you use subversion and given when moving from CVS it does seem a little different at first. However, it becomes apparent rather quickly that it is the ideal way to structure a subversion repository. Given that I have spent a great number of hours structuring the build process and environment for the labs, I am a fan of anything which promotes a consistent, sensible, practical set of working surrounds and processes across all projects.

To boil it right down, it goes a little something like this:

Basic Subversion Repository Layout
It is no secret that internally Subversion functions a little differently to CVS. Given this, it should come as no surprise to learn that the notions of branching and tagging are different to the CVS world. Essentially, a branch and a tag (and for that matter, a private shelf) are just a copy of the repository at a certain point in time. It may sound a space inefficient to create an entire copy of a codebase simply to tag it, however, internally, subversion functions a little smarter than CVS and doesn't actually make a copy of the files. Following along these lines, the recommended file system structure for a project is as follows:

/ (project root) /trunk /branches /tags

The mainline of the codebase should reside in /trunk. Regardless of the workflow your development team follows, (stable head with new development on branches, unstable head with releases tagged or branched off near release time, etc...) /trunk is the mainline. When you create a branch or a tag you do a subversion copy of the trunk into a new place under the branches or tags directories.

For example, let's say that I want to branch from the trunk to do some patch work and test it before merging it back into the head. To create the new branch I would issue the following command (all on one line):

svn copy svn://svn.example.com/home/svn/project/trunk svn://svn.example.com/home/svn/project/branches/branch-0001

Obviously you might want to use a better name for the branch than "branch-0001" :P

Having created the new branch, you can check it out like so:

svn checkout svn://svn.example.com/home/svn/project/branches/branch-0001

OR, if you already have a working copy of the trunk (or anything other branch or tag) checked out you can just switch your workspace to the branch with:

svn switch svn://svn.example.com/home/svn/project/branches/branch-0001

The advantage of switching is that it will take with you all your uncommitted changes into your "new" working copy. If you had changed the file Main.java and realised that you were going to make a number of changes which you didn't want to do on the trunk, creating the new branch and switching to it would bring your updated version of Main.java with you (where you could safely commit it to the branch without having to worry about bringing the trunk into ill repute). From the point of switch onward you are working on the branch and not the trunk, so any commits you make are to the branch and will not effect the code on the trunk (or the people using it).

To create a tag you follow the exact same process. The only difference being that you could copy from the trunk into a new directory under the /tags directory.

Shelving with Subversion
The concept of shelving in subversion is no different to branching or tagging. To cope with the new requirement of areas we are defining as private, we create a new directory under the project root. Under this directory we would create another directory for each developer which might want to make use of shelves. After this, our structure will have changed to look as follows:

/ (project root) /trunk /branches /tags /shelves /shelves/tim /shelves/mick

Now, to create a new shelf (starting from the current trunk) we just issue a simple copy and switch (all on one line):

svn copy svn://svn.example.com/home/svn/project/trunk svn://svn.example.com/home/svn/project/shelves/tim/23june2005

svn switch svn://svn.example.com/home/svn/project/shelves/tim/23june2005

Naturally you can use whatever naming scheme you wish for identifying your shelves. Personally, I prefer the format used above, but that is a personal preference decision. Following the copy and switch you will now be working on your shelf. Feel free to commit code which is as unstable, error ridden, bug laden and all round hideous as you wish. Sure, Mick could come along and checkout or switch to my shelf thus exposing him to the embarrassing mess I'd left it in, but if he did it would be under the knowledge that it's possible what he sees there might turn him off coding forever.

Moving on, there are two things which you will most likely need to know when working on your shelf. The first is how to merge an updated trunk into your shelf so you can test your Pandora's Box with the latest work (obviously if that work is happening on a branch you will need to merge changes in from that branch and not the trunk) AND you will need to know how to merge the changes you made on your shelf back into the mainline.

Merging from trunk to shelf
Mick has been hard at work on the trunk of fedWS2 and I want to bring his changes into my shelf so I can test that my new stuff works with it before merging my shelf back into the mainline. What I want to do is merge the changes which happened on the trunk between the time I split off and started working on my shelf and now (or whatever revision of the trunk we are treating as the end point). After the merge, all these changes will have been brought into my working copy where I can test them and commit them to my shelf. Assuming that I created the shelf on revision 141, the merge command looks like this:

svn merge -r141:HEAD svn://svn.example.com/home/svn/project/trunk

This will merge all the changes which have occured on the trunk from revision 141 to the current revision into my working copy (which is of the shelf). If I just want to accept them as they are I can issue a commit right then and everything will be commited to my shelf.

Merging from shelf to trunk
I've finally completed work on my shelf to a point where it is ready to go back into the trunk. The process I prefer to take is pretty much exactly the same as that of merging trunk changes into my shelf. The process goes like this:

1. Commit all my changes to my shelf

svn commit -m "Preparing for merge with trunk"

2. Switch my working copy back to the trunk

svn switch svn://svn.example.com/home/svn/project/trunk

3. Merge all the changes on my shelf from when I created the shelf right up to now into my working copy (which is now the trunk, not the shelf)

svn merge -r141:HEAD svn://svn.example.com/home/svn/project/shelves/tim/23june2005

4. Having done this, I have merged all the changes I made to my shelf between revision 141 and the current HEAD revision into my working copy. If I am satisfied that everything is in order I can go ahead and commit my current copy to the trunk

svn commit -m "Merged changes from my shelf (tim:23june2005) into the trunk"

Conclusion
That's it! Quite simple really. How anyone could choose CVS over subversion is beyond me.

Finally, before I go, I will leave you with a couple of links to some great Subversion guides:

The Red-Bean Subversion Book

A Crash Course in Subversion Part 1

A Crash Course in Subversion Part 1