From 31f84c0a4e13009a19bf9195729a6ea48b44788a Mon Sep 17 00:00:00 2001 From: banteg <4562643+banteg@users.noreply.github.com> Date: Sat, 10 Jan 2026 01:23:01 +0400 Subject: [PATCH] fix(worktrees): reuse project root for current branch (#77) --- src/takopi/worktrees.py | 20 ++++++++++++++++++-- tests/test_worktrees.py | 23 +++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/takopi/worktrees.py b/src/takopi/worktrees.py index 96d6e2c..0e405aa 100644 --- a/src/takopi/worktrees.py +++ b/src/takopi/worktrees.py @@ -4,7 +4,13 @@ from pathlib import Path from .config import ProjectConfig, ProjectsConfig from .context import RunContext -from .utils.git import git_is_worktree, git_ok, git_run, resolve_default_base +from .utils.git import ( + git_is_worktree, + git_ok, + git_run, + git_stdout, + resolve_default_base, +) class WorktreeError(RuntimeError): @@ -23,7 +29,10 @@ def resolve_run_cwd( raise WorktreeError(f"unknown project {context.project!r}") if context.branch is None: return project.path - return ensure_worktree(project, context.branch) + branch = _sanitize_branch(context.branch) + if _matches_project_branch(project.path, branch): + return project.path + return ensure_worktree(project, branch) def ensure_worktree(project: ProjectConfig, branch: str) -> Path: @@ -112,6 +121,13 @@ def _sanitize_branch(branch: str) -> str: return cleaned +def _matches_project_branch(root: Path, branch: str) -> bool: + current = git_stdout(["branch", "--show-current"], cwd=root) + if not current: + return False + return current == branch + + def _ensure_within_root(root: Path, path: Path) -> None: root_resolved = root.resolve(strict=False) path_resolved = path.resolve(strict=False) diff --git a/tests/test_worktrees.py b/tests/test_worktrees.py index eaf9431..1be8deb 100644 --- a/tests/test_worktrees.py +++ b/tests/test_worktrees.py @@ -34,6 +34,29 @@ def test_resolve_run_cwd_rejects_invalid_branch(tmp_path: Path) -> None: resolve_run_cwd(ctx, projects=projects) +def test_resolve_run_cwd_uses_root_when_branch_matches( + monkeypatch, tmp_path: Path +) -> None: + projects = _projects_config(tmp_path) + + def _fake_stdout(args, **_kwargs): + if args == ["branch", "--show-current"]: + return "main" + return None + + def _unexpected(*_args, **_kwargs): + raise AssertionError("unexpected") + + monkeypatch.setattr("takopi.worktrees.git_stdout", _fake_stdout) + monkeypatch.setattr( + "takopi.worktrees.ensure_worktree", + _unexpected, + ) + + ctx = RunContext(project="z80", branch="main") + assert resolve_run_cwd(ctx, projects=projects) == tmp_path + + def test_ensure_worktree_creates_from_base(monkeypatch, tmp_path: Path) -> None: project = ProjectConfig( alias="z80",