Code Review with Git Patches and Outlook via PowerShell

In the spirit of “simplest thing that works,” my team has a rather low-fidelity approach to code reviews: patch files and e-mail. Nothing fancy, but we find it works rather well. It’s even easier thanks to git format-patch, which lets me easily generate a patch per commit, but I was never able to get send-email to work quite like I wanted. Instead, I whipped together a PowerShell script in a few minutes that does just the trick:

function patch ($ref = 'master..', $Message = '', [switch]$KeepFiles) {
  $patchPaths = $(git format-patch -C -o C:/Temp/Patches $ref)
  if($patchPaths) {
    $outlook = New-Object -ComObject Outlook.Application
    $mail = $outlook.CreateItem(0)
    [void]$mail.Recipients.Add('myteam@mycompany.com')
    $mail.Subject = "Review - $Message"
    $commits = $(git log -C --pretty=format:'%s' --reverse $ref) | foreach {  "<li>$_</li>" }
    $mail.HTMLBody = "<ol style=`"font: 11pt Calibri`">$commits</ol>"
    $patchPaths | foreach {
      [void]$mail.Attachments.Add($_)
      if(!$KeepFiles) { Remove-Item $_ }
    }
    $mail.Display()
  } else {
    Write-Warning 'Nothing to patch!'
  }
}

Usage:

Create patch of everything on current branch since master:

patch -m "Issue 123 - This is neat"

Create patch of last two commits, without message:

patch HEAD~2..

Create patch of everything except the current commit, with message:

patch master..HEAD~1 'Refactoring for Story 234'
Posted in Git, PowerShell. Tags: . Comments Off

Git-Achievements in PowerShell

Reading through Jason’s post on using Git-Achievements with msysGit, I couldn’t help but get it working with PowerShell. The result is a single PowerShell script added to my Git-Achievements repository, tagged here on the off chance I decided to upload my achievements.

To install posh-git-achievements…

  1. Fork my repository on GitHub (or if you have an existing repository, add me as a remote and pull)
  2. Clone your fork of the repository (into C:\Git\git-achievements, for this example)
  3. Open your PowerShell profile and add the following:
    Set-Alias git C:\Git\git-achievements\git-achievements.ps1
  4. “dot source” your profile to reload it in your current session (or just start a new session):
    . $PROFILE
  5. Check the install:
    git achievements --help

If all goes according to plan, this should unlock your first achievement.

Note that this will pass every git call through a few extra layers, including calls made for the posh-git prompt. But if you can tolerate the performance hit, it’s a rather fun way to expand your working knowledge of Git. Enjoy!

Posted in Git, PowerShell. Tags: . Comments Off

posh-git Release v0.2

I just tagged the v0.2 release of posh-git, which you can download here. This is the last release supporing msysgit 1.6.5 and 1.7.0. In this release…

PowerShell Module

Thanks to a contribution from David Muhundro, posh-git now exposes its functions through a module (.psm1). The module exposes a number of functions whose usage can be seen in the example profile.

Tab Expansion Updates

  • TortoiseGit commands: tgit <tab>
  • git-svn operations: git svn <tab>
  • Stash completion for git stash operations: show, apply, drop, pop, branch
  • Branch completion for git reset and git rebase
  • Completion of deleted files for git rm
  • For most commands, tab completion should now work if other command flags are in use. For example, git rebase -i <tab> works as expected.

Thanks to Jeremy Skinner and Mark Embling for their contributions to this release.

Next Steps

The most common complaint about posh-git is performance, which has already been addressed for the next release (available in my master branch). However, the fix requires taking a dependency on msysgit 1.7.1, which has not been officially released yet.  Still, it has been working fine for me.

Beyond that, we still need to address the first two items on my list from the last release…

  • Testing! I’d like to figure out a way to run some integration tests that verify a given repository state renders the expected prompt. If you have suggestions how to approach this, or know how other Git integration projects are tested, please let us know over at the Google Group.
  • Documentation! How to get started, what the project provides, etc.

If you have any other feature requests or find issues, please let us know.

Posted in PowerShell. Tags: , . Comments Off

Elegant Inline Debug Tracing

As much fun as it is to step through code with a debugger, I usually prefer to use System.Diagnostics.Debug and Trace with DebugView to see what’s happening in realtime. This is particularly handy to track intermediate results in higher-order functions that you might not be able to step into. However, it’s not always convenient to insert debugging statements amongst the composed expressions of F#, PowerShell or LINQ.

An alternative first came to mind while working in F#:

let dbg x = System.Diagnostics.Debug.WriteLine(x |> sprintf "%A"); x

(Read |> as “as next parameter to”.) We can then use this function anywhere to peek at a value, perhaps an intermediate list in this trivial example:

let data = [1..10]
           |> List.filter (fun i -> i%3 = 0) |> dbg
           |> List.map (fun i -> i*i)

Indeed [3; 6; 9] are traced as multiples of three. Not a particularly convincing example, but it should be pretty easy to imagine a more complex algorithm for which unintrusive tracing would be useful.

This works pretty well with F#’s |> operator to push values forward, but what about C#? Given my posting history, it shouldn’t be hard to guess where I’m going with this…

Extension Methods

So if |> is “as next parameter to”, the . of an extension method call might read “as first parameter to”. So we can implement a roughly equivalent function (sans F#’s nice deep-print formatter "%A") like so:

    public static T Debug<T>(this T value)
    {
        Debug.WriteLine(value);
        return value;
    }

    public static T Dbg<T>(this T value, string category)
    {
        Debug.WriteLine(value, category);
        return value;
    }

I find the optional label handy to keep different traces separate. Looking again, there’s an overload that accepts a category, so we’ll use that instead. So why might this be useful? Maybe we want to log the value assigned within an object initializer:

var q = new SPQuery() {
  Query = GetMyQuery().Debug("Query")
};

Rather than store the query string to a temporary variable or retrieve the property after it’s been set, we can just trace the value inline. Or consider a LINQ example:

var items = from SPListItem item in list.GetItems(q)
            let url = new SPFieldUrlValue(item["URL"] as string)
            where url.Url.Debug("URL").StartsWith(baseUrl, StringComparison.OrdinalIgnoreCase)
            select new
            {
                Title = item.Title.Debug("Title"),
                Description = url.Description,
            };

Here we log all URLs that pass through, even the ones excluded from the result by the predicate. This would be much harder to implement efficiently without inline logging.

This technique works great for simple objects with a useful ToString(), but what about more complex objects? As has often been the answer lately, we can use higher-order functions:

    public static T Dbg<T, R>(this T value, Func<T, R> selector)
    {
        Debug.WriteLine(selector(value));
        return value;
    }

    public static T Dbg<T, R>(this T value, string category, Func<T, R> selector)
    {
        Debug.WriteLine(selector(value), category);
        return value;
    }

Now we can provide a delegate to trace whatever we want without affecting the object itself. For example, we can easily trace a row count for the DataView being returned:

public DataView GetResults()
{
    var myTable = GetDataTable();
    // Process data...
    return myTable.DefaultView.Dbg("Result Count", v => v.Count);
}

I could go on, but you get the idea.

PowerShell Filter

Finally, we can implement similar functionality in PowerShell using a filter with an optional scriptblock parameter:

filter Debug([scriptblock] $sb = { $_ })
{
  [Diagnostics.Debug]::WriteLine((& $sb))
  $_
}

PS > 1..3 | Debug { $_*2 } | %{ $_*$_ }
1
4
9

Which traces 2, 4, 6, as expected.

Update 4/19/2009: Changed functions to use category overloads. And another point to consider: if the value being traced could be null, selector should be designed accordingly to avoid NullReferenceException. There’s nothing worse than bugs introduced by tracing or logging.

SharePoint+PowerShell Leak Workarounds

Zach Rosenfield recently posted about an obscure memory leak issue when working with PowerShell and the SharePoint object model. You can read his post for details but in summary:

  1. PowerShell spawns a new thread for each pipeline (essentially any batch of code that runs together).
  2. SharePoint objects should not be used across multiple threads due to mechanics of the unmanaged heap.
  3. SharePoint objects used across multiple pipelines result in unmanaged heap leaks until PowerShell closes or you run out of memory.

This PowerShell behavior is easy enough to verify:

PS 1> function Get-ThreadId { [Threading.Thread]::CurrentThread.ManagedThreadId }
PS 2> set-alias gt Get-ThreadId
PS 3> gt
6
PS 4> gt
4

So we need to ensure that our SharePoint objects are allocated, used and disposed within the same thread. How can we do this? Zach offers two suggestions, but there are actually several options:

Single Line

PS 5> gt; gt; gt
10
10
10
PS 6> gt; `
>> gt; `
>> gt
>>
3
3
3

Script

PS 7> @"
>> gt
>> gt
>> "@ > gt.ps1
>>
PS 8> .\gt.ps1
8
8

Function

PS 9> function gt2 {
>> gt
>> gt
>> }
>>
PS 10> gt2
4
4

Inline Script Block

PS 11> &{
>> gt
>> gt
>> }
>>
7
7

My Using Function

PS 12> using Microsoft.SharePoint
PS 13> gt; using ($s = [Microsoft.SharePoint.SPSite] 'http://moss') {
>> gt
>> $s.Url
>> gt
>> }
>>
5
5

http://moss

5

Cmdlet + Wrapper Class

Gary Lapointe has a set of PowerShell cmdlets that use wrapper objects to hide SPSite/SPWeb objects unless you specifically ask for them.

PS 14> $spi = Get-SPSite-gl 'http://moss'
PS 15> gt; using ($s = $spi.SPBase) { $s.Url; gt }
8

http://moss

8

The severity of this issue depends on your environment and the kinds of scripts you’re running, but in memory-sensitive and production environments these are definitely some techniques to keep in mind.

PowerShell Get-Type: Simplified Generics

PowerShell doesn’t include any syntactic sugar for dealing with generic objects, so a number of people have posted code snippets online showing how to create generic lists, dictionaries, etc. Interestingly, nobody has pointed out that the real problem isn’t creating the objects—New-Object does that without issue (for the most part)—but simply that it’s tedious to build correct closed type references for generics. This post attempts to address this core problem.

If we leverage the $args array, a trivial solution is quickly discovered:

function TypeOf-Generic ([type] $base) {
  $base.MakeGenericType($args)
}

Which is quite simple to use:

$intList = New-Object (TypeOf-Generic System.Collections.Generic.List int)

However, a bit more code yields a more complete solution:

function global:Get-Type (
    $type = $(throw "Please specify a type")
    )
{
  trap [System.Management.Automation.RuntimeException] { throw ($_.Exception.Message) }

  if($args -and $args.Count -gt 0)
  {
    $types = $args[0] -as [type[]]
    if(-not $types) { $types = [type[]] $args }

    if($genericType = [type] ($type + '`' + $types.Count))
    {
      $genericType.MakeGenericType($types)
    }
  }
  else
  {
    [type] $type
  }
}

