Mercurial + PowerShell

Thursday, January 28, 2010 – 9:30 PM

My last couple of posts have been about Mercurial. I keep discovering new things I like about it. Combining it with PowerShell also presents some exciting possibilities.

I have a couple of PowerShell scripts I’ve used for building stuff for a while. This one for instance.

$lastBuild = join-path $env:temp lastbuild.log

function b {
msbuild /l:"FileLogger,Microsoft.Build.Engine;logfile=$lastBuild" $args
}

function lb { notepad $lastBuild }

The advantage of this is that you can use the lb command to load the last build log into your favorite editor and browse or search it. The example above uses notepad but notepad2 is a better choice, especially if your log files are large. This beats scrolling up and down in the shell window looking for the root cause of build breaks etc.

So where does Mercurial come in? Turns out Mercurial’s repository cloning feature(s) mean you can create a “buddy build” to quickly test local commits before pushing them to the authoritative repository. 

How about buddy builds? What’s a buddy build?

A buddy build is a practice which I came across at Microsoft prior to the widespread adoption of Continuous Integration. Before checking in a developer would create a patch file and send it to another developer (their “buddy”) or a special machine set aside for the purpose. They, or their buddy, would then unpack the change on the other machine and make sure that their changes built and tests pass. Typically this caught issues like files which had not been added to the source tree but just happens to exist on the developer’s original machine.

You can do this for yourself in Mercurial by committing to the local repository and then cloning the entire repository locally to another folder and making sure it builds (and passes tests etc) there. This will catch missing or misnamed file errors.

I wouldn’t recommend doing this all the time but it’s a a useful technique if you’ve moved, renamed or changed lots of files.

The PowerShell script for this looks like…

function bb        
{
if ($args.Length -gt 1) { return "bb ([repository root])" }
if ($args.Length -eq 1) { $root = $args[0] }
else { $root = hg root }
if ([String]::IsNullOrEmpty($root)) { return "Error: No root provided!" }
if (!(test-path $root)) { return "Error: Repository $root not found!" }
if ($root.EndsWith("-buddy")) { return "Error: Folder $root is already a buddy build root!" }
echo "Respository: $root"
$buddyroot = "${root}-buddy"
echo "Buddy build repository: $buddyroot"

if (test-path $buddyroot\.hg) {
pushd "$buddyroot"
echo " Hg: hg pull --update"
hg pull --update
if (!$?) { return }
}
else {
echo " Hg: hg clone '$root' '$buddyroot'"
hg clone "$root" "$buddyroot"
if (!(test-path $buddyroot)) { return "Error: $buddyroot not created!"}
pushd "$buddyroot"
}
pushd src
b CI.msbuild /v:m
popd
popd
}

This script creates or updates a new repository in a peer folder with the “-buddy” postfix. It then runs a build file; src\CI.msbuild (which by convention is where all my projects have their CI build).

Yes, you probably can do this in Subversion or TFS by creating a second instance of a repository or workspace on your machine and then using patches or shelf sets to copy your changes across to it. Mercurial makes this a trivial exercise at it assumes developers will push changes between workspaces. Most of the above script is about parameter and error checking, it only executes two or three Mercurial commands.

Sorry, comments for this entry are closed at this time.