How can I display my current git branch name in my PowerShell prompt?
Asked Answered
P

12

105

Basically I'm after this but for PowerShell instead of bash.

I use git on windows through PowerShell. If possible, I'd like my current branch name to displayed as part of the command prompt.

Peg answered 17/8, 2009 at 12:35 Comment(3)
With Git 2.22 (Q2 2019), don't forget git branch --current.Seriatim
@Seriatim the command is git branch --show-current. Not sure if it was originally --current, but as of 2023 it is the former.Uprear
@JacquesMathieu I agree, and the answer I reference does mention --show-current indeed.Seriatim
T
85

An easier way would be just installing the Powershell module posh-git. It comes out of the box with the desired prompt:

The Prompt

PowerShell generates its prompt by executing a prompt function, if one exists. posh-git defines such a function in profile.example.ps1 that outputs the current working directory followed by an abbreviated git status:

C:\Users\Keith [master]>

By default, the status summary has the following format:

[{HEAD-name} +A ~B -C !D | +E ~F -G !H]

(For installing posh-git I suggest using psget)

If you don't have psget use the following command:

(new-object Net.WebClient).DownloadString("https://raw.githubusercontent.com/psget/psget/master/GetPsGet.ps1") | iex

To install posh-git use the command: Install-Module posh-git

To ensure posh-git loads for every shell, use the Add-PoshGitToProfile command.

Textile answered 11/4, 2012 at 13:28 Comment(8)
How do I enable this module on startup? Looks like powershell forgets this module every time I open a new instance.Jaquez
@Jaquez you have to create a profile, look at the answers #24915089Huddersfield
@NicolaPeluchetti Thanks! I followed howtogeek.com/50236/customizing-your-powershell-profile and just added Import-Module posh-git in my powershell profile. Worked like a charm!Jaquez
If you use chocolatey you can simply install posh-git using choco install poshgit from an elevated command prompt.Divaricate
GetPsGet.ps1 is now at "raw.githubusercontent.com/psget/psget/master/GetPsGet.ps1". The "psget.net/GetPsGet.ps1" URL doesn't seem to exist any more.Ana
I found it slow.. Loading personal and system profiles took 1065ms.Rochet
posh-git is super slow, I find it's doing far too much, I personally just want to know what branch I'm on and if my local copy has unstaged changes, anything else is just overkill that slows down my terminal.Steatite
I have installed but I don't see branch name in terminal (powershell)Signalment
P
64

Here's my take on it. I've edited the colours a bit to make it more readable.

Microsoft.PowerShell_profile.ps1

function Write-BranchName () {
    try {
        $branch = git rev-parse --abbrev-ref HEAD

        if ($branch -eq "HEAD") {
            # we're probably in detached HEAD state, so print the SHA
            $branch = git rev-parse --short HEAD
            Write-Host " ($branch)" -ForegroundColor "red"
        }
        else {
            # we're on an actual branch, so print it
            Write-Host " ($branch)" -ForegroundColor "blue"
        }
    } catch {
        # we'll end up here if we're in a newly initiated git repo
        Write-Host " (no branches yet)" -ForegroundColor "yellow"
    }
}

function prompt {
    $base = "PS "
    $path = "$($executionContext.SessionState.Path.CurrentLocation)"
    $userPrompt = "$('>' * ($nestedPromptLevel + 1)) "

    Write-Host "`n$base" -NoNewline

    if (Test-Path .git) {
        Write-Host $path -NoNewline -ForegroundColor "green"
        Write-BranchName
    }
    else {
        # we're not in a repo so don't bother displaying branch name/sha
        Write-Host $path -ForegroundColor "green"
    }

    return $userPrompt
}

Example 1:

enter image description here

Example 2:

enter image description here

