fix(issue-21): PR #29 review R1-R3 - untracked-safe cleanliness checks; a worktree gets its own venv
R1/R2: `git status --porcelain` honours status.showUntrackedFiles, so a `no` setting in a submodule (or the worktree) hid untracked files from the checks that justify worktree_remove.sh's single --force and from the review snapshot guard (_dirty_submodules), which then approved content it never captured. All three checks (lib_py/worktree.py, worktree_remove.sh, runner._dirty_submodules) now pass --untracked-files=all. The source tree's own snapshot was already safe: `git add -A` ignores that setting. R3: worktree_prepare.sh no longer links .venv to the control root's. It creates the worktree's own venv with the control root's interpreter and installs requirements*.txt; a leftover link is refused (exit 1), a control root without an interpreter is a warning (VENV=skipped); VENV= is printed. Tests: w1 (real venv, kept on re-run), w1b (link refused / skipped), w6e parametrised over the hidden setting for the post-stop recheck, w6e2 (hidden untracked file in the submodule and in the worktree blocks the removal), l17 untracked-hidden / during-hidden (guard refuses before the first review; a mid-round hidden file makes the round stale). Each new case fails without the fix. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCgu9Fa9iT7TbJUSzdq4Ff
This commit is contained in:
1 parent
90a289da44
commit
08db1da9d5
9 files changed
+107
-29
No files matched your search
@@ -712,9 +712,10 @@ class LoopRunner:
|
||||
def _dirty_submodules(self) -> Optional[List[str]]:
|
||||
"""Checked-out submodules (recursively) with uncommitted or untracked
|
||||
content; None when the check itself fails. Each submodule is asked
|
||||
directly with --ignore-submodules=none, so no submodule.*.ignore or
|
||||
diff.ignoreSubmodules setting can hide it."""
|
||||
script = ('s=$(git status --porcelain --ignore-submodules=none) || exit 1; '
|
||||
directly with --ignore-submodules=none and --untracked-files=all, so no
|
||||
submodule.*.ignore, diff.ignoreSubmodules or status.showUntrackedFiles
|
||||
setting can hide it."""
|
||||
script = ('s=$(git status --porcelain --untracked-files=all --ignore-submodules=none) || exit 1; '
|
||||
'[ -z "$s" ] || printf "%s\\n" "$displaypath"')
|
||||
r = self._git(self.worktree_root, "submodule", "foreach", "--recursive", "--quiet", script)
|
||||
if r.returncode != 0:
|
||||
|
||||
@@ -155,7 +155,10 @@ def submodule_findings(wt: str) -> Dict[str, object]:
|
||||
after `submodule update`, so its commits may be on no branch, and a commit
|
||||
kept only by a tag (review R6) or a note is lost with the repository just
|
||||
the same. Stashes live in refs/stash and its reflog; the stash check owns
|
||||
them."""
|
||||
them. --untracked-files=all: `status.showUntrackedFiles=no` in a
|
||||
submodule would otherwise hide its untracked files from the check (review
|
||||
R1), and display settings must not decide what a forced removal may
|
||||
delete."""
|
||||
gd = _git(wt, "rev-parse", "--absolute-git-dir")
|
||||
if gd.returncode != 0 or not gd.stdout.strip():
|
||||
return {"trace": False, "dirty": "", "unpushed": "", "stashes": "",
|
||||
@@ -164,7 +167,7 @@ def submodule_findings(wt: str) -> Dict[str, object]:
|
||||
"dirty": "", "unpushed": "", "stashes": "", "error": ""}
|
||||
if not res["trace"]:
|
||||
return res
|
||||
checks = (("dirty", "git status --porcelain"),
|
||||
checks = (("dirty", "git status --porcelain --untracked-files=all"),
|
||||
("unpushed", "git log --oneline " + " ".join(UNPUSHED_REVS)),
|
||||
("stashes", "git stash list"))
|
||||
errors = []
|
||||
|
||||
Reference in new issue
Block a user