fix(update): address review of the index.lock fix

- The automatic update's status check now sets GIT_OPTIONAL_LOCKS=0
  whichever runner it is given, not only the default one.
- Update Code no longer runs `git rebase --abort` on any rebase dir: a
  rebase in progress (detached HEAD) holds the user's conflict work and
  blocks the update; an abandoned one is dropped with `rebase --quit`,
  as the automatic update does, and a failure to clear it stops the update.
- run_git waits out the lock-freshness window after stopping a timed-out
  git, so a lock created just before the timeout is not left behind.
- Mark the Popen/subprocess use with the repo's nosec comments.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-10-29 11:44:41 -04:00
co-authored by Claude Sonnet 5.5
parent e506c1b576
commit 67b373fca0
4 changed files with 118 additions and 10 deletions
+17 -3
View File
@@ -19,7 +19,7 @@ the file alone. Standard library only.
import logging
import os
import signal
import subprocess
import subprocess # nosec B404 - list-form argv only, no shell # nosemgrep
import time
from pathlib import Path
@@ -166,6 +166,20 @@ def _stop_group(proc):
pass
def _clear_after_stop(project_dir):
"""Remove the lock a stopped git left, waiting out the freshness window.
A lock created within ``MIN_STALE_AGE_SECONDS`` of the stop reads as IN_USE
on age alone; with no git process left it only needs to age, so wait for
that rather than leave it to block the next attempt.
"""
deadline = time.monotonic() + MIN_STALE_AGE_SECONDS + 1
while clear_stale_index_lock(project_dir) == IN_USE and git_running_in(project_dir) is False:
if time.monotonic() >= deadline:
break
time.sleep(0.5)
def run_git(cmd, *, cwd=None, timeout=None, capture_output=True, text=True, **kwargs):
"""``subprocess.run`` for git, without leaving ``.git/index.lock`` behind.
@@ -177,14 +191,14 @@ def run_git(cmd, *, cwd=None, timeout=None, capture_output=True, text=True, **kw
"""
if capture_output:
kwargs['stdout'] = kwargs['stderr'] = subprocess.PIPE
with subprocess.Popen(cmd, cwd=cwd, text=text, start_new_session=True, **kwargs) as proc:
with subprocess.Popen(cmd, cwd=cwd, text=text, start_new_session=True, **kwargs) as proc: # nosec B603 - list-form argv # nosemgrep
try:
stdout, stderr = proc.communicate(timeout=timeout)
except subprocess.TimeoutExpired as exc:
_stop_group(proc)
exc.stdout, exc.stderr = proc.communicate()
if cwd is not None:
clear_stale_index_lock(cwd)
_clear_after_stop(cwd)
raise
except BaseException:
_stop_group(proc)