Possing answered 7/6, 2017 at 11:18 Comment(8)
Neat! Just changed the colors for my background. What is your background color?Bubo
@YogeeshSeralathan I'm using the Monokai theme for ConEmu. The background colour is #272822Possing
this works great! would be cool if it can also show some indication that there are some uncommitted changes.Rochet
Test-Path is not recursive though and will return true only when we are in the root of git project - which not always the case.Beckford
For those who have no idea how to use this ps1 script, just copy and paste this into the Microsoft.PowerShell_profile.ps1 file, located here (for your local user): $UserHome\[My ]Documents\PowerShell` For me, because I have OneDrive integrated into my machine, it was located here: C:\Users\<USER>\OneDrive - \Documents\WindowsPowerShell`Hippo
this is basically all I want, only other thing I'd possible want is to know if there's any local changes and add a * like oh my zsh doesSteatite
Find out more about profiles here: learn.microsoft.com/en-us/powershell/scripting/… . Create your profile using one of the 4 options on that page. Then copy tamj0rd2's code. As has been said - it works out of the box.Broida
Awesome, thanks. I usually prefer a few hand-written commands that just do the job and that one can easily understand in favor of installing some external and potentially heavy-weight/slow/unkown-source plugins.Kinard
F
24

@Paul-

My PowerShell profile for Git is based off of a script I found here:

http://techblogging.wordpress.com/2008/10/12/displaying-git-branch-on-your-powershell-prompt/

I've modified it a bit to display the directory path and a bit of formatting. It also sets the path to my Git bin location since I use PortableGit.

# General variables
$pathToPortableGit = "D:\shared_tools\tools\PortableGit"
$scripts = "D:\shared_tools\scripts"

# Add Git executables to the mix.
[System.Environment]::SetEnvironmentVariable("PATH", $Env:Path + ";" + (Join-Path $pathToPortableGit "\bin") + ";" + $scripts, "Process")

# Setup Home so that Git doesn't freak out.
[System.Environment]::SetEnvironmentVariable("HOME", (Join-Path $Env:HomeDrive $Env:HomePath), "Process")

$Global:CurrentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$UserType = "User"
$CurrentUser.Groups | foreach { 
    if ($_.value -eq "S-1-5-32-544") {
        $UserType = "Admin" } 
    }

function prompt {
     # Fun stuff if using the standard PowerShell prompt; not useful for Console2.
     # This, and the variables above, could be commented out.
     if($UserType -eq "Admin") {
       $host.UI.RawUI.WindowTitle = "" + $(get-location) + " : Admin"
       $host.UI.RawUI.ForegroundColor = "white"
      }
     else {
       $host.ui.rawui.WindowTitle = $(get-location)
     }

    Write-Host("")
    $status_string = ""
    $symbolicref = git symbolic-ref HEAD
    if($symbolicref -ne $NULL) {
        $status_string += "GIT [" + $symbolicref.substring($symbolicref.LastIndexOf("/") +1) + "] "

        $differences = (git diff-index --name-status HEAD)
        $git_update_count = [regex]::matches($differences, "M`t").count
        $git_create_count = [regex]::matches($differences, "A`t").count
        $git_delete_count = [regex]::matches($differences, "D`t").count

        $status_string += "c:" + $git_create_count + " u:" + $git_update_count + " d:" + $git_delete_count + " | "
    }
    else {
        $status_string = "PS "
    }

    if ($status_string.StartsWith("GIT")) {
        Write-Host ($status_string + $(get-location) + ">") -nonewline -foregroundcolor yellow
    }
    else {
        Write-Host ($status_string + $(get-location) + ">") -nonewline -foregroundcolor green
    }
    return " "
 }

So far, this has worked really well. While in a repo, the prompt happily looks like:

GIT [master] c:0 u:1 d:0 | J:\Projects\forks\fluent-nhibernate>

*NOTE: Updated with suggestions from Jakub Narębski.

  • Removed git branch/git status calls.
  • Addressed an issue where 'git config --global' would - fail because $HOME was not set.
  • Addressed an issue where browsing to a directory that didn't have the .git directory would cause the formatting to revert to the PS prompt.
Forint answered 17/8, 2009 at 13:15 Comment(3)
Cheers David, I ewas easily able to modify this and use the instructions from the linked blogged post to get something up and running that suits me.Peg
Do not scrape git-branch output to get name of current branch; it is meant for end user (it is porcelain). Use git symbolic-ref HEAD. Do not use git-status; it is meant for end user and is subject to change (it would change in 1.7.0). Use git-diff-files, git-diff-tree, git-diff-index.Limbert
#Jakub Narębski - Updated the code based on your suggestions--thanks much. Since it isn't calling the branch and status, it's also quite a bit faster.Forint
R
10

