如何在PowerShell提示符中显示当前的git分支名称?

103

基本上我想要的是这个,但是用于PowerShell而不是bash。

我在Windows上通过PowerShell使用git。如果可能的话,我希望我的当前分支名称可以显示为命令提示符的一部分。


1
在 Git 2.22(2019 年第二季度)中,不要忘记 git branch --current - VonC
1
@VonC,命令是 git branch --show-current。不确定最初是否为 --current,但截至2023年,它已更改为前者。 - Jacques Mathieu
@JacquesMathieu 我同意,而且我参考的答案确实提到了 --show-current - VonC
12个回答

81
一个更简单的方法是安装 Powershell 模块 posh-git。它自带所需的提示符:
引用块:
提示符
PowerShell 通过执行 prompt 函数(如果存在)来生成其提示符。posh-git 在 profile.example.ps1 中定义了这样一个函数,它输出当前工作目录,后跟缩写的 git 状态: C:\Users\Keith [master]> 默认情况下,状态摘要具有以下格式: [{HEAD-name} +A ~B -C !D | +E ~F -G !H] (安装 posh-git 我建议使用 psget
如果没有 psget,请使用以下命令:
(new-object Net.WebClient).DownloadString("https://raw.githubusercontent.com/psget/psget/master/GetPsGet.ps1") | iex

要安装posh-git,请使用以下命令: Install-Module posh-git 为确保posh-git在每个shell中加载,请使用Add-PoshGitToProfile命令

2
@NicolaPeluchetti 谢谢!我按照 https://www.howtogeek.com/50236/customizing-your-powershell-profile/ 的步骤,在我的 PowerShell 配置文件中添加了 Import-Module posh-git。效果非常好! - Mihir
3
如果你使用chocolatey,你可以在提升的命令提示符中使用choco install poshgit来简单安装posh-git。 - German
2
我发现它很慢...加载个人和系统配置文件需要1065毫秒。 - GorvGoyl
1
posh-git太慢了,我觉得它做的太多了,我个人只想知道自己在哪个分支上,以及本地副本是否有未提交的更改,其他任何东西只会拖慢我的终端速度。 - Eman
显示剩余2条评论

57
这是我的看法。我稍微编辑了一下颜色,使其更易读。

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
}

示例1:

enter image description here

例子2:

enter image description here


1
不错!我刚刚改变了我的背景颜色。你的背景颜色是什么? - Yogeesh Seralathan
@YogeeshSeralathan 我正在使用 ConEmu 的 Monokai 主题。背景颜色为 #272822。 - tamj0rd2
1
Test-Path 不具有递归功能,只有在我们位于 Git 项目的根目录时才会返回 true - 而这并不总是情况。 - shabunc
3
对于那些不知道如何使用此ps1脚本的人,只需将其复制并粘贴到此处(适用于您的本地用户):Microsoft.PowerShell_profile.ps1文件中。$UserHome\[My ]Documents\PowerShell\对于我来说,因为我将OneDrive集成到了我的计算机中,它位于此处:C:\Users\<USER>\OneDrive - \Documents\WindowsPowerShell - rj2700
太棒了,谢谢。我通常更喜欢一些手写的命令,只需完成工作,而且易于理解,而不是安装一些外部的、可能很庞大/慢/来源不明的插件。 - undefined
显示剩余3条评论

24

@Paul-

我的Git PowerShell配置文件基于我在这里找到的脚本:

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

我对它进行了一些修改,以显示目录路径和一些格式设置。它还将路径设置为我的Git bin位置,因为我使用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 " "
 }

到目前为止,这个方法非常有效。在一个存储库中,提示符看起来像这样:
GIT [主分支] c:0 u:1 d:0 | J:\Projects\forks\fluent-nhibernate> *注意:已按Jakub Narębski的建议更新。
- 删除了git分支/git状态调用。 - 解决了“git config --global”失败的问题,因为$ HOME未设置。 - 解决了浏览到没有.git目录的目录会导致格式恢复为PS提示的问题。

大家好,David。我很容易地修改了这个,并且按照链接博客文章中的说明进行操作,成功地创建了适合我的程序。谢谢! - Paul Batum
6
不要使用git-branch输出来获取当前分支的名称,因为它是面向最终用户的(是“瓷器”)。请使用git symbolic-ref HEAD。不要使用git-status,因为它是面向最终用户的,且可能会更改(在1.7.0中已更改)。请使用git-diff-files、git-diff-tree、git-diff-index。 - Jakub Narębski
Jakub Narębski - 根据您的建议更新了代码--非常感谢。由于它没有调用分支和状态,因此速度也快了很多。 - David R. Longnecker

