Scott Hanselman

PowerShell, AnkhSVN and Subversion

June 30, '06 Comments [3] Posted in PowerShell | ASP.NET | Subversion | Tools
Sponsored By

I've been up to my ears in PowerShell (podcast) this last week at work. It continues to rock my world. I've been doing some automation work using Subversion not as source control but as a quasi-journaled file system.

There's a number of ways one could use Subversion from PowerShell, but I kind of want it as integrated as possible. I'm still trying out a few things. Here's what I've got so far...

Via SVN.exe

One could just use svn.exe, the command-line client. There's some obscure switches to remember, but Arild, the guy that wrote AnkhSVN (Subversion support within Visual Studio) has written a PowerShell tabcompletion script that gives one completion for svn.exe commands.

So you might type svn c[TAB] and get svn cat then [TAB] and get svn checkout, etc. Clever and cool but it just extends the command line tool with help.

Via TortoiseProc (the command line that comes with TortoiseSVN)

One could use the TortoiseProc without it being in the PATH with a script like this (stolen from Brad Wilson). This isn't that interesting of a script as the file could just be in the PATH, but it is an example of a decent pattern for calling tools that have discoverable paths (via the registry, etc) but aren't in the PATH proper.

if ($args.Length -lt 1) {
    write-host "usage: tsvn <command>"
    return
}

if ((test-path "HKLM:\Software\TortoiseSVN") -eq $false) {
 write-host -foregroundColor Red "Error: Could not find TortoiseProc.exe"
 return
}

$tortoiseKey = get-itemproperty "HKLM:\Software\TortoiseSVN"

if ($tortoiseKey -eq $null) {
 write-host -foregroundColor Red "Error: Could not find TortoiseProc.exe"
 return
}

$tortoise = $tortoiseKey.ProcPath

if ($tortoise -eq $null) {
 write-host -foregroundColor Red "Error: Could not find TortoiseProc.exe"
 return
}

$commandLine = '/command:' + $args[0] + ' /notempfile /path:"' + ((get-location).Path) + '"'
& $tortoise $commandLine

Use NSvn.Core directly, in-process, from PowerShell

The most attractive option to me was to find a way to talk to Subversion directly using something in-process. These last two options would require "shelling out" to non-PowerShell things. I was hoping to update Subversion from within ASP.NET for some Admin stuff, and while I could use System.EnterpriseServices to manage identity, I didn't want to have new processes firing up for a number of obvious reasons.

Deep in AnkhSVN is a great little .NET library called NSvn that did just want I needed. It doesn't appear to be distributed outside of Ankh, but I just took it from the latest install.  It's got no docs but Reflector to the rescue got me this PowerShell script that I called get-fromsvn.ps1.

param ([string]$svnurl       = $(read-host "Please specify the path to SVN"),
       [string]$svnlocalpath = $(read-host "Please specify the local path")
      )

if ([System.IO.Path]::IsPathRooted($svnlocalpath) -eq $false)
{
  throw "Please specific a local absolute path"
}

[System.Reflection.Assembly]::LoadFrom((join-Path $GLOBAL:someGlobalPath -childPath NSvn.Common.dll))
[System.Reflection.Assembly]::LoadFrom((join-Path $GLOBAL:someGlobalPath -childPath NSvn.Core.dll))

$PRIVATE:svnclient = new-object NSvn.Core.Client
$PRIVATE:svnclient.AuthBaton.Add( [NSvn.Core.AuthenticationProvider]::GetWindowsSimpleProvider() )
if ((test-Path $svnlocalpath) -eq $true )
{
  write-progress -status "Updating from $svnurl" -activity "Updating Working Copy"
  $PRIVATE:svnclient.Update($svnlocalpath, [NSvn.Core.Revision]::Head, $true)
}
else
{
  write-progress -status "Checkout from $svnurl" -activity "Updating Working Copy"
  $PRIVATE:svnclient.Checkout($svnurl, $svnlocalpath, [NSvn.Core.Revision]::Head, $true)
}

This script lets me checkout/update from Subversion happily. There's still some voodoo I need to work out like getting progress updates...

(How do you do a delegate from within PowerShell if a .NET Assembly needs a delegate with a certain signature? This library will call me back with status updates I'd like to broker to write-progress)

...but it gets 90% of what I need from an in-process call to Subversion from PowerShell. I know Arild Fines is not only the author of this library but a fan of PowerShell as well. Perhaps he'll help me either update this script, or better yet, write a PSSnapin to use Subversion as first-class cmdlets with progress and detailed logging. It'd be as useful, if not more so, than the SVN PSDriveProvider he's been messing with.

UPDATE: Arild Fines posted a solution to my question. I agree that the syntax for delegates is poo and I hope the PowerShell team takes that feedback. ;) The syntax for events, however, is quite elegant.

The addition of two lines gives me the write-progress and event notification support I wanted:

param ([string]$svnurl       = $(read-host "Please specify the path to SVN"),
       [string]$svnlocalpath = $(read-host "Please specify the local path")
      )

if ([System.IO.Path]::IsPathRooted($svnlocalpath) -eq $false)
{
  throw "Please specific a local absolute path"
}

[System.Reflection.Assembly]::LoadFrom((join-Path $GLOBAL:someGlobalPath -childPath NSvn.Common.dll))
[System.Reflection.Assembly]::LoadFrom((join-Path $GLOBAL:someGlobalPath -childPath NSvn.Core.dll))

$PRIVATE:svnclient = new-object NSvn.Core.Client

$PRIVATE:notificationcallback = [NSvn.Core.NotificationDelegate]{
        Write-Progress -status ("{0}: {1}" -f ($_.Action, $_.Path)) -activity "Updating Working Copy"
    }
    
$PRIVATE:svnclient.add_Notification($notificationcallback)   

$PRIVATE:svnclient.AuthBaton.Add( [NSvn.Core.AuthenticationProvider]::GetWindowsSimpleProvider() )
if ((test-Path $svnlocalpath) -eq $true )
{
  write-progress -status "Updating from $svnurl" -activity "Updating Working Copy"
  $PRIVATE:svnclient.Update($svnlocalpath, [NSvn.Core.Revision]::Head, $true)
}
else
{
  write-progress -status "Checkout from $svnurl" -activity "Updating Working Copy"
  $PRIVATE:svnclient.Checkout($svnurl, $svnlocalpath, [NSvn.Core.Revision]::Head, $true)
}

I hope more people in the community start digging into PowerShell and seeing how truly powerful it is to have .NET at the command line.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Friday, June 30, 2006 12:30:32 PM UTC
Hey Scott,

NSvn - if I read your article right, could I add this dll as a reference to a C# project and then call updates to a SVN repository from a .NET app?
Friday, June 30, 2006 3:41:46 PM UTC
Mark, sure.
Scott Hanselman
Monday, July 03, 2006 9:41:04 PM UTC
I posted an updated version of your script that handles the notification callbacks here: http://arildf.spaces.msn.com/blog/cns!E99F8B43533149B0!127.entry?_c11_blogpart_blogpart=blogview&_c=blogpart#permalink
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.