posh-git is slow, there's a better way with https://ohmyposh.dev/.

enter image description here

  1. Run this command from powershell to install ohmyposh module:
Install-Module oh-my-posh -Scope CurrentUser -AllowPrerelease
  1. install font that supports glyphs (icons) from https://www.nerdfonts.com/.
    I like Meslo LGM NF.

  2. set that font in powershell defaults settings:

enter image description here

  1. Open/create file Microsoft.PowerShell_profile.ps1 at C:\Program Files\PowerShell\7 and write below to set theme (same as screenshot):
Set-PoshPrompt -Theme aliens

You can choose other theme also. see preview by running Get-PoshThemes

Now open powershell at location containing git repo and you'll see the status.


See more: Power up your PowerShell

Rochet answered 8/1, 2021 at 15:56 Comment(0)
S
9

With Git 2.22 (Q2 2019), any script (Powershell or not) could use the new --show-current option.

$branch = git branch --show-current

If empty, it means "detached HEAD".

Seriatim answered 27/9, 2019 at 16:3 Comment(1)
@KarenGoh It should, unless you are in a detached HEAD mode. What version of Git are you using?Seriatim
R
3

I tweaked the prompt code (from @david-longnecker answer) to be a bit more colorful.

Edit: June 2019 - Updated to show untracked, stashes, renames. Tweaked visual to show index.

Why I use this (over posh-git etc):

  • Learning: Learn stuff about git/SCM concepts when as I tweak
    • End up fixing problems solved by posh-git, but I learn more through this
  • Cross Platform: Have same prompt in POSIX shells, code is almost identical.
  • Flexible: I've tweaked my Prompt to show untracked/stashes/index/renames.
  • Lightweight & Portable: No fetching external modules (this is minor benefit, but nice)

The powershell code:

NOTE: Some commands used are porcelain (not recommended for scripting/parsing, e.g. git status). Will eventually migrate to plumbing commands, but this works for now.

