Jeroen

Perfection is achieved, not when there is nothing left to add, but when there is nothing left to take away.
Resharper 4.0 RC is available!

FINALLY!! Get it here:

http://www.jetbrains.com/resharper/beta/beta.html

 

Using Subversion in a VSS-only shop

Once upon a time, I was used to Visual SourceSafe and the way it handles concurrent versioning problems: if you need to edit a file, you lock it in the VSS 'database' (ahum), change it and finally check it back in. If someone else has already placed a lock, tough luck: you have to wait. When you're alone or in very small teams, that situation does not happen too often, so you can probably live with it. When your team grows however, you really run into trouble, with ugly 'dead-lock' situations: I have files that you need, you have files that I need, and we both can't check in without breaking the build. So you start implementing ways to circumvent the system, making files writable without checking out, with the risk of improper merging, possibly even undoing each other's changes when not paying enough attention. Been there, done that.

Then I learned about how subversion uses a completely different philosophy to tackle this problem, called 'copy-modify-merge' (sometimes also called 'edit-merge-commit'), as opposed to 'lock-modify-unlock'. If you haven't heard of this before, read the chapter about versioning models in the svn book.

A new world went open, the world of "version control done right".

  • Atomic commits ('change sets')
  • Log: 'svn log' has become part of my daily toolset. I consider the history of a project almost as important as it's current state in order to be able to fully understand the code. However, history in VSS is essentially file-oriented, not change-set oriented as in svn. This makes searching in the VSS history awkward and painful. As a result, people typically don't enter any check-in comments, making the version history even less accessible.
  • Blame (or Annotate): there is no such feature in VSS, except again a painful 'binary search' through the file's history (as opposed to a one-click operation with TortoiseSVN)
  • Easy branching (and merging!)
  • Concurrent checkout

But unfortunately, you don't always get to choose the version control system to be used. For my current assignment, VSS is a given. Obviously, as a new team member you can't simply come in and change the version control system they're used to working with for 5 years: such a conversion is a project on it's own.

So I started thinking: wouldn't it be nice to set up a local svn repository for my own use, so I can work in some isolation, and take advantage of at least some of the powerful svn functionality? 

Scope

First, let's elaborate a bit on what we're trying to do here: the idea is obviously not to replace VSS. Instead, I want a local svn repository, where I can leverage some subversion functionality. First and foremost, I wanted to get rid of the 'lock-modify-unlock' hassle, and second, I wanted a better view on the history, especially for my own changes and, as far as possible, also on the changes made by other team members.

So how could we implement this? Here is how I went along:

  • I set up a local subversion repository, in which I imported the sources from VSS 
  • I set up a 'VSS+SVN' working copy, which I use to synchronize changes back and forth between VSS and svn
  • Finally, I have a 'svn-only' working copy, in which I do my day-to-day development work

A note about local svn repositories

It's a little-known fact that you don't really need a subversion server to make use of subversion. Subversion repositories can be accessed in essentially three ways:

  • using the native svn:// (or svn+ssh) protocol through svnserve
  • using http(s):// through apache
  • using the file:/// protocol

The last option is not very well known, I guess since it's only useful in single-user environments, but since we're going to be the only physical user accessing this repo, that's good enough for our purposes. Of course, if you already have a server set up that you can use for this, that will do also, but I will be explaining the case where we use a simple file:/// repository. And what's more, the repository format is identical to a repository served through svnserve or apache. So if you would want to migrate later to a real server, you could simply copy the repository to your server and serve it from there.

Preparations  

Let me start with a little warning: if you're not really comfortable in using svn yet, then don't start with this immediately on a live project. Make sure you really understand what you're doing first. If you don't really understand everything in what follows, learn more about svn first and come back when you're ready. You don't want to accidently revert changes from your team members in VSS... However, if you're reasonably familiar with subversion, you should be just fine. So in what follows, I will assume that this is the case, and that you have your favourite subversion client already installed (for most people this will be either TortoiseSVN or the svn command line client, which you can download here or here). For the sake of this article, our project in VSS will be called MyProject (how original, yes I know).

Creating a repository 

The first step is to create a local repository for your project. If you're a command line person, you probably know how that works using svnadmin. With TortoiseSVN, simply create a root folder (e.g. C:\SvnRepository, right-click on it in Windows Explorer and select "create repository here").

The structure of the repository does not really matter at this point. For my own use, I typically set up a "single repository for multiple projects", and I first create a 'template' project with a trunk, tags and branches subfolder which I can then simply copy when I want to create a new project. But feel free to use any other repository layout you're comfortable with.

Importing the sources from VSS into SVN

Now we can import the sources from VSS into our local SVN repository. Start by checking out your (still empty) project trunk from SVN into a folder on your local system, e.g. C:\Projects\MyProject\VSS. Yes, that's right, VSS. Bear with me.

