Yes, but it is not for the faint of heart. You have to use svnadmin dump
and svnadmin load
to recreate your repository.
If you choose to do this, the first step is to stop your users from committing, and make a backup of your repository. I also recommend walking through the steps on a copy of your repository; you can use rsync
to copy the entire repository directory into a temporary directory, and work from there.
For these instructions, I'm going to create a new repository in a temporary directory:
tmp, 502> svnadmin create example.repo
tmp, 503> svn co file://`pwd`/example.repo example
Checked out revision 0.
I created a file that contained four revisions, the 3rd of which needs to be deleted:
example, 536> svn log test.txt
------------------------------------------------------------------------
r4 | kgregory | 2011-04-06 08:46:59 -0400 (Wed, 06 Apr 2011) | 1 line
post-bad commit
------------------------------------------------------------------------
r3 | kgregory | 2011-04-06 08:46:42 -0400 (Wed, 06 Apr 2011) | 1 line
bad revision
------------------------------------------------------------------------
r2 | kgregory | 2011-04-06 08:46:28 -0400 (Wed, 06 Apr 2011) | 1 line
good revision
------------------------------------------------------------------------
r1 | kgregory | 2011-04-06 08:46:02 -0400 (Wed, 06 Apr 2011) | 1 line
initial revision
------------------------------------------------------------------------
So, we need to dump revisions both before and after the bad one, using the -r
flag to specify revision ranges. The --incremental
flag on the second dump is important, because it will tell the load command not to create a new file.
Note that I'm running these commands from the same directory that holds the repository.
svnadmin dump -r 1:2 example.repo/ > pred.svndump
* Dumped revision 1.
* Dumped revision 2.
tmp, 552> svnadmin dump -r 4:4 --incremental example.repo/ > succ.svndump
* Dumped revision 4.
Now move the original repository out of the way, and create an empty repository with the same name:
tmp, 540> mv example.repo example.repo.bak
tmp, 541> svnadmin create example.repo
And import the contents of the dump files.
tmp, 569> svnadmin --quiet load example.repo < pred.svndump
tmp, 570> svnadmin --quiet --ignore-uuid load example.repo < succ.svndump
Now tell everyone to delete their working directories and check out fresh. And you should see the following log:
example, 574> svn log test.txt
------------------------------------------------------------------------
r3 | kgregory | 2011-04-06 08:46:59 -0400 (Wed, 06 Apr 2011) | 1 line
post-bad commit
------------------------------------------------------------------------
r2 | kgregory | 2011-04-06 08:46:28 -0400 (Wed, 06 Apr 2011) | 1 line
good revision
------------------------------------------------------------------------
r1 | kgregory | 2011-04-06 08:46:02 -0400 (Wed, 06 Apr 2011) | 1 line
initial revision
------------------------------------------------------------------------
There is one HUGE caveat: this process assumes that there haven't been any commits to the file since the bad commit (ie, the successor dump only contains changes to other files).
If that's not the case, you can still delete the revision, but it's a lot more work. You need to create a new check-in, containing a clean copy of the file along with any other files that were changed with it while bad. Then create multiple dumpfiles, excluding any revisions that contained the bad file.
Finally: I strongly suggest several dry runs. As you can see from the history numbers in my examples, I screwed up a few times. As I said at the beginning, it's easy to copy a Subversion repository into a temporary directory. And when you do that, you can keep trying until you get it right, then just copy the fixed repository back into place.