I recently had some Subversion fun when I decided my organization needed to upgrade from Subversion 1.4 to 1.6. Since the release of 1.4 there have been monumental advances in disk storage and merging capabilities. We “branch for feature” off the trunk (which is also in development), and need to merge back into the trunk after the branch is released. Subversion 1.6 has what is called a “Reintegration Merge” which is used to prevent reflective merging indicative of this type of workflow.
I decided I did not want to simply upgrade the svn binaries and then update the repository since this would not get me the new storage optimizations. I opted instead to create a new 1.6 server and sync the old 1.4 repo to the new 1.6 repo using svnsync. This would allow me to keep all the revision history and revision properties, but it would also commit each revision into the new repo which would allow 1.6 to do its storage optimization.
The second part of my mission was to break off 3 projects into their own repository. Our code was all stored in one repository over on the 1.4 server, and I wanted to break it up and allow the different groups have their own repos.
The first thing I had to do was bring up a new Subversion stack on a new box. I chose CollabNet’s Subversion Edge which is a single-file download and very easy to install as a service. Once it was installed, I used the Admin Console to make a new repository called “old”. This new repo was to be a live mirror of the 1.4 repo until we were ready to flip the switch. To keep this mirror repo read-only, and still allow sync writes, I created a new user on both the old 1.4 server and the new 1.6 server called “svnsync”. I then used the hooks in the new repo to only allow writes by the “svnsync” user:
if [ "$USER" = "svnsync" ]; then exit 0; fi
echo "Only the svnsync user may commit new revisions" >&2
if [ "$USER" = "svnsync" ]; then exit 0; fi
echo "Only the svnsync user may change revision properties" >&2
Now that the new 1.6 repo was read-only by obscurity. I needed to let it to know it was to sync with the 1.4 repo.
svnsync initialize http://new/repo http://old/repo --source-username svnsync --source-password xxxxxxxx --sync-username svnsync --sync-password xxxxxxxx
Then - I started the sync:
svnsync synchronize http://new/repo --source-username svnsync --source-password xxxxxxxx --sync-username svnsync --sync-password xxxxxxxx
This took some time as we had more than 47K revisions. Each revision is read over the network and then committed to the new repo. 10 hours later....
I needed to make sure the new repo stayed in sync. I had two options: Add post-commit and post-revprop-change hooks to the 1.4 repo that would push changes to the 1.6 repo, or set up a cron job on the 1.6 host that would poll every minute. I choose the latter, mostly because I did not want to dirty the waters of the old repo. Since I had full access to the new host - this seemed the best course of action. I added the cron job:
* * * * * <host_user> /opt/csvn/bin/svnsync synchronize http://new/repo --source-username svnsync --source-password xxxxxxxx --sync-username svnsync --sync-password xxxxxxxx
Once the cron job was set up - I was able make changes to files in the old 1.4 repo - and see them show up in the new 1.6 read-only mirrior! Very cool. When we’re ready... we can now easily turn off the old one - and remove the read-only hooks from the new one. We're then in 1.6.
Now I needed to break out my 3 projects into their own repository. I again made a new repository on the new 1.6 server. But this time I could not use svncync. While 1.6 does allow a syncing of a single directory - it does not allow the syncing of 3 directories into the same repository. I decided to use svndump to dump the “old” repo to a file:
svnadmin dump /path/to/old > ./old.dump
I then used the power of dumpfilter to filter out the 3 projects I was interested in, and dump them into a file of their own. Because my 3 projects were relatively new - I decided I did not want 47K empty revisions in my new repository - and opted instead to skip the repository revisions that did not contain my projects. This still kept the revision history for my projects - but assigned new revision numbers starting at 0:
svndumpfilter include /project1 /project2 /project3 --drop-empty-revs --renumber-revs --preserve-revprops < ./old.dump > ./my_projects_only.dump
Once I had a file containing just my projects, it was as easy as using the load command to load the projects into the new repo.
svnadmin load /path/to/my/repo < ./my_projects_only.dump
If you renamed projects you will need to also include the old project names. The dumpfilter needs to know the parent revision of the projects you want - and if they were renamed - it needs to include the revision of the original name.
Because my projects were maven projects - after the load - I had to check out the projects and edit the scm tag in the each project's pom file (trunk,tags,branches) to reflect their new location. I then tested the mvn deploy and mvn release plugins. Everything worked as expected. Of course... I guess the real test will be a trying a reintegration merge - but that will be for another day. :-)