Retry on ref lock errors during fetch/pull (#5161)

Related to #5124

## Changes
- Extended existing retry logic to handle transient ref lock errors
- Added retry for "cannot lock ref" and "cannot update ref" error
messages
- Extracted retryable error check into helper function for clarity

These errors can occur intermittently during fetch/pull operations on
large repositories and typically succeed on retry.
This commit is contained in:
Stefan Haller 2026-04-20 09:09:17 +02:00 committed by GitHub
commit c183f1ea8c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -20,6 +20,13 @@ type gitCmdObjRunner struct {
innerRunner oscommands.ICmdObjRunner
}
// isRetryableError returns true if the error output indicates a transient
// lock-related error that may succeed on retry
func isRetryableError(output string) bool {
return strings.Contains(output, ".git/index.lock") ||
strings.Contains(output, "cannot lock ref")
}
func (self *gitCmdObjRunner) Run(cmdObj *oscommands.CmdObj) error {
_, err := self.RunWithOutput(cmdObj)
return err
@ -32,12 +39,12 @@ func (self *gitCmdObjRunner) RunWithOutput(cmdObj *oscommands.CmdObj) (string, e
newCmdObj := cmdObj.Clone()
output, err = self.innerRunner.RunWithOutput(newCmdObj)
if err == nil || !strings.Contains(output, ".git/index.lock") {
if err == nil || !isRetryableError(output) {
return output, err
}
// if we have an error based on the index lock, we should wait a bit and then retry
self.log.Warn("index.lock prevented command from running. Retrying command after a small wait")
// if we have an error based on a lock, we should wait a bit and then retry
self.log.Warn("lock error prevented command from running. Retrying command after a small wait")
time.Sleep(WaitTime)
}
@ -51,12 +58,12 @@ func (self *gitCmdObjRunner) RunWithOutputs(cmdObj *oscommands.CmdObj) (string,
newCmdObj := cmdObj.Clone()
stdout, stderr, err = self.innerRunner.RunWithOutputs(newCmdObj)
if err == nil || !strings.Contains(stdout+stderr, ".git/index.lock") {
if err == nil || !isRetryableError(stdout+stderr) {
return stdout, stderr, err
}
// if we have an error based on the index lock, we should wait a bit and then retry
self.log.Warn("index.lock prevented command from running. Retrying command after a small wait")
// if we have an error based on a lock, we should wait a bit and then retry
self.log.Warn("lock error prevented command from running. Retrying command after a small wait")
time.Sleep(WaitTime)
}