resolve_canonical_repo_root fallback now uses commonpath only — no string split and no os.path.isdir gate — so MCP project_root = branches/<wt> still resolves to the repo root when the path is not yet on disk (#274 / review #551 regression).
This commit is contained in:
@@ -93,9 +93,10 @@ def resolve_canonical_repo_root(workspace_path: str, fallback_project_root: str)
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
# Fallback when git metadata is unavailable. Never string-split on
|
# Fallback when git metadata is unavailable. Never string-split on
|
||||||
# "/branches/" (review #531 Finding 2 / F-6): recover the repo root only
|
# "/branches/" (review #531 F2 / #551): recover the repo root only via
|
||||||
# via resolved-path commonpath ancestry against a parent that owns a
|
# resolved-path commonpath ancestry. Do **not** require on-disk isdir —
|
||||||
# real ``branches`` directory containing the fallback path.
|
# MCP may launch with project_root = branches/<wt> before that path
|
||||||
|
# exists, and #274 path-shaped worktree-as-project-root must still resolve.
|
||||||
fallback = os.path.realpath(fallback_project_root or workspace_path or ".")
|
fallback = os.path.realpath(fallback_project_root or workspace_path or ".")
|
||||||
cur = fallback
|
cur = fallback
|
||||||
for _ in range(64):
|
for _ in range(64):
|
||||||
@@ -103,16 +104,18 @@ def resolve_canonical_repo_root(workspace_path: str, fallback_project_root: str)
|
|||||||
if parent == cur:
|
if parent == cur:
|
||||||
break
|
break
|
||||||
branches_dir = os.path.realpath(os.path.join(parent, "branches"))
|
branches_dir = os.path.realpath(os.path.join(parent, "branches"))
|
||||||
if os.path.isdir(branches_dir):
|
|
||||||
try:
|
try:
|
||||||
|
# Path-shaped: fallback is under parent/branches/ (commonpath).
|
||||||
if os.path.commonpath([branches_dir, fallback]) == branches_dir:
|
if os.path.commonpath([branches_dir, fallback]) == branches_dir:
|
||||||
return parent
|
return parent
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
# Also accept fallback itself being the branches directory.
|
# Fallback path itself is the branches directory.
|
||||||
if os.path.basename(cur) == "branches" and os.path.isdir(cur):
|
if os.path.basename(os.path.realpath(cur)) == "branches":
|
||||||
try:
|
try:
|
||||||
if os.path.commonpath([cur, fallback]) == cur:
|
if os.path.commonpath([os.path.realpath(cur), fallback]) == os.path.realpath(
|
||||||
|
cur
|
||||||
|
):
|
||||||
return parent
|
return parent
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -86,6 +86,13 @@ class TestAssessAuthorMutationWorktree(unittest.TestCase):
|
|||||||
self.assertTrue(result["proven"])
|
self.assertTrue(result["proven"])
|
||||||
self.assertFalse(result["block"])
|
self.assertFalse(result["block"])
|
||||||
|
|
||||||
|
def test_path_shaped_branches_ancestry_without_isdir(self):
|
||||||
|
"""Review #551: commonpath recovery must not require on-disk isdir."""
|
||||||
|
fake_wt = "/repo/Gitea-Tools/branches/issue-274"
|
||||||
|
root = amw.resolve_canonical_repo_root(fake_wt, fake_wt)
|
||||||
|
self.assertEqual(root, "/repo/Gitea-Tools")
|
||||||
|
self.assertNotIn('split("/branches/")', open(amw.__file__).read())
|
||||||
|
|
||||||
|
|
||||||
class TestPreflightIntegration(unittest.TestCase):
|
class TestPreflightIntegration(unittest.TestCase):
|
||||||
def test_verify_preflight_blocks_control_checkout_with_test_porcelain(self):
|
def test_verify_preflight_blocks_control_checkout_with_test_porcelain(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user