A combined Mercurial and Git PowerShell Prompt

A while ago I posted the code I use to create a custom PowerShell prompt to show the status of my Mercurial repositories. Since then I have also started using Git so I updated my prompt to work for both. I find this prompt very convenient since it quickly shows what branch you are in as well as what pending changes you have in your working directory.

Here is the updated code:

if (test-path function:\prompt) {
   $oldPrompt = ls function: | ? {$_.Name -eq "prompt"}
   remove-item -force function:\prompt
 }
 function prompt {
  function getGitStatus {
  $branch = git branch 2>&1
  if($branch.Exception -eq $null) {
    $status = "git"
    $branch | foreach {
      if ($_ -match "^\*(.*)"){
        $status += $matches[1]
      }
    }

    $untracked = 0
    $updates = 0
    $deletes = 0
    $addsIndex = 0
    $deletesIndex =0
    $updatesIndex = 0

    git status --porcelain | foreach {
      if($_ -match "^([\w?]|\s)([\w?])?\s*") {
        switch ($matches[1]) {
          "A" {$addsIndex++ }
          "M" {$updatesIndex++ }
          "D" {$deletesIndex++ }
          "?" {$untracked++ }
        }
        switch ($matches[2]) {
          "A" {$adds++ }
          "M" {$updates++ }
          "D" {$deletes++ }
         }
      }
    }
    $status += " a:" +$addsIndex
    if($adds -gt 0) {
      $status += "($adds)"
    }

    $status += " m:" +$updatesIndex
    if($updates -gt 0) {
      $status += "($updates)"
    }

    $status += " d:" +$deletesIndex
    if($deletes -gt 0) {
      $status += "($deletes)"
    }     

    $status += " ?:$untracked"
    return $status
  }
  else {
    return $false
  }
}

  function getHgStatus {
    $summary = hg summary 2>&1
    if($summary.Exception -eq $null) {
      $regex = "(?si)(parent:(?<parent>.*?)(\n|\r)+.*?)(branch:(?<branch>.*)\s)(commit:(?<commit>.*)\s)(update:(?<update>.*))";
      $summary = [System.String]::Join([System.Environment]::NewLine,$summary)
      $res = $summary -match $regex
      $status = "hg {0} c:{1}" -f $matches["branch"].Trim(), $matches["commit"].Trim()
      return $status
   }
   else {
    return $false
   }
  }

  $status = getGitStatus
  if(-not $status) {
      $status = getHgStatus
  }
  $host.ui.rawui.WindowTitle = (get-location).Path
  if($status) {
    write-host ($status) -NoNewLine -ForegroundColor Green
    write-host (">") -NoNewLine -ForegroundColor Green
  }
  else {
    & $oldPrompt
  }
  return " "
}

After adding this prompt to your profile when you are in a git repository you will see this:

This is more complex than the Mercurial prompt since git has the concept of a staging area. The number not in parentheses is the “staged” value and the number in parentheses is the unstaged value. So in the prompt above the repository has 1 modification staged and one unstaged.

When you are in a Mercurial repository you will see this:

hgPrompt

 

Launching the TortoiseHg log more conveniently from the command line

When working with Mercurial I usually perform most tasks from the command line but often I want to be able to visually explore the history of the whole repository or of a single file.  For this I find TortoiseHg’s repository explorer very useful.

image

It is easy to launch it from the command line by executing the “hgtk log” command. The annoying thing about this command is that you must specify the full path of a file to see it’s history.  I am lazy and don’t want to type the following in to see the history of a file

htgk log .\website\content\css\myfile.css

To make this quicker I wrote a PowerShell function called hlog. Just place the following function in your PowerShell profile:

function hlog ($file) {
  if (-not $file) {
    hgtk log
  }
  elseif(Test-Path -LiteralPath $file -ErrorAction SilentlyContinue) {
    hgtk log $file
  }
  else {
    $path = Split-Path $file
    $leaf = Split-Path $file -Leaf
    Get-ChildItem $path -include $leaf -recurse | Where-Object { hgtk log $_.FullName }
  }
}

With this you have much greater flexibility in how you launch the visual log.  You can…

Launch for the whole repository

hlog

Launch it using a full file path

hlog .\website\content\css\myfile.css

Launch it with just the file name
(This will launch the log for every file with the name “myfile.css” in your source tree)

hlog myfile.css

Launch for files matching wild cards
(This will match all css files two directories deep that start with the word my)

hlog *\*\my*.css