HOW-TO: svn repository upgrade procedure ========================================= $LastChangedDate$ PROBLEM: In revision 2093 [June 5, 2002], the underlying schema for subversion repositories (libsvn_fs) was changed. This means you can potentially see two different errors when accessing a repository: 1. if your client or server is r2093 or newer, you'll see this error when trying to open an "old" repository: Berkeley DB error while opening `copies' table 2. if your client or server is r2092 or younger, you'll see this error when trying to open a "new" repository: Malformed revision skeleton SOLUTION: Here's the procedure for upgrading your old repository to a new one. If your repository is very large, it may have quite a bit of "deltified" data in it; you might notice very slow checkout times on old revisions, and if so, you're probably a victim of inefficient undeltification. In these cases, we recommend running 'svnadmin undeltify' on every Nth revision... it will drastically speed up the repository export/import later on. If you plan to do this, we labeled relevant steps below as [OPTIONAL]. 1. Obtain a working copy of r2092. We made branch of this revision that contains with useful fixes to help with repository dumping: svn co http://svn.collab.net/repos/svn/branches/fs-convert-2092 Or, if you prefer, there should be a tarball of this branch available on the website (at http://subversion.tigris.org) 2. Build the branch statically (pass --disable-shared to ./configure), and then copy the large svnadmin-r2092 binary to a safe place. 3. [OPTIONAL] Run 'svnadmin-r2092 undeltify' on every Nth revision in your repository. A perl script at the bottom of this document can help with that. 4. Build the HEAD revision of svn. Find the latest svnadmin binary. 5. Create a brand new repository using the new fs schema: svnadmin create newrepos 6. Create a 'dump' of your repository using the *older* svnadmin binary like so: svnadmin-r2092 dump oldrepos > dumpfile Then load the dumpfile into the new repository with the new svnadmin binary. This effectively replays all of your commits: svnadmin load newrepos < dumpfile OR, if you're feeling saucy, you can do it all at once: svnadmin-r2092 dump oldrepos | svnadmin load newrepos Congratulations, you now have a newly upgraded repository. For starters, 'svn log' actually traces back through copy/rename history now. :-) ---------------------------------------------------------------------- Here's a perl script to undeltify every Nth revision in your repository. Run it like so: ./undeltify.pl REPOS-PATH [START-REV:END_REV] [INCREMENT] #!/usr/bin/perl use strict; ############################################################################# # SETUP my $svnadmin_cmd = '/home/cmpilato/bin/svnadmin-2092b'; # ############################################################################# sub do_usage { print "ERROR: usage: $0 REPOS [START-REV:END-REV] [INCREMENT]]\n\n"; exit; } sub do_undeltify # ($repos, $start_rev, $end_rev, $increment) { my $repos = shift @_; my $start_rev = shift @_; my $end_rev = shift @_; my $increment = shift @_; my $i = $start_rev; while (1) { print "--- Undeltifying revision $i..."; `$svnadmin_cmd undeltify $repos $i /`; print "done.\n"; if ($start_rev > $end_rev) { $i = $i - $increment; last if ($i < $end_rev); } else { $i = $i + $increment; last if ($i > $end_rev); } } } my $next_arg; my $repos; my $start_rev; my $end_rev; my $increment; my $youngest; # REPOS argument is required. $next_arg = shift @ARGV; if ($next_arg eq '') { &do_usage(); } # Use the REPOS argument to first figure out the youngest revision in # the repository. $repos = $next_arg; $youngest = `$svnadmin_cmd youngest $repos`; chomp $youngest; # Setup the default argument list, a backwards walk of all revisions # in the repository. $start_rev = $youngest - 1; $end_rev = 1; $increment = 1; # Parse the remaining arguments. $next_arg = shift @ARGV; if ($next_arg ne '') { if ($next_arg =~ /^(\d+)\:(\d+)$/) { $start_rev = $1; $end_rev = $2; $next_arg = shift @ARGV; if ($next_arg ne '') { if ($next_arg =~ /^\d+$/) { $increment = $next_arg; } else { &do_usage(); } } } elsif ($next_arg =~ /^\d+$/) { $increment = $next_arg; } else { &do_usage(); } } # Validate the input. if (($start_rev > $youngest) or ($end_rev > $youngest) or ($start_rev < 1) or ($end_rev < 1)) { print "ERROR: You've specified an invalid revision.\n"; print "ERROR: Valid revisions are those between 1 and $youngest.\n\n"; exit; } print "Undeltifying `$repos', revs $start_rev - $end_rev (by $increment).\n"; &do_undeltify ($repos, $start_rev, $end_rev, $increment); print "Finished. Happy computing!\n\n"; exit;