Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion hermes_cli/kanban_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -5655,6 +5655,37 @@ def _git_branch_exists(repo_root: Path, branch_name: str) -> bool:
return result.returncode == 0


def _git_default_base(repo_root: Path) -> str:
"""Resolve the commit a new worktree branch should be cut from.

Prefers the remote default branch (``origin/HEAD``, e.g. ``origin/main``)
so worker branches are based on the integration branch rather than
whatever the primary checkout happens to be parked on. If the operator or
another session leaves the repo on an unmerged feature branch, cutting new
worktree branches from that parked ``HEAD`` would contaminate every worker
branch with unrelated commits (see issue #68201).

Falls back to ``HEAD`` when no remote default is configured (e.g. a fresh
clone with no upstream, or an offline box), preserving the previous
behavior.
"""
try:
result = subprocess.run(
["git", "-C", str(repo_root), "rev-parse", "--abbrev-ref", "origin/HEAD"],
capture_output=True,
text=True,
timeout=30,
check=False,
)
except Exception:
return "HEAD"
if result.returncode == 0:
ref = (result.stdout or "").strip()
if ref and ref != "HEAD":
return ref
return "HEAD"


def _git_common_dir(path: Path) -> Optional[Path]:
try:
result = subprocess.run(
Expand Down Expand Up @@ -5748,9 +5779,13 @@ def _ensure_git_worktree(repo_root: Path, target: Path, branch_name: str) -> Non
if _git_branch_exists(repo_root, branch_name):
cmd = ["git", "-C", str(repo_root), "worktree", "add", str(target), branch_name]
else:
# Cut the new branch from the remote default (origin/HEAD) when
# available, falling back to HEAD. Basing on a parked local HEAD can
# contaminate the worker branch with unrelated commits (#68201).
base = _git_default_base(repo_root)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

origin/HEAD here is only a local cached symref, and _git_default_base() never fetches. A clone can therefore avoid the parked feature branch but still branch from a stale remote-tracking commit; an absent cached symref falls back to the parked HEAD. Please consolidate on a fresh remote-default contract and add coverage where the remote advances after clone.

cmd = [
"git", "-C", str(repo_root), "worktree", "add", "-b", branch_name,
str(target), "HEAD",
str(target), base,
]
result = subprocess.run(
cmd,
Expand Down
72 changes: 72 additions & 0 deletions tests/hermes_cli/test_kanban_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -4959,3 +4959,75 @@ def test_bare_connect_does_not_close_on_context_exit(tmp_path):
# Still usable after with-block exit (the leak).
conn.execute("SELECT 1").fetchone()
conn.close() # explicit close to avoid leaking THIS test


# ---------------------------------------------------------------------------
# Worktree base resolution (issue #68201)
# ---------------------------------------------------------------------------

def _add_remote_default(repo: Path, branch: str = "main") -> None:
"""Point origin/HEAD at the given branch so _git_default_base can resolve it."""
subprocess.run(
["git", "-C", str(repo), "symbolic-ref", "refs/remotes/origin/HEAD",
f"refs/remotes/origin/{branch}"],
check=True, capture_output=True, text=True,
)


def test_git_default_base_prefers_origin_head(tmp_path):
"""_git_default_base resolves origin/HEAD when the remote default exists."""
repo = tmp_path / "repo"
_init_git_repo(repo)
subprocess.run(["git", "-C", str(repo), "update-ref", "refs/remotes/origin/main", "HEAD"],
check=True, capture_output=True, text=True)
_add_remote_default(repo, "main")
assert kb._git_default_base(repo) == "origin/main"


def test_git_default_base_falls_back_to_head_without_remote(tmp_path):
"""With no origin/HEAD configured, _git_default_base falls back to HEAD."""
repo = tmp_path / "repo"
_init_git_repo(repo)
assert kb._git_default_base(repo) == "HEAD"


def test_ensure_git_worktree_bases_new_branch_on_origin_head(tmp_path):
"""A new worktree branch is cut from origin/HEAD, not a parked local HEAD (#68201).

Regression for #68201: if the primary checkout is left parked on an
unmerged feature branch, every worker worktree created during that window
inherited that branch's commits. We now base new branches on the remote
default branch when it resolves.
"""
repo = tmp_path / "repo"
_init_git_repo(repo) # default branch 'main', one commit on README.md
# Simulate a "parked" primary checkout: a feature branch with extra files.
subprocess.run(["git", "-C", str(repo), "checkout", "-b", "feature/parked"],
check=True, capture_output=True, text=True)
(repo / "feature_only.txt").write_text("should not leak\n", encoding="utf-8")
subprocess.run(["git", "-C", str(repo), "add", "feature_only.txt"],
check=True, capture_output=True, text=True)
subprocess.run(["git", "-C", str(repo), "commit", "-m", "feature work"],
check=True, capture_output=True, text=True)
# Leave the primary checkout parked on the feature branch.
subprocess.run(["git", "-C", str(repo), "update-ref", "refs/remotes/origin/main", "main"],
check=True, capture_output=True, text=True)
_add_remote_default(repo, "main")

target = tmp_path / "worktrees" / "wt1"
kb._ensure_git_worktree(repo, target, "wt/issue-68201")

# The new worktree branch must NOT contain the parked feature's file.
listing = subprocess.run(
["git", "-C", str(target), "ls-files"],
check=True, capture_output=True, text=True,
).stdout
assert "feature_only.txt" not in listing
assert "README.md" in listing
# And it must actually be based on origin/main (the parked commit is absent).
contained = subprocess.run(
["git", "-C", str(repo), "merge-base", "--is-ancestor", "feature/parked",
"wt/issue-68201"],
capture_output=True, text=True,
)
assert contained.returncode != 0, "worker branch should not descend from parked feature branch"