diff options
| author | godosa <godosa@godosa.eu> | 2026-10-07 09:57:12 +0200 |
|---|---|---|
| committer | godosa <godosa@godosa.eu> | 2026-10-07 09:57:12 +0200 |
| commit | 6f559437fe95b3a952b63b6672db7d89d0ce86c2 (patch) | |
| tree | 03eb1c00d219ed8698e005a6dfaf30075cc9ca0d | |
| parent | 959ba8df813ceda4df6466f2c35eea3bcce82271 (diff) | |
| download | workflow-6f559437fe95b3a952b63b6672db7d89d0ce86c2.tar.gz workflow-6f559437fe95b3a952b63b6672db7d89d0ce86c2.zip | |
wf start: refuse worktree of another repo; .wf-home not stray
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JEAjUkQRrCYX5MZhWxdtj2
| -rw-r--r-- | tests/test_split.py | 30 | ||||
| -rwxr-xr-x | wf.py | 10 |
2 files changed, 39 insertions, 1 deletions
diff --git a/tests/test_split.py b/tests/test_split.py index 934bf6f..9d19308 100644 --- a/tests/test_split.py +++ b/tests/test_split.py @@ -91,6 +91,26 @@ class StartSplitTest(SplitCli): self.assertIn(f"cd {wt} && (make test) && ", out) self.assertFalse((self.root / ".worktrees").exists()) # nothing in the private repo + def test_start_refuses_private_repo_worktree(self): + wt = self.root / ".worktrees" / "slow" + git(self.root, "worktree", "add", "-q", str(wt), "-b", "slow/old") + code, out, err = self.wf("start", "t-three", "--worktree", str(wt), "--branch", "slow/t-three", + project=False, env=IDENT) + self.assertNotEqual(code, 0) + self.assertIn("is not a worktree of", err) + self.assertFalse((wt / ".wf-home").exists()) + self.assertEqual(subprocess.run(["git", "branch", "--show-current"], cwd=wt, capture_output=True, + text=True).stdout.strip(), "slow/old") # not switched + self.assertIn("- **t-three** [P2] (<1h): Third", (self.root / "TASKS.md").read_text()) # no progress + + def test_start_reuses_existing_code_worktree(self): + wt = self.code / ".worktrees" / "slow" + git(self.code, "worktree", "add", "-q", str(wt), "-b", "slow/old") + code, out, err = self.wf("start", "t-three", "--worktree", str(wt), "--branch", "slow/t-three", + project=False, env=IDENT) + self.assertEqual(code, 0, out + err) + self.assertEqual((wt / ".wf-home").read_text(), f"{self.root}\n") + def test_setup_in_code_worktree(self): wt = self.add_wt() code, out, err = self.wf("setup", project=False, cwd=wt) @@ -152,6 +172,16 @@ class FinishSplitTest(SplitCli): self.assertEqual(subprocess.run(["git", "branch", "--list", "sonnet/t-three"], cwd=self.code, capture_output=True, text=True).stdout, "") + def test_unignored_wf_home_does_not_block(self): + (self.code / ".gitignore").write_text(".worktrees/\n") + git(self.code, "commit", "-qam", "no wf-home ignore") + git(self.wt, "rebase", "-q", "master") + (self.wt / "code.txt").write_text("x\n") + code, out, err = self.finish("t-three", "-m", "ok", "--commit", "impl", "code.txt") + self.assertEqual(code, 0, out + err) + self.assertNotIn(".wf-home", subprocess.run(["git", "show", "--stat", "master"], cwd=self.code, + capture_output=True, text=True).stdout) + def test_stray_code_file_refused_task_open(self): (self.wt / "code.txt").write_text("x\n") (self.wt / "stray.txt").write_text("s\n") @@ -1231,7 +1231,7 @@ def cmd_merge(args) -> int: bmain = config.git_top(cfg.root) or main # books repo: the private repo of a split project, else main private = [str(Path(x).resolve().relative_to(bmain.resolve())) for x in getattr(args, "private", None) or []] files = [str(f.relative_to(bmain)) for f in (cfg.tasks, cfg.archive)] + private - if git_run(top, "status", "--porcelain", "-uall").stdout.strip(): + if _dirty_outside(top, []): raise Failure("worktree has uncommitted changes: commit them first") had = unmerged_count(top, master) if not branch and not had: @@ -1326,6 +1326,8 @@ def _dirty_outside(top: Path, keep: list[Path]) -> list[str]: for line in git_run(top, "status", "--porcelain", "-uall").stdout.splitlines(): rel = line[3:].split(" -> ")[-1].strip('"').rstrip("/") path = (top / rel).resolve() + if rel == config.WF_HOME: # wf start's pointer in a code worktree, never committed + continue if not any(path == k or k in path.parents for k in keep): stray.append(rel) return stray @@ -1531,6 +1533,12 @@ def cmd_start(args) -> int: git_ok(repo, "worktree", "add", "-q", str(wt), "-b", b, master) how = f"new, branch {b} from {master}" else: + own = git_run(wt, "rev-parse", "--path-format=absolute", "--git-common-dir").stdout.strip() + want = git_run(repo, "rev-parse", "--path-format=absolute", "--git-common-dir").stdout.strip() + if not own or Path(own).resolve() != Path(want).resolve(): + raise Failure(f"{shown} is not a worktree of {repo}" + + (f" (it belongs to {Path(own).resolve().parent})" if own else "") + + f": pass a worktree path of {repo} (e.g. {repo}/.worktrees/<lane>)") dirty = git_run(wt, "status", "--porcelain").stdout.rstrip("\n") cur = config.git_branch(Path(git_run(wt, "rev-parse", "--absolute-git-dir").stdout.strip())) if dirty and not args.recovery: |
