Subversion: The Magic of Merging

When programming professionally, Subversion is a must-have. Same for system administration: it's quite a good idea to keep your configuration files (e.g in Linux the whole /etc/ directory) as a Subversion checkout.

So the goal of Subversion (or any other Source Control system) is to allow you to do something Apple will introduce with it's new Leopard operating system: Time Machine. Go back in time (and restore a version of a file as it was on day x).

Using Subversion on a daily basis is quite easy. Just check in (svn ci) your changes after you have completed a certain task. When you work collaboratively, and someone else has committed some changes, you do a svn up and the changes of the others are applied to your codebase.

That's all you basically need. But how can you go back in time now?

So you poke around a bit and find that svn up has a parameter -r which let's you put your checkout to the state in which it was at a certain revision.

Let's suppose we know that something was ok on monday and is not today. So let's use the command from above to see what it looks like.

~/project/trunk$ svn up -r {2006-10-09} app.php
U app.php

Voila, there it is. Now we choose to use that code now and throw away all changes that have been committed since. We modify the file a bit and do a check in:

~/project/trunk$ svn ci -m "revert to monday" app.php
Sending app.php
svn: Commit failed (details follow):
svn: Your file or directory 'app.php' is probably out-of-date
svn: The version resource does not correspond to the resource within the transaction. Either the requested version resource is out of date (needs to be updated), or the requested version resource is newer than the transaction root (restart the commit).

Uh.. ok. So you probably you know that error message already. It is also returned when you want to check something in on a file that has been changed by someone else since your last svn up.

When you check something into a subversion repository, one of the basic rules is that the file you want to commit is "up to date", i.e. the revision number of your local file (updated by svn up) equals the number in the repository (on the server).

Ok, so, let's update our checkout so we can re-run the check in.

~/project/trunk$ svn up
G app.php

So you discover the changes that happened since have been re-inserted to that file again. Maybe Subversion has alerted you of a conflict, because you changed some lines that have been modified since monday also.

Great! Basically we are back to where we started.

Let's not resign here, but rather use the appropriate command: svn merge. That command is mostly known for merging changes from one branch of development to another. But it can also help you to go back in time.

The parameters of svn merge are to specify a revision range, which changes to be merged, and a source — what part of the subversion repository should be searched for the changes.

Usually one would find this command used in a way like:

~/project/trunk$ svn merge -r 15:26 ../branches/first_release/
G app.php

So with two revisions specified you define a range of changes which should be merged into the current checkout. Ok so how would us help this here?

You can also specify revisions backwards, to go back in time. So to undo the command form before you can write:

~/project/trunk$ svn merge -r 26:15 ../branches/first_release/
G app.php

To put it simple, Subversion generates a diff file behind the scenes that incorporates the changes between the given revisions. Then the changes are merged with the files in the same way the patch command (Linux, Unix, OS X, …) does it. When going back in time, the parameter -R is used which applies the patch in the reverse direction. Voila.

So as a final solution this leaves us with:

~/project/trunk$ svn merge -r head:{2006-10-09} .
U app.php
~/project/trunk$ svn ci -m "revert to monday" app.php
Sending app.php
Transmitting file data .
Committed revision 27.

For further questions, the Subversion FAQ is a good starting point when you know exactly what you want (i.e. the correct terminology). (For example reverting does not mean to go back to a previous version of the file, but rather to remove the changes you did locally).

There is the subversion book (also published by O'Reilly), of which the Guided Tour is a good starting point.

The process I described above as a trial and error is also described in that book at Undoing changes.

Also OSCON: Subversion Best Practices, a transcript of a talk given by the subversion creators (Ben Collins-Sussman and Brian W. Fitzpatrick) by Brad Choate has some good tips.

Have fun :)

, , ,