Function Prompt {

$SYMBOL_GIT_BRANCH='⑂'
$SYMBOL_GIT_MODIFIED='*'
$SYMBOL_GIT_PUSH='↑'
$SYMBOL_GIT_PULL='↓'

if (git rev-parse --git-dir 2> $null) {

  $symbolicref = $(git symbolic-ref --short HEAD 2>$NULL)

  if ($symbolicref) {#For branches append symbol
    $branch = $symbolicref.substring($symbolicref.LastIndexOf("/") +1)
    $branchText=$SYMBOL_GIT_BRANCH + ' ' + $branch
  } else {#otherwise use tag/SHA
      $symbolicref=$(git describe --tags --always 2>$NULL)
      $branch=$symbolicref
      $branchText=$symbolicref
  }

} else {$symbolicref = $NULL}


if ($symbolicref -ne $NULL) {
  # Tweak:
  # When WSL and Powershell terminals concurrently viewing same repo
  # Stops from showing CRLF/LF differences as updates
  git status > $NULL

  #Do git fetch if no changes in last 10 minutes
  # Last Reflog: Last time upstream was updated
  # Last Fetch: Last time fetch/pull was ATTEMPTED
  # Between the two can identify when last updated or attempted a fetch.
  $MaxFetchSeconds = 600
  $upstream = $(git rev-parse --abbrev-ref "@{upstream}")
  $lastreflog = $(git reflog show --date=iso $upstream -n1)
  if ($lastreflog -eq $NULL) {
    $lastreflog = (Get-Date).AddSeconds(-$MaxFetchSeconds)
  }
  else {
    $lastreflog = [datetime]$($lastreflog | %{ [Regex]::Matches($_, "{(.*)}") }).groups[1].Value
  }
  $gitdir = $(git rev-parse --git-dir)
  $TimeSinceReflog = (New-TimeSpan -Start $lastreflog).TotalSeconds
  if (Test-Path $gitdir/FETCH_HEAD) {
    $lastfetch =  (Get-Item $gitdir/FETCH_HEAD).LastWriteTime
    $TimeSinceFetch = (New-TimeSpan -Start $lastfetch).TotalSeconds
  } else {
    $TimeSinceFetch = $MaxFetchSeconds + 1
  }
  #Write-Host "Time since last reflog: $TimeSinceReflog"
  #Write-Host "Time since last fetch: $TimeSinceFetch"
  if (($TimeSinceReflog -gt $MaxFetchSeconds) -AND ($TimeSinceFetch -gt $MaxFetchSeconds)) {
    git fetch --all | Out-Null
  }

  #Identify stashes
  $stashes = $(git stash list 2>$NULL)
  if ($stashes -ne $NULL) {
    $git_stashes_count=($stashes | Measure-Object -Line).Lines
  }
  else {$git_stashes_count=0}

  #Identify how many commits ahead and behind we are
  #by reading first two lines of `git status`
  #Identify how many untracked files (matching `?? `)
  $marks=$NULL
  (git status --porcelain --branch 2>$NULL) | ForEach-Object {

      If ($_ -match '^##') {
        If ($_ -match 'ahead\ ([0-9]+)') {$git_ahead_count=[int]$Matches[1]}
        If ($_ -match 'behind\ ([0-9]+)') {$git_behind_count=[int]$Matches[1]}
      }
      #Identify Added/UnTracked files
      elseIf ($_ -match '^A\s\s') {
        $git_index_added_count++
      }
      elseIf ($_ -match '^\?\?\ ') {
        $git_untracked_count++
      }

      #Identify Modified files
      elseIf ($_ -match '^MM\s') {
        $git_index_modified_count++
        $git_modified_count++
      }
      elseIf ($_ -match '^M\s\s') {
        $git_index_modified_count++
      }
      elseIf ($_ -match '^\sM\s') {
        $git_modified_count++
      }

      #Identify Renamed files
      elseIf ($_ -match '^R\s\s') {
        $git_index_renamed_count++
      }

      #Identify Deleted files
      elseIf ($_ -match '^D\s\s') {
        $git_index_deleted_count++
      }
      elseIf ($_ -match '^\sD\s') {
        $git_deleted_count++
      }

  }
  $branchText+="$marks"

}

if (test-path variable:/PSDebugContext) {
  Write-Host '[DBG]: ' -nonewline -foregroundcolor Yellow
}

Write-Host "PS " -nonewline -foregroundcolor White
Write-Host $($executionContext.SessionState.Path.CurrentLocation) -nonewline -foregroundcolor White

if ($symbolicref -ne $NULL) {
  Write-Host (" [ ") -nonewline -foregroundcolor Magenta

  #Output the branch in prettier colors
  If ($branch -eq "master") {
    Write-Host ($branchText) -nonewline -foregroundcolor White
  }
  else {Write-Host $branchText -nonewline -foregroundcolor Red}

  #Output commits ahead/behind, in pretty colors
  If ($git_ahead_count -gt 0) {
    Write-Host (" $SYMBOL_GIT_PUSH") -nonewline -foregroundcolor White
    Write-Host ($git_ahead_count) -nonewline -foregroundcolor Green
  }
  If ($git_behind_count -gt 0) {
    Write-Host (" $SYMBOL_GIT_PULL") -nonewline -foregroundcolor White
    Write-Host ($git_behind_count) -nonewline -foregroundcolor Yellow
  }

  #Output staged changes count, if any, in pretty colors
  If ($git_index_added_count -gt 0) {
    Write-Host (" Ai:") -nonewline -foregroundcolor White
    Write-Host ($git_index_added_count) -nonewline -foregroundcolor Green
  }

  If ($git_index_renamed_count -gt 0) {
    Write-Host (" Ri:") -nonewline -foregroundcolor White
    Write-Host ($git_index_renamed_count) -nonewline -foregroundcolor DarkGreen
  }

  If ($git_index_modified_count -gt 0) {
    Write-Host (" Mi:") -nonewline -foregroundcolor White
    Write-Host ($git_index_modified_count) -nonewline -foregroundcolor Yellow
  }

  If ($git_index_deleted_count -gt 0) {
    Write-Host (" Di:") -nonewline -foregroundcolor White
    Write-Host ($git_index_deleted_count) -nonewline -foregroundcolor Red
  }

  #Output unstaged changes count, if any, in pretty colors
  If (($git_index_added_count) -OR ($git_index_modified_count) -OR ($git_index_deleted_count)) {
    If (($git_modified_count -gt 0) -OR ($git_deleted_count -gt 0))  {
      Write-Host (" |") -nonewline -foregroundcolor White
    }
  }

  If ($git_modified_count -gt 0) {
    Write-Host (" M:") -nonewline -foregroundcolor White
    Write-Host ($git_modified_count) -nonewline -foregroundcolor Yellow
  }

  If ($git_deleted_count -gt 0) {
    Write-Host (" D:") -nonewline -foregroundcolor White
    Write-Host ($git_deleted_count) -nonewline -foregroundcolor Red
  }

  If (($git_untracked_count -gt 0) -OR ($git_stashes_count -gt 0))  {
    Write-Host (" |") -nonewline -foregroundcolor White
  }

  If ($git_untracked_count -gt 0)  {
    Write-Host (" untracked:") -nonewline -foregroundcolor White
    Write-Host ($git_untracked_count) -nonewline -foregroundcolor Red
  }

  If ($git_stashes_count -gt 0)  {
    Write-Host (" stashes:") -nonewline -foregroundcolor White
    Write-Host ($git_stashes_count) -nonewline -foregroundcolor Yellow
  }

  Write-Host (" ]") -nonewline -foregroundcolor Magenta

}

$(Write-Host $('>' * ($nestedPromptLevel + 1)) -nonewline -foregroundcolor White)



return " "}#Powershell requires a return, otherwise defaults to factory prompt