Trapping RuntimeException allows us to repackage failed casts without the full ErrorRecord. We get a bit of usage flexibility by accepting our type arguments in either a single array or as the full $args array. With this function, all of the following will return valid types:

$a = Get-Type ([System.Collections.ArrayList])
$b = Get-Type System.Collections.Hashtable
$c = Get-Type System.Collections.Generic.List int
$d = Get-Type System.Collections.Generic.Dictionary int,string
$e = Get-Type System.Collections.Generic.Dictionary int $d

Corresponding to:

System.Collections.ArrayList
System.Collections.Hashtable
System.Collections.Generic.List`1[System.Int32]
System.Collections.Generic.Dictionary`2[System.Int32,System.String]
System.Collections.Generic.Dictionary`2[System.Int32,System.Collections.Generic.Dictionary`2[System.Int32,System.String]]

Having delegated the responsibility of resolving type references, we can update Lee Holmes‘s New-GenericObject:

function New-GenericObject(
    $type = $(throw "Please specify a type"),
    [object[]] $typeParameters = $null,
    [object[]] $constructorParameters = @()
    )
{
    $closedType = (Get-Type $type $typeParameters)
    ,[Activator]::CreateInstance($closedType, $constructorParameters)
}

I’ve included a few extra tweaks:

  1. I replaced [string] $typeName with [object] $type and changed $typeParameters from [string[]] to [object[]], specifically to accept [type] values if they are provided.
  2. If $typeParameters is $null, I assume we were passed a closed type; if not, Get-Type fails accordingly.
  3. $constructorParameters needs a default value to get around this exception:

    Exception calling “CreateInstance” with “2″ argument(s): “Ambiguous match found.”

