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