The result (VSCode, Using Powershell terminal): Powershell Git Prompt in action

Here are commands from result to view what it would look like:

mkdir "c:\git\newrepo" | Out-Null
cd "c:\git\newrepo"
git init
"test" >> ".gitignore"
"test" >> ".gitignore2"
git add -A
git commit -m "test commit" | Out-Null
"test" >> ".gitignore1"
git add -A
"test1" >> ".gitignore2"
git rm .gitignore
git add -A
git commit -m "test commit2" | Out-Null
git checkout -b "newfeature1"
"test" >> ".test"
mv .gitignore1 .gitignore3
git add -A
git stash
git checkout "master"
cd c:\git\test #Just a sample repo had that was ahead 1 commit
#Remove-Item "c:\git\newrepo" -Recurse -Force #Cleanup
Rabies answered 8/3, 2017 at 21:15 Comment(2)
Definitely looks good.... I'm questioning whether or not I can live with a delay of over a second everytime I hit enter before the new prompt appears....Stockholm
@Stockholm Depending on the repo size, the various git commands could take a while to process. Anecdotally, this is "fast" for me - maybe a fraction of second delay entering one of my git repos. Is there a better way (that maintains feature set)? I maintain this in a dotfiles repo and have matching prompts on POSIX based shells - it has been helpful for consistency and more importantly learning and is easyish to read and understand (and tweak). Lastly, this prompt include untradtitional but nice feature of checking for upstream changes - I'll take a small delay if it keeps me aware of status.Rabies
R
2

I like the accepted answer so I will elaborate on the steps to set it up. You can install PoshGit using Chocolatey or using the PowerShellGet command which is available for the new Core PowerShell.

For Chocolatey, you need to have it already installed before proceeding. In an administrator/elevated shell execute the command:

choco install poshgit

For Core PowerShell, installation is also required. To install Core PowerShell, execute the following command:

dotnet tool install --global PowerShell

NB: You need to have .NET Core SDK installed (latest version preferably)

After installing Core PowerShell, execute the command below to install PoshGit:

PowerShellGet\Install-Module posh-git -Scope CurrentUser -AllowPrerelease -Force

Using PoshGit requires you import it to the currently running shell environment using the command Import-Module posh-git. This means you have to run this command every time you open a new shell.

If you want PoshGit to be always available you should execute this command instead:

Add-PoshGitToProfile

Please note that PowerShell and Core PowerShell are different and as a result they run on different profiles. What this means is that if you want PoshGit to work for either of the shells you need to execute those commands in their respective shell environment.

