Skip to content
Draft
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
21 changes: 14 additions & 7 deletions src/molecule/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,23 +440,30 @@ def _filter_platforms(


def _is_valid_vcs_dir(path: Path, name: str) -> bool:
"""Check if a VCS directory is a genuine repository root.
"""Check if a VCS entry marks a genuine repository root.

Args:
path: Parent directory to check.
name: VCS directory name (e.g. ".git", ".hg", ".svn").
name: VCS entry name (e.g. ".git", ".hg", ".svn").

Returns:
Whether the VCS directory is a genuine repository root.
Whether the VCS entry marks a genuine repository root.
"""
vcs_dir = path / name
if not vcs_dir.is_dir():
vcs_entry = path / name
# In a git worktree (or a submodule) the ".git" entry is a file
# containing a "gitdir: <path>" pointer rather than a directory.
if name == ".git" and vcs_entry.is_file():
try:
return vcs_entry.read_text(encoding="utf-8").startswith("gitdir:")
except OSError:
return False
if not vcs_entry.is_dir():
return False
# A real .git directory always contains a HEAD file.
# Bare repos, worktrees, and regular repos all have it.
# Bare repos and regular repos all have it.
# Spurious .git dirs (e.g. created by GitKraken) do not.
if name == ".git":
return (vcs_dir / "HEAD").exists()
return (vcs_entry / "HEAD").exists()
return True


Expand Down
46 changes: 46 additions & 0 deletions tests/unit/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,52 @@ def test_find_vcs_root_skips_fake_git_dir(tmp_path: Path) -> None:
assert result == str(repo)


def test_find_vcs_root_in_git_worktree(tmp_path: Path) -> None:
"""Ensure find_vcs_root recognizes a worktree where .git is a file.

In a git worktree the ".git" entry is a file containing a
"gitdir: <path>" pointer rather than a directory.

Args:
tmp_path: pytest fixture for temporary directory.
"""
# Worktree root: .git is a file pointing at the main repo's gitdir.
worktree = tmp_path / "worktree"
worktree.mkdir()
git_file = worktree / ".git"
git_file.write_text(f"gitdir: {tmp_path / 'main' / '.git' / 'worktrees' / 'wt'}\n")

subdir = worktree / "infra"
subdir.mkdir()

util.find_vcs_root.cache_clear()
result = util.find_vcs_root(location=str(subdir))
assert result == str(worktree)


def test_find_vcs_root_skips_bogus_git_file(tmp_path: Path) -> None:
"""Ensure find_vcs_root ignores a .git file without a gitdir pointer.

Args:
tmp_path: pytest fixture for temporary directory.
"""
# Real repo at top level.
repo = tmp_path / "repo"
repo.mkdir()
git_dir = repo / ".git"
git_dir.mkdir()
(git_dir / "HEAD").write_text("ref: refs/heads/main\n")

# Subdirectory with a spurious .git file that is not a worktree pointer.
subdir = repo / "infra"
subdir.mkdir()
(subdir / ".git").write_text("not a gitdir pointer\n")

util.find_vcs_root.cache_clear()
result = util.find_vcs_root(location=str(subdir))
assert result == str(repo)


@pytest.mark.parametrize(
("input_value", "expected"),
(
Expand Down
Loading