PowerShell 的 Prompt 中显示 Git 分支已经有非常成熟的解决方案 — posh-git ,脚本写得非常严谨,但是使用 posh-git 首先需要忍受其加载时间,如果在 PowerShell 的 profile 文件中加载往往需要 5 秒左右,对于一个强迫症患者是无法忍受的,所以可以通过 lazy load 的方式来加载 posh-git module,如下面的脚本所示
function git_prompt {
if (-not $(Get-InstalledModule -Name "posh-git")) {
Install-Module posh-git -Scope CurrentUser -Force
}
if (-not (Get-Module -Name "posh-git")) {
Import-Module -Name posh-git -Scope Global
}
else {
$GitPromptSettings.EnablePromptStatus = -not $GitPromptSettings.EnablePromptStatus
}
}
使用 posh-git 面临的第二个挑战是执行时间。我们知道 PowerShell 环境下修改 prompt 是通过 Override 内置函数 prompt 来实现的,posh-git 也是同样的原理,所以我们使用类似 Unix Shell 中 time
命令的 Measure-Command
来验证一下 posh-git 中 prompt 函数的执行时间,执行的截图如下所示。
可能是我实验的 Repo 有点大,prompt 函数的执行时间确实非常感人,可以达到 221 毫秒,这个延迟足以让人误以为 CPU 是不是 100% 了而感到心慌。所以抱着节约生命不瞎折腾的心态坚持使用了 posh-git 一年后,我还是决定自己造个轻便一点的小轮子,也就有了下文。
参考 Bash 和 Zsh 环境 Prompt 显示 Git 分支 的思路,通过判断 .git/HEAD
文件内容来决定是否重新计算 branch 的方式来优化 prompt 执行时间,脚本参考如下。
function git_branch_internal {
$dir = $PWD.Path
do {
if (Test-Path "${dir}/.git/HEAD") {
$head = Get-Content "${dir}/.git/HEAD"
if ($head -eq $Global:GIT_NAME_HEAD) {
return
}
$Global:GIT_NAME_HEAD = $head
if ($head -match "^ref\:\ refs\/heads\/(?<branch>.*)") {
$Global:GIT_NAME_TITLE = "branch"
$Global:GIT_NAME_CONTENT = $Matches.branch
}
else {
$describe = $(git describe --tags --abbrev=7 2> $null)
if ($describe) {
$Global:GIT_NAME_TITLE = "tag"
$Global:GIT_NAME_CONTENT = $describe
}
else {
$Global:GIT_NAME_TITLE = "commit"
$Global:GIT_NAME_CONTENT = $head.Substring(0, 7)
}
}
$Global:GIT_NAME_LEFT = ":["
$Global:GIT_NAME_RIGHT = "]"
"$Global:GIT_NAME_TITLE$Global:GIT_NAME_LEFT$Global:GIT_NAME_CONTENT$Global:GIT_NAME_RIGHT" | Out-Null
return
}
${dir} = Split-Path -Parent -Path "${dir}"
} while (${dir})
$Global:GIT_NAME_TITLE = ""
$Global:GIT_NAME_CONTENT = ""
$Global:GIT_NAME_LEFT = ""
$Global:GIT_NAME_RIGHT = ""
$Global:GIT_NAME_HEAD = ""
}
function prompt_custom {
git_branch_internal
Write-Host $(prompt_bak) -NoNewline
Write-Host ${GIT_NAME_TITLE}${GIT_NAME_LEFT} -ForegroundColor Cyan -NoNewline
Write-Host ${GIT_NAME_CONTENT} -ForegroundColor Yellow -NoNewline
Write-Host ${GIT_NAME_RIGHT}">" -ForegroundColor Cyan -NoNewline
# Prompt function requires a return, otherwise defaults to factory prompt
return " "
}
function git_prompt {
if (Get-Command prompt_bak -errorAction SilentlyContinue) {
Copy-Item Function::Global:prompt_bak Function::Global:prompt
Remove-Item Function::prompt_bak
$Global:GIT_NAME_HEAD = ""; "$Global:GIT_NAME_HEAD" | Out-Null
}
else {
Copy-Item Function::Global:prompt Function::Global:prompt_bak
Copy-Item Function::Global:prompt_custom Function::Global:prompt
}
}
修改后使用 Measure-Command
测试了一下 prompt 函数的执行时间,截图如下。
可以看到,prompt 函数的执行时间从百毫秒级降到了毫秒级,经验证可以同时支持 MacOS/Linux/Windows 系统下的 Powershell 运行环境。