Rabat answered 21/2, 2020 at 4:4 Comment(0)
N
2

I adapted @tamj0rd2 answer to further display the branch name in sub folders of the repository as well.

function Write-BranchName () {
    try {
        $branch = git rev-parse --abbrev-ref HEAD

        if ($branch -eq "HEAD") {
            # we're probably in detached HEAD state, so print the SHA
            $branch = git rev-parse --short HEAD
            Write-Host " ($branch)" -ForegroundColor "red"
        }
        else {
            # we're on an actual branch, so print it
            Write-Host " ($branch)" -ForegroundColor "blue"
        }
    } catch {
        # we'll end up here if we're in a newly initiated git repo
        Write-Host " (no branches yet)" -ForegroundColor "yellow"
    }
}

function prompt {
    $base = "PS "
    $path = "$($executionContext.SessionState.Path.CurrentLocation)"
    $userPrompt = "$('>' * ($nestedPromptLevel + 1)) "

    Write-Host "`n$base" -NoNewline

    if (! (git -C . rev-parse) ) {
        Write-Host $path -NoNewline -ForegroundColor "green"
        Write-BranchName
    }
    else {
        # we're not in a repo so don't bother displaying branch name/sha
        Write-Host $path -ForegroundColor "green"
    }

    return $userPrompt
}

See the diff:

- if (Test-Path .git) {
+ if (! (git -C . rev-parse) ) {
Nogging answered 14/12, 2022 at 7:4 Comment(0)
T
1

From @tamj0rd2's answer we can get the branch name to a string variable like this.

$branch = git rev-parse --abbrev-ref HEAD
echo $branch
Thermogenesis answered 27/9, 2019 at 11:11 Comment(0)
P
1

Here is my configuration for PowerShell Core. just copy the function below and put it in your $PROFILE

function prompt {
  try {
    $GitBranch = git rev-parse --abbrev-ref HEAD
    # we're probably in detached HEAD state, so print the SHA
    if ($GitBranch -eq "HEAD") { $GitBranch = git rev-parse --short HEAD }
  } catch {}

  if ($GitBranch) { $GitBranch = " `e[33;93m[`e[33;96m$GitBranch`e[33;93m]`e[0m" }

  "PS $pwd$GitBranch> "
}
Pilsen answered 8/4, 2021 at 23:3 Comment(0)
P
0

I used a similar method to the one above from @tamj0rd2, but less complex.

Open: Microsoft.PowerShell_profile.ps1

Add First:

function Get-GitBranch {
    if (Test-Path .git) {
        $branch = git rev-parse --abbrev-ref HEAD
        " [$branch]"
    }
}

Add Second:

function prompt {
    $p = Split-Path -leaf -path (Get-Location)
    Write-Host "$p" -NoNewline
    Write-Host (Get-GitBranch) -NoNewline -ForegroundColor Yellow
    Write-Host "> " -NoNewline
    return " "
}

The result is as below: enter image description here

How I achieved the reduced string or directory path is by using the split-path, so in total the prompt is:

function prompt {
    $p = Split-Path -leaf -path (Get-Location)
    Write-Host "$p" -NoNewline
    Write-Host (Get-GitBranch) -NoNewline -ForegroundColor Yellow
    Write-Host "> " -NoNewline
    return " "
}
Plumbing answered 29/5, 2023 at 7:38 Comment(1)
Works only in the root folder of the repository!Rivulet
S
0

This was the simplest method for me on Windows 11 using the Terminal app. It works on a themed PowerShell, applies no styling, and displays the branch when in sub directories.

  1. Run echo %profile in powershell
  2. Navigate to the file or create the directory and file if it doesn't exist. Mine is located here: C:\Users\YourName\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
  3. Paste the following code:
function prompt {
    $gitBranch = & git rev-parse --abbrev-ref HEAD 2>$null
    $promptString = "$PWD"
    
    if ($gitBranch) {
        $promptString += " ($gitBranch)"
    }
    
    "$promptString> "
}

My final result with the solarized dark PowerShell theme: enter image description here

Shortcut answered 14/8, 2023 at 22:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.