update_manager: enhance attempt to recover from loose object errors
Attempt to recover from "loose object" error in git status and git fetch commands. It is no longer necessary to run git fsck on every refresh attempt, only run it after a recovery attempt. Signed-off-by: Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
parent
daa7b5edd4
commit
564e33bc18
|
@ -895,6 +895,7 @@ GIT_ENV_VARS = {
|
||||||
GIT_MAX_LOG_CNT = 100
|
GIT_MAX_LOG_CNT = 100
|
||||||
GIT_LOG_FMT = \
|
GIT_LOG_FMT = \
|
||||||
"\"sha:%H%x1Dauthor:%an%x1Ddate:%ct%x1Dsubject:%s%x1Dmessage:%b%x1E\""
|
"\"sha:%H%x1Dauthor:%an%x1Ddate:%ct%x1Dsubject:%s%x1Dmessage:%b%x1E\""
|
||||||
|
GIT_OBJ_ERR = "fatal: loose object"
|
||||||
|
|
||||||
class GitRepo:
|
class GitRepo:
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
|
@ -971,7 +972,6 @@ class GitRepo:
|
||||||
|
|
||||||
if need_fetch:
|
if need_fetch:
|
||||||
await self.fetch()
|
await self.fetch()
|
||||||
await self.run_fsck()
|
|
||||||
|
|
||||||
self.upstream_url = await self.remote(f"get-url {self.git_remote}")
|
self.upstream_url = await self.remote(f"get-url {self.git_remote}")
|
||||||
self.current_commit = await self.rev_parse("HEAD")
|
self.current_commit = await self.rev_parse("HEAD")
|
||||||
|
@ -1051,9 +1051,25 @@ class GitRepo:
|
||||||
return False
|
return False
|
||||||
await self._wait_for_lock_release()
|
await self._wait_for_lock_release()
|
||||||
self.valid_git_repo = False
|
self.valid_git_repo = False
|
||||||
try:
|
retries = 3
|
||||||
resp = await self._run_git_cmd("status -u no")
|
while retries:
|
||||||
except Exception:
|
self.git_messages.clear()
|
||||||
|
try:
|
||||||
|
resp: Optional[str] = await self._run_git_cmd(
|
||||||
|
"status -u no", retries=1)
|
||||||
|
except Exception:
|
||||||
|
retries -= 1
|
||||||
|
resp = None
|
||||||
|
# Attempt to recover from "loose object" error
|
||||||
|
if retries and GIT_OBJ_ERR in "\n".join(self.git_messages):
|
||||||
|
ret = await self._repair_loose_objects()
|
||||||
|
if not ret:
|
||||||
|
# Since we are unable to recover, immediately
|
||||||
|
# return
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
if resp is None:
|
||||||
return False
|
return False
|
||||||
resp = resp.strip().split('\n', 1)[0]
|
resp = resp.strip().split('\n', 1)[0]
|
||||||
self.head_detached = resp.startswith("HEAD detached")
|
self.head_detached = resp.startswith("HEAD detached")
|
||||||
|
@ -1310,6 +1326,19 @@ class GitRepo:
|
||||||
return
|
return
|
||||||
self._check_lock_file_exists(remove=True)
|
self._check_lock_file_exists(remove=True)
|
||||||
|
|
||||||
|
async def _repair_loose_objects(self) -> bool:
|
||||||
|
try:
|
||||||
|
await self.cmd_helper.run_cmd_with_response(
|
||||||
|
"find .git/objects/ -type f -empty | xargs rm",
|
||||||
|
timeout=10., retries=1, cwd=self.git_path)
|
||||||
|
await self._run_git_cmd_async(
|
||||||
|
"fetch --all -p", retries=1, fix_loose=False)
|
||||||
|
await self._run_git_cmd("fsck --full", timeout=300., retries=1)
|
||||||
|
except Exception:
|
||||||
|
logging.exception("Attempt to repair loose objects failed")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
async def _run_git_cmd_async(self,
|
async def _run_git_cmd_async(self,
|
||||||
cmd: str,
|
cmd: str,
|
||||||
retries: int = 5,
|
retries: int = 5,
|
||||||
|
@ -1345,14 +1374,15 @@ class GitRepo:
|
||||||
if ret == 0:
|
if ret == 0:
|
||||||
self.git_messages.clear()
|
self.git_messages.clear()
|
||||||
return
|
return
|
||||||
elif fix_loose and "loose object" in "\n".join(self.git_messages):
|
elif fix_loose:
|
||||||
# attempt to remove corrupt objects
|
if GIT_OBJ_ERR in "\n".join(self.git_messages):
|
||||||
try:
|
ret = await self._repair_loose_objects()
|
||||||
await self.cmd_helper.run_cmd_with_response(
|
if ret:
|
||||||
"find .git/objects/ -type f -empty | xargs rm",
|
break
|
||||||
timeout=10., retries=1, cwd=self.git_path)
|
# since the attept to repair failed, bypass retries
|
||||||
except self.server.error:
|
# and immediately raise an exception
|
||||||
pass
|
raise self.server.error(
|
||||||
|
f"Unable to repair loose objects, use hard recovery")
|
||||||
retries -= 1
|
retries -= 1
|
||||||
await tornado.gen.sleep(.5)
|
await tornado.gen.sleep(.5)
|
||||||
self._check_lock_file_exists(remove=True)
|
self._check_lock_file_exists(remove=True)
|
||||||
|
|
Loading…
Reference in New Issue