There is a very cool Subversion feature called svn:externals (or External Definitions), which is a very powerful feature that allows several projects to reuse common code. However, this can be a very dangerous feature, if you’re not careful… but we’ll get to that.
The svn:externals feature allows you to create a working copy consisting of multiple working copies from several repositories (or several locations within the same repository). Basically, it allows you to create something like a “symbolic link” (or “shortcut” in Windows). The end result is that a sub-folder in your working copy is actually in a different repository (or repository location) than it’s parent folder — you’ve effectively “mounted” a different repository location as the sub-folder in your working copy.
The way that you do this is to set the “svn:externals” property in the parent folder. This tells the Subversion client (such as TortoiseSVN) to treat the sub-folder as part of the working copy.
This is a very powerful feature that allows several projects to reuse common code. That sounds great, but there’s a big problem:
When you create a tag in subversion (such as when you create a release of your product), you’re not tagging the svn:externals repository — you’re tagging your working copy, which references the svn:externals repository.
So, if your svn:externals property does not specify the revision of the svn:externals repository, it will always float to the latest version when you do an update, including when you checkout or do updates on working copies of tags! In other words:
If you do a checkout of a tag, any svn:external sub-folders (without revision information) will update to the latest revision and not the latest revision at the time you created the tag! This means that you probably don’t have an exact copy of the code that you tagged.
The solution is to explicitly set the revision of the svn:externals property. For example, instead of setting the property as the following:
We should explicitly set the revision, like the following (note the “-r 21″ argument):
toolkit -r 21 http://svn.red-bean.com/repos/skin-maker
Allowing an svn:external to float to the latest revision may be is desirable for working in the trunk (when we’re working towards a release), however this is certainly not desirable in a tag (when we want a permanent record of what we released). There is really only one correct way to address this issue (that I can think of):
Make sure that your trunk specifies the revision of svn:externals before you create a tag. You can always unset the svn:externals revision in the trunk, after you create the tag.
Now, you might want to let svn:externals float to the latest revision when you’re doing development on the trunk, but this requires that you are diligent about setting the revision of svn:externals, before you create your tag. This is manual work (unless you have a script that does it for you) and might not get done by lazy developers (and we’re all a little lazy, aren’t we?).
How do we fix this problem, if we’ve have already created a tag that doesn’t specify svn:externals revisions? Easy: you can go into the tag and look at the log to determine the svn:externals revision at the time the tag was created. Then, set the svn:externals revision in the tag. However, this is considered “cheating”, since you’re not really supposed to commit changes to tags — that’s what branches are for. However, it’s a necessary fix for a bad tag.
On a side note, I wonder why TortoiseSVN or the command-line svn client don’t have a feature to automatically set the revisions of svn:externals during a tag operation — and, at least they could warn you if you try to create a tag that has svn:externals that don’t have revisions set.