9

posh-git 运行缓慢,现在有更好的替代方案:https://ohmyposh.dev/.

enter image description here

  1. 从 PowerShell 运行以下命令安装 ohmyposh 模块:
Install-Module oh-my-posh -Scope CurrentUser -AllowPrerelease
  1. 安装支持图标的字体(https://www.nerdfonts.com/)。
    我喜欢Meslo LGM NF

  2. 在 Powershell 默认设置中设置该字体:

enter image description here

  1. 打开/创建位于 C:\Program Files\PowerShell\7 的文件 Microsoft.PowerShell_profile.ps1,并写入以下内容以设置主题(与截图相同):
Set-PoshPrompt -Theme aliens

你可以选择其他主题。通过运行Get-PoshThemes来查看预览。

现在,在包含Git存储库的位置打开PowerShell,您将看到状态。


了解更多: 赋予您的PowerShell更强大的功能


8

从Git 2.22(2019年第二季度)开始,任何脚本(包括Powershell)都可以使用新的--show-current选项

$branch = git branch --show-current

如果为空,则表示“分离的HEAD”。

@KarenGoh,除非您处于分离的HEAD模式下,否则应该可以。您使用的Git版本是什么? - VonC

3
我调整了提示代码(来自@david-longnecker的答案),使其更加丰富多彩。
编辑:2019年6月-更新以显示未跟踪,藏匿,重命名。微调视觉效果以显示索引。
为什么我使用这个(而不是posh-git等):
学习:在我调整时学习有关git / SCM概念的知识
通过这种方式解决posh-git解决的问题,但我通过这种方式学到了更多
跨平台:在POSIX shell中具有相同的提示,代码几乎相同。
灵活:我已经调整了我的提示以显示未跟踪/藏匿/索引/重命名。
轻量级和便携式:无需获取外部模块(这是次要的好处,但很好)
PowerShell代码:
注意:使用的一些命令是瓷器(不建议用于脚本/解析,例如git status)。最终将迁移到plumbing命令,但现在可以使用。
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

结果(VSCode,使用Powershell终端):

Powershell Git Prompt in action

以下是从结果中提取的命令,以查看其外观:

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

看起来确实不错...但我在考虑每次按回车键后等待超过一秒钟才出现新提示是否能接受... - xyious
@xyious 根据仓库的大小,各种git命令可能需要一段时间来处理。根据经验,这对我来说是“快速”的 - 可能只有在进入我的git仓库之一时会有几分之一秒的延迟。是否有更好的方法(保持功能集)? 我将其保存在dotfiles存储库中,并在基于POSIX的shell上具有匹配的提示符 - 这对于一致性和更重要的学习非常有帮助,而且易于阅读和理解(以及调整)。最后,此提示包括检查上游更改的非传统但不错的功能 - 如果它使我保持状态感知,则会花费一些时间。 - PotatoFarmer

2

我喜欢被接受的答案,所以我将详细说明设置步骤。 您可以使用Chocolatey或使用PowerShellGet命令安装PoshGit,该命令适用于新的Core PowerShell。

对于Chocolatey,您需要在继续之前已经安装它。 在管理员/提升的shell中执行以下命令:

choco install poshgit

对于核心PowerShell,还需要进行安装。要安装核心PowerShell,请执行以下命令:

dotnet tool install --global PowerShell

注意:您需要安装.NET Core SDK(最好是最新版本)

安装Core PowerShell后,执行以下命令安装PoshGit:

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

使用 PoshGit 需要使用命令 Import-Module posh-git 将其导入到当前正在运行的 shell 环境中。这意味着您每次打开新的 shell 时都必须运行此命令。

如果您希望始终可用,则应执行以下命令:

Add-PoshGitToProfile

请注意,PowerShell和Core PowerShell是不同的,因此它们运行在不同的配置文件上。这意味着,如果您希望PoshGit在这两个shell上工作,您需要在它们各自的shell环境中执行这些命令。

1

我根据@tamj0rd2的回答进行了改进,以在存储库的子文件夹中进一步显示分支名称。

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
}

查看差异:

- if (Test-Path .git) {
+ if (! (git -C . rev-parse) ) {

1

以下是我的 PowerShell Core 配置。只需复制以下函数并将其放入您的 $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> "
}

1

从 @tamj0rd2 的回答中,我们可以通过以下方式将分支名称存储到字符串变量中。

$branch = git rev-parse --abbrev-ref HEAD
echo $branch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接