在同一个本地存储库上工作的多个git客户机会竞争该锁。每个客户端都应该等待锁被另一方释放,这是一个好公民。对于我们来说,当我们运行大型提交脚本时,SourceTree或MSVS似乎正在后台进行一些维护。
也许'git'本身应该支持一个'——retriesWhenLocked 5'参数来支持重试。甚至在手动运行时默认为此。
下面是一个名为“gitr”的PowerShell包装器,它可以重新尝试直到创建索引。锁定消失,默认5次,每次3秒。它从不删除索引。锁定,假设用户应该干预。它是从一个较大的提交脚本中提取的。它只使用简单的参数进行最少的测试。
复制脚本到C:\bin,并将C:\bin添加到$PATH。
来自PS1> gitr -救命
从DOS %> powershell gitr -help
gitr.ps1
#requires -version 2
<#
.SYNOPSIS
gitr
.DESCRIPTION
Run "git" as an external process with retry and capturing stdout stderr.
.NOTES
2017/05/16 crokusek: Initial version
#>
#---------------------------------------------------------[Initializations]--------------------------------------------------------
#Set Error Action
$ErrorActionPreference = "Stop";
#----------------------------------------------------------[Declarations]----------------------------------------------------------
$scriptDir = Split-Path $script:MyInvocation.MyCommand.Path
#Set-Location $scriptDir
## Disabled logging
# Log File
# $logFile = "$($scriptDir)\getr.log"
# If (Test-Path $logFile) { Clear-Content $logFile }
#-----------------------------------------------------------[Functions]------------------------------------------------------------
Function Log([string]$msg, [bool]$echo = $true)
{
$timestamp = "$(get-date -Format 'yyyy/MM/dd HH:mm:ss'): "
$fullmsg = $msg -replace '(?ms)^', $timestamp # the (?ms) enables multiline mode
## Disabled Logging
# Add-content $LogFile -value $fullmsg
if ($echo)
{
Write-Host $msg
}
}
Function ExecSimple([string]$command, [bool]$echo=$true, [bool]$stopOnNonZeroExitCode=$true)
{
$command, $args = $command -split " "
return Exec $command $args $echo $stopOnNonZeroExitCode
}
Function Exec([string]$exe, [string[]]$arguments, [bool]$echo=$true, [bool]$stopOnNonZeroExitCode=$true)
{
# Passing $args (list) as a single parameter is the most flexible, it supports spaces and double quotes
$orgErrorActionPreference = $ErrorActionPreference
Try
{
$error.clear() # this apparently catches all the stderr pipe lines
if ($false -and $exe -eq 'git') # todo make this a generic flag
{
$exe = "$($exe) 2>&1"
}
$output = ""
$argflattened = $arguments -join ' '
Log "`n% $($exe) $($arguments)`n"
# This way some advantages over Invoke-Expressions or Start-Process for some cases:
# - merges stdout/stderr line by line properly,
# - echoes the output live as it is streamed to the current window,
# - waits for completion
# - works when calling both console and windows executables.
#
$ErrorActionPreference = "Continue" # required in order to catch more than 1 stderr line in the exception
if ($echo)
{
# Using "cmd.exe" allows the stderr -> stdout redirection to work properly. Otherwise the 2>&1 runs after PS for
# some reason. When a command such as "git" writes to stderr, powershell was terminating on the first stderr
# line (and stops capturing additional lines).
#
# but unfortuantely cmd has some bizarre de-quoting rules that weren't working for all cases.
#& cmd /c "`"" $exe $arguments "`"" | Tee-Object -variable output | Write-Host | out-null
# This is simplest but has some issues with stderr/stdout (stderr caught as exception below)
#
& $exe $arguments 2>&1 | tee -variable output | Write-Host | out-null
}
else
{
& $exe $arguments 2>&1 | tee -variable output | out-null
}
$output = $output -join "`r`n"
if ($stopOnNonZeroExitCode -and !$LASTEXITCODE -eq 0)
{
throw [System.Exception] "Exit code ($($LASTEXITCODE)) was non-zero. Output:`n$($output)"
}
}
catch [System.Management.Automation.RemoteException]
{
$output = $_.Exception.ToString().Replace("System.Management.Automation.RemoteException:", "").Trim()
if ($output.Contains("fatal"))
{
throw
}
if ($echo)
{
Log $output
}
}
finally
{
$ErrorActionPreference = $orgErrorActionPreference;
}
if (-not $output -eq "")
{
Log $output $false # don't echo to screen as the pipe above did
}
return $output
}
Function ExecWithRetry([string]$exe, [string[]]$arguments, [bool]$echo=$true, [bool]$stopOnNonZeroExitCode=$true,
[int]$maxRetries = 5, [int]$msDelay = 3000, [AllowNull()][string]$exceptionMustContain = $null)
{
for ($i = 0; $i -lt $maxRetries; $i++)
{
try
{
Exec $exe $arguments $echo $stopOnNonZeroExitCode
return
}
catch
{
if (-not [string]::IsNullOrEmpty($exceptionMustContain) -and $_.Exception.ToString().Contains($exceptionMustContain))
{
Log "Last Error from $($exe) is retryable ($($i + 1) of $($maxRetries))" $true
Start-Sleep -Milliseconds ($msDelay);
continue
}
throw
}
}
throw [System.Exception] "Unable to successfully exec '$($exe)' within $($maxRetries) attempts."
}
Function GitWithRetry([string[]]$arguments, [bool]$echo=$true)
{
ExecWithRetry "git" $arguments $echo -exceptionMustContain "Another git process seems to be running"
}
#-----------------------------------------------------------[Main]------------------------------------------------------------
function Main([string[]]$arguments)
{
GitWithRetry @($arguments)
}
#-------------------------------------- Startup ------------------------------------
try
{
Main $args
Exit 0
}
catch
{
#Log "*** A fatal error occured: $($_.Exception)"
#Read-Host -Prompt "`nA fatal error occurred, press enter to close."
exit 1
}