同じローカルリポジトリで作業している複数のgitクライアントがそのロックをめぐって競合します。各クライアントは、相手がロックを解除するまで待機して、善良な市民になる必要があります。私たちにとって、大きなコミットスクリプトを実行している間、SourceTreeまたはMSVSがバックグラウンドで何らかのメンテナンスを行っているように見えます。
おそらく、 'git'自体が再試行をサポートするために '--retriesWhenLocked 5'引数をサポートする必要があります。手動で実行した場合、デフォルトでこれに設定されます。
これは、「gitr」という名前のgitを囲むPowerShellラッパーで、index.lockが消えるまで再試行します。デフォルトは5回、それぞれの間隔は3秒です。ユーザーが介入する必要があると想定して、index.lockを削除することはありません。より大きなコミットスクリプトから抽出されました。単純な引数を使用した最小限のテストしかありません。
- スクリプトをC:\ binにコピーし、C:\ binを$ PATHに追加します。
- PS1から> gitr --help
- 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
}