Cross-Assembly Generics

A user on Stack Overflow found an interesting quirk when trying to instantiate a generic SortedDictionary in PowerShell. The problem is easy to reproduce:

PS 1> $sdt = Get-Type System.Collections.Generic.SortedDictionary string,string
PS 2> $sdt.FullName
System.Collections.Generic.SortedDictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToke
n=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
PS 3> $sd = New-Object $sdt
New-Object : Cannot find type [System.Collections.Generic.SortedDictionary`2[System.String,System.String]]: make sure t
he assembly containing this type is loaded.
At line:1 char:17
+ $sd = New-Object  <<<< $sdt

Yet the type works fine with New-GenericObject:

PS 4> $sd = New-GenericObject $sdt
PS 5> $sd.GetType().FullName
System.Collections.Generic.SortedDictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToke
n=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

As the answer on Stack Overflow points out, SortedDictionary and System.String live in different assemblies (System.dll and mscorlib.dll, respectively), which apparently confuses the PowerShell type converter. And because New-Object expects -typeName to be a string, it automatically casts our [type] object (which has the necessary assembly information) back to [string]:

PS 6> [string] $sdt
System.Collections.Generic.SortedDictionary`2[System.String,System.String]

If we pass in the type’s FullName instead, everything works as expected:

PS 7> $sd2 = New-Object $sdt.FullName
PS 8> $sd2.GetType().FullName
System.Collections.Generic.SortedDictionary`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToke
n=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]

Note that this behavior extends to all assemblies, not just within the System namespace:

PS 11> $l = New-Object (Get-Type System.Collections.Generic.List ([Microsoft.SharePoint.SPWeb]))
New-Object : Cannot find type [System.Collections.Generic.List`1[Microsoft.SharePoint.SPWeb]]: make sure the assembly c
ontaining this type is loaded.
At line:1 char:16
+ $l = New-Object  <<<< (Get-Type System.Collections.Generic.List ([Microsoft.SharePoint.SPWeb]))
PS 12> $l = New-Object (Get-Type System.Collections.Generic.List ([Microsoft.SharePoint.SPWeb])).FullName
PS 13> $l.GetType().FullName
System.Collections.Generic.List`1[[Microsoft.SharePoint.SPWeb, Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral,
 PublicKeyToken=71e9bce111e9429c]]

If someone on the PowerShell team reads this, it would be awesome if New-Object had a parameter set that accepted a [type] instead of a [string]. Thanks in advance!

PowerShell Coalesce and PowerShellASP Query String Parameters

I had fun initially with PowerShellASP, but its sealed classes prevented me from doing much useful with it in SharePoint. I really need to take a look at Andre de Cavaignac‘s PowerShellPages project, though the GPL license is a bit restrictive for my taste. Maybe I’ll just build my own…

Anyway, I actually had a practical use for PoShASP today, and in working with it I decided there needed to be a better way to handle QueryString values. The resulting function is fairly straight-forward, and supports typed and optional parameters:

<% QSParam w x,int,10 y,double z,zzz %>
w = <%= $w %>
x = <%= $x %>
y = <%= $y %>
z = <%= $z %>

With no query string, we get the following:

w =
x = 10
y =
z = zzz

However, if I use a query string like w=42&x=oops&y=3.14&z=pi, the valid values fall through:

w = 42
x = 10
y = 3.14
z = pi

Since ‘oops’ isn’t an [int], we get the default. But first…

PowerShell Coalesce

One of the most underused C# operators is the coalesce operator (??), which evaluates to the right-hand value if the left-hand value is null. PowerShell doesn’t include such an operator (to my knowledge), but we can certainly build a function to do the job. I found an implementation contributed anonymously in the comments here:

function Coalesce-Args {
    ([object[]]($args | ?{$_}) + ,$null)[0]
}

Which can be simplified to this:

function Coalesce-Args {
  (@($args | ?{$_}) + $null)[0]
}
Set-Alias ?? Coalesce-Args

The array subexpression @( ... ) always returns an array, to which we append $null in case the array is empty. Taking the first element yields the desired result. I also define an alias (??) for an easily remembered shorthand, though the usage is different from the binary C# operator. With our new function, my original QueryString pattern:

if( !($n = $Request.QueryString["n"] -as [int]) ) { $n = 10 }

Can be rewritten as:

$n = ?? ($Request.QueryString["n"] -as [int]) 10

QSParam Function

Now that we have Coalesce-Args, we can implement QSParam:

function QSParam {
  $QueryString = $Request.QueryString
  $args | ForEach-Object {
    $arg = @($_)
    $varName = $arg[0];
    $varType = $arg[1] -as [type];

    $qsValue = $QueryString[$varName]
    if ($varType -eq $null) {
      $varValue = Coalesce-Args $qsValue $arg[2] $arg[1]
    } else {
      $varValue = Coalesce-Args ($qsValue -as $varType) `
                                ($arg[2] -as $varType)
    }

    Set-Variable $varName $varValue -Scope script
  }
}

If  PowerShell can’t cast the second value as a type, we use it as a default value. If a third value is specified, that takes priority over the second value (which is how you can specify a default value that would otherwise be confused for a type: QSParam rhymesWithSquid,$null,guid). Finally, we assign the result to a script-scoped variable so we don’t have to dot source the function call.

And in case you don’t follow the PowerShell team blog (or missed the post), Channel 9′s Expert-to-Expert with Erik Meijer and Jeffrey Snover is definitely worth watching if you’re at all interested in the language internals and other geeky stuff. Jeffrey’s enthusiasm for the language is infectious.

Follow

Get every new post delivered to your Inbox.