Mon 7 Aug 2006
Continuing my “Extending SVK for fun and profit” series, I present the mergeproject macro, which builds upon the mymerge command I talked about last time.
As I mentioned in the mymerge article, I name my local and mirrored repositories //$project_name/local/ and //$project_name//main/, respectively. In addition, I follow the convention of giving my checkout paths equally imaginative names, like /home/collin/src/$project_name/.
When last we left our heros, I had managed to cut the command to sync my local repository to the mirrored repository down from a monstrous
svk sm -I //$project_name/local //$project_name/main
to a more lazy-coder-friendly
svk mm $project_name
That’s good, but we can go further.
Since all of my project checkouts follow the same naming conventions, and since most of my svk mm commands are issued from within the project’s checkout directory, there’s no reason for me to type $project_name each time. Some File::Spec incantations should be more than enough to figure this out for me.
After some digging around through SVK’s internals, I present you…mergeproject:
package SVK::Command::Mergeproject;
use strict;
use SVK::Version; our $VERSION = $SVK::VERSION;
use base qw( SVK::Command::Mymerge );
use SVK::Util qw(splitdir catdir);
use SVK::I18N qw(loc);
use Cwd;
sub parse_arg {
my $self = shift;
my @arg = @_;
return if @arg != 0;
my $pwd = Cwd::cwd();
my @dirs = splitdir($pwd);
for(my $i = 0; $i < @dirs; $i++) {
my $dir = catdir(@dirs[0..$i]);
# See if the directory is a valid checkout path
# If it's not, an error will be raised and $@ will be set.
# If the directory is a valid checkout path, pass only the
# directory name -- ie, not the full path -- up to
# Mymerge, which will handle the rest.
eval { $self->{xd}->find_repos_from_co($dir, 0) };
unless ($@) {
return $self->SUPER::parse_arg($dirs[$i]);
}
}
die loc(”Unable to find a checkout path while traversing %1n”,
$pwd);
}
We use Cwd::cwd() to grab the absolute path to the current directory, then use SVK::Util::splitdir() (SVK::Util autoloads all the useful bits of File::Spec for us) to break the path into individual directory names. We then iterate over the list of directory names, building up longer and longer paths with SVK::Util::catpath(). For example, given the current working directory of /home/collin/src/svnmock/trunk/, we’d look in the following succession of directories for SVK checkouts:
/ /home/ /home/collin/ /home/collin/src/ /home/collin/src/svnmock/ /home/collin/src/svnmock/trunk/
stopping once SVK::XD::find_repos_from_co() reports that we have indeed found one. (In the above example, we’d end up stopping at /home/collin/src/svnmock/, the first directory that SVK can map to a repository.) The second argument of 0 to find_repos_from_co() tells SVK that we’re only interested in whether the checkout maps to a repository.
Once we’ve found a valid checkout path, the last directory in the series (the one that actually holds the checkout) is assumed to be the project name and so is passed up to mymerge.
Let’s recap: we went from this:
svk sm -I //$project_name/local //$project_name/main
to this
svk mm $project_name
to now this (using the mp alias for mergeproject)
svk mp
Hooray, laziness!
If you’re interested in doing something similar, put this code in /usr/lib/perl5/site_perl/*/SVK/Commands/Mergeproject.pm or wherever your SVK command modules happen to be. If you want to use a shortcut (I use mp), you’ll need to add a line to the %alias hash in SVK::Command, something like “mp mergeproject”.