Plusieurs clients git travaillant sur le même référentiel local se disputent ce verrou. Chaque client doit attendre que le verrou soit libéré par l'autre partie pour être un bon citoyen. Pour nous, SourceTree ou MSVS semblent effectuer une maintenance en arrière-plan pendant que nous exécutions de gros scripts de validation.
Peut-être que 'git' lui-même devrait supporter un argument '--retriesWhenLocked 5' pour supporter les tentatives. ou même par défaut à cela lorsqu'il est exécuté manuellement.
Voici un wrapper PowerShell autour de git nommé "gitr" qui réessaye jusqu'à ce que index.lock disparaisse, en utilisant 5 essais par défaut, 3 secondes entre chacun. Il ne supprime jamais le fichier index.lock, en supposant que l'utilisateur doit intervenir. Il a été extrait d'un script de validation plus grand. Il n'a que peu de tests avec des arguments simples.
- Copiez le script dans C: \ bin et ajoutez C: \ bin dans $ PATH.
- Depuis PS1> gitr --help
- Depuis 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
}