Working around the lack of svn:externals support in SVKEdit
I am a big fan of the Subversion externals feature (also known as svn:externals). I use it to share code among my projects. It’s heavily used by many popular open source projects including, for example, Rails.
The trouble is, SVK, doesn’t support externals.
Nested checkouts
If you try to "fake" externals support by doing a nested checkout:
svk co //mirrors/project/trunk src
cd src
svk co //mirrors/other_project/trunk subdir
You’ll fail with an error message like this:
Overlapping checkout path is not supported (/Users/John/project/src); use 'svk checkout --detach' to remove it first.
Views
It’s been suggested on the mailing lists that SVK’s views feature might work but it’s also noted that they are a beta-quality feature right now (SVK 2.0) not recommended for production use.
I also am not entirely sure of how views would be used; I asked for clarification on svk-devel but it’s still not clear to me how views are supposed to work as substitutes for Subversion externals.
Non-nested checkouts
Changing nested paths to non-nested paths
This will definitely work but unfortunately requires a lot of busy-work to fix breakage caused by the changed relative paths. That is, everything that was formally in ./other_project/
will now be in ../other_project
or ../../other_project
. In the case of Xcode this can lead to a lot of broken and hard-to-fix file references.
See "Search and replace in multiple files with a Perl one-liner" for information that may help to ease the pain of such a change.
Using symbolic links
Another alternative is to remove the external and replace it with a symbolic link to the non-nested checkout; for example, in the case of WOCommon:
cd $SOURCE_ROOT
svk propdel svn:external .
ln -s ../WOCommon WOCommon
Along with this change I’m making a minor adjustment to my directory structure. When I originally managed my code using CVS each project lived in a folder like this:
project
project/cvs-files
project/non-cvs-files
Where the trunk or whatever branch was being worked on would be checked out into the cvs-files
subdirectory.
On migrating to Subversion this structure changed to:
project
project/svn-files
project/non-svn-files
Now that I have yet another migration I’m going to remove the SCM system’s name from the path so that I won’t ever have to make this kind of adjustment again:
project
project/src
project/extra
Command-line helper scripts
Without proper externals you miss out on the following functionality:
svk co
doesn’t automatically check out dependent modules the waysvn co
does.svk up
doesn’t automatically update dependent modules the waysvn up
does.svk st
doesn’t report on changes in dependent modules the waysvn st
does.
One way to work around this would be to write a set of helper scripts, checkout
, up
and st
(co
can’t be used as a name because there is already an RCS tool with that name), which would serve as a wrapper for svk
and add the desired "dependency" functionality.
checkout DEPOTPATH [PATH]
would invoke svk co DEPOTPATH [PATH]
and then check for any svk:externals
properties within the checked out path. Declared externals would then be checked out automatically at the appropriate location. up
would perform similarly, wrapping svk up
. st
would wrap svk st
.
Given the following svk:externals
property set on a symbolic link local_name
inside a working copy:
svk:externals : //mirrors/project/remote_name
The scripts would expect the symbolic link local_name
to reveal the real location where //mirrors/project/remote_name
should be checked out.
This system would require some setup (the symbolic links) but once established would at least afford a level of automation comparable to that provided by Subversion externals.
Shell tricks
It is possible to achieve some level of "automation" by employing some shell tricks.
For example, if you want to find and get status on all checked out instances of a particular external named WOCommon
on your system you could do:
svk co --list | grep WOCommon | awk '{print $2}'| xargs -L 1 svk st
To perform svk up
on all such externals you could do:
svk co --list | grep WOCommon | awk '{print $2}'| xargs -L 1 svk up
This is a brute force way of making sure that changes made to one external get propagated to all others.