Skip to content

Commit 1b44672

Browse files
copilot feedback
1 parent a2a9614 commit 1b44672

2 files changed

Lines changed: 60 additions & 8 deletions

File tree

src/holoviz_mcp/holoviz_mcp/data.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,16 +1259,24 @@ def _clone_or_update_repo_sync(self, repo_name: str, repo_config: "GitRepository
12591259
try:
12601260
if repo_path.exists():
12611261
repo = git.Repo(repo_path)
1262-
expected_ref = repo_config.branch or repo_config.tag
12631262

1264-
if expected_ref:
1263+
if repo_config.tag:
1264+
# Tag checkouts leave the repo in detached HEAD; verify via tag→commit mapping.
1265+
matching_tag = next((t for t in repo.tags if t.name == repo_config.tag), None)
1266+
if matching_tag and matching_tag.commit == repo.head.commit:
1267+
# Already on the correct tag — tags are immutable, nothing to pull.
1268+
return repo_path
1269+
logger.info(f"Stale checkout for {repo_name}: expected tag '{repo_config.tag}'. Deleting and re-cloning...")
1270+
shutil.rmtree(repo_path)
1271+
# Fall through to clone below
1272+
elif repo_config.branch:
12651273
try:
1266-
current_ref = repo.active_branch.name
1274+
current_branch = repo.active_branch.name
12671275
except TypeError:
1268-
current_ref = None # Detached HEAD (e.g. tag/commit checkout)
1276+
current_branch = None # Detached HEAD
12691277

1270-
if current_ref != expected_ref:
1271-
logger.info(f"Stale checkout for {repo_name}: on '{current_ref}', expected '{expected_ref}'. " "Deleting and re-cloning...")
1278+
if current_branch != repo_config.branch:
1279+
logger.info(f"Stale checkout for {repo_name}: on '{current_branch}', expected '{repo_config.branch}'. Deleting and re-cloning...")
12721280
shutil.rmtree(repo_path)
12731281
# Fall through to clone below
12741282
else:

tests/docs_mcp/test_data.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2058,8 +2058,46 @@ def test_tag_config_used_as_branch_kwarg_on_clone(self, tmp_path):
20582058
_, kwargs = mock_clone_from.call_args
20592059
assert kwargs.get("branch") == "v1.2.3"
20602060

2061+
def test_correct_tag_already_checked_out_returns_path(self, tmp_path):
2062+
"""Existing repo already on the correct tag (detached HEAD): returns path, no re-clone, no pull."""
2063+
from unittest.mock import MagicMock
2064+
from unittest.mock import patch
2065+
2066+
indexer = self._make_indexer(tmp_path)
2067+
repo_config = GitRepository(
2068+
url=AnyHttpUrl("https://github.com/fake/repo.git"),
2069+
base_url=AnyHttpUrl("https://example.com/"),
2070+
tag="v1.2.3",
2071+
folders=["doc"],
2072+
)
2073+
2074+
repo_dir = tmp_path / "repos" / "repo"
2075+
repo_dir.mkdir(parents=True)
2076+
2077+
# Simulate detached HEAD: active_branch raises TypeError (real GitPython behaviour)
2078+
mock_commit = MagicMock()
2079+
mock_tag = MagicMock()
2080+
mock_tag.name = "v1.2.3"
2081+
mock_tag.commit = mock_commit
2082+
2083+
mock_repo = MagicMock()
2084+
mock_repo.tags = [mock_tag]
2085+
mock_repo.head.commit = mock_commit # same object → already on correct tag
2086+
2087+
import git
2088+
2089+
with patch("holoviz_mcp.holoviz_mcp.data.git.Repo", return_value=mock_repo):
2090+
with patch("holoviz_mcp.holoviz_mcp.data.shutil.rmtree") as mock_rmtree:
2091+
with patch.object(git.Repo, "clone_from") as mock_clone_from:
2092+
result = indexer._clone_or_update_repo_sync("repo", repo_config)
2093+
2094+
assert result == repo_dir
2095+
mock_rmtree.assert_not_called()
2096+
mock_clone_from.assert_not_called()
2097+
mock_repo.remotes.origin.pull.assert_not_called()
2098+
20612099
def test_stale_tag_triggers_reclone(self, tmp_path):
2062-
"""Existing repo on wrong tag: dir is deleted and re-cloned on the correct tag."""
2100+
"""Existing repo on wrong tag (detached HEAD): dir is deleted and re-cloned on the correct tag."""
20632101
from unittest.mock import MagicMock
20642102
from unittest.mock import patch
20652103

@@ -2076,8 +2114,14 @@ def test_stale_tag_triggers_reclone(self, tmp_path):
20762114
repo_dir = tmp_path / "repos" / "repo"
20772115
repo_dir.mkdir(parents=True)
20782116

2117+
# Simulate detached HEAD at a different tag — tag commit != HEAD commit
2118+
mock_tag = MagicMock()
2119+
mock_tag.name = "v1.2.3"
2120+
mock_tag.commit = MagicMock() # tag points somewhere
2121+
20792122
mock_repo = MagicMock()
2080-
mock_repo.active_branch.name = "v0.9.0"
2123+
mock_repo.tags = [mock_tag]
2124+
mock_repo.head.commit = MagicMock() # HEAD is elsewhere → stale
20812125

20822126
with patch("holoviz_mcp.holoviz_mcp.data.git.Repo", return_value=mock_repo):
20832127
with patch("holoviz_mcp.holoviz_mcp.data.shutil.rmtree") as mock_rmtree:

0 commit comments

Comments
 (0)