Now, use VSS to "Get Latest Version" the sources from VSS into that very same folder. 'Add' all relevant sources to svn. Make sure to exclude all VSS-specific files (*.scc, *.vspscc, *.vssscc, ...), and some other Visual Studio-specific files that are not needed (like *.csproj.user, *.suo, the bin and obj folders, ...). Review what you've added before committing the files to svn.

What we have now is a svn repository containing all sources of our project, and a combined vss+svn working copy which we're going to use to synchronize our local svn repository with the VSS database.

Setting up our 'real' working copy

Next to svn/vss working copy, we need one for our own developments, too. That's easy of course, simply perform an 'svn checkout' into a second working copy (e.g. C:\Projects\MyProject\SVN).

Synchronizing external changes (VSS -> SVN)

This is the easier part. To mirror the changes committed into VSS by any team member, you can use the following procedure: 

  • Do a recursive VSS 'Get Latest Version' in the VSS working copy. VSS will make all files in the svn+vss working copy read-only.
  • Since our working copy is both linked with VSS and SVN at the same time, any files changed in VSS (by other users) will be automatically picked up as 'modified' by svn. However, when files are added or deleted from VSS, we still need to communicate this to svn. With TortoiseSVN this is easy (if you don't care about renames/moves): just open the commit dialog and check the 'Select All'  checkbox at the bottom.
  • Commit, with a nice little comment of course (at least put something like 'update from VSS').

These steps can also be easily automated this in a batch script similar to the following (you need command line svn installed and in the path to make this work; also obviously you'll need to adjust some variables to adapt it to your own environment:

REM set some variables 
set SSDIR=path\to\VSS\ (folder containing srcsafe.ini)
set SS=path\to\SS.EXE

REM move to VSS+SVN working folder
pushd C:\Projects\MyProject\VSS

REM get latest version (recursively)
%SS% GET $/path/to/MyProject/In/VSS -R -O-

REM svn add new files, svn delete disappeared files
for /f "tokens=2" %%i in ( 'svn status ^| findstr /R "^\?"') do svn add %%i
for /f "tokens=2" %%i in ( 'svn status ^| findstr /R "^\!"') do svn del %%i

svn commit -m "got latest version (vss)"

popd

As a last step, we still need to update our 'real' (svn only) working copy. I keep this step manual, since I like to have control over when changes are actually copied over to my working copy; SVN will then merge the external changes, possibly marking any files which have been modified in VSS (by other users) and in our SVN working copy as 'conflicted'. First advantage of the system: you get to decide yourself when to merge external changes into your working copy (since VSS does a 'Get Latest Version' on check-out, you often need to do a full Get Latest Version of the project, which you may not yet be ready for).
 
Checking in your changes into VSS: bridging svn to vss

As said before, we use a 'svn-only' working copy to develop our features/bug fixes. Whenever a feature is ready to be committed, we can commit it to our local svn, but obviously we also have to make sure that we also check it properly into VSS. This is a little tricky, since here is where we need to bridge the gap between the svn 'copy-modify-merge' and vss 'lock-modify-unlock' philosophies.

In a subversion environment, this is a matter of doing an 'svn commit'; if that fails because of an out-of-date conflict, we need to do an 'svn update' first, then resolve conflicts (if any), build and test and try to commit again; repeat until the commit passes. With VSS however, we need to make sure we have all files checked out before we can start modifying them.

The steps needed to do the 'svn update' part in our VSS+SVN environment are described above (vss -> svn). But the 'svn commit' part is now to be replaced by a 'VSS check in'. To be able to do so, we first need to explicitly check out the needed files in VSS.

Now wat do we want to achieve here? We want to merge our change from our svn-only working copy into our vss+svn working copy, where we can check things into VSS.

  • Check out all files from VSS into our vss+svn working copy. VSS will get the latest version, which may cause a final 'vss2svn' synchronization step. For files that are locked by someone else, we're still blocked, however it's now easier for me to simply 'undo checkout' if needed.
  • Once we have all files checked out in VSS, we can do the commit from our svn-only working copy, and update our vss+svn working copy. This effectively merges our change into the VSS working copy.
  • Finally, we can check things into VSS.

Benefits 

Now this may seem quite some work, and it is to some extent. However, you do get some nice functionality in return:

  • first and foremost, you get rid of the checkout-modify-checkin burden
  • you can do your own developments in some isolation (of course it remains a best practice to integrate the work of your colleages early and often, but at least you're calling the shots now instead of VSS)
  • you get a much more powerful, changeset-based 'svn log', and 'svn annotate'
  • it becomes really easy to reverse previous change sets (impossible with VSS), or go back to a previous revision of the project (possible but awkward with VSS)
  • branching: e.g. you can create your own private feature branches, if you want

For example, we're currently finalizing a release, while the next release will be dedicated to upgrading to VS2008/.Net 2.0 (from VS2003/.Net 1.1). So we've created a branch in subversion to perform the necessary changes for the upgrade, while other team members can still work on finalizing the release. When the release is done, we will merge the changes from the feature branch back into VSS. 

jeroen

Posted: Apr 05 2008, 01:22 PM by jeroen | with no comments |
Filed under: ,