diff --git a/src/poetry/core/vcs/__init__.py b/src/poetry/core/vcs/__init__.py index 3afa1de31..321aa71a3 100644 --- a/src/poetry/core/vcs/__init__.py +++ b/src/poetry/core/vcs/__init__.py @@ -1,17 +1,18 @@ from __future__ import annotations -import os import subprocess -from pathlib import Path +from typing import TYPE_CHECKING from poetry.core.vcs.git import Git -def get_vcs(directory: Path) -> Git | None: - working_dir = Path.cwd() - os.chdir(str(directory.resolve())) +if TYPE_CHECKING: + from pathlib import Path + +def get_vcs(directory: Path) -> Git | None: + directory = directory.resolve(strict=True) vcs: Git | None try: @@ -21,23 +22,23 @@ def get_vcs(directory: Path) -> Git | None: [executable(), "check-ignore", "."], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL, + cwd=directory, ).returncode if check_ignore == 0: vcs = None else: - git_dir = subprocess.check_output( - [executable(), "rev-parse", "--show-toplevel"], + rel_path_to_git_dir = subprocess.check_output( + [executable(), "rev-parse", "--show-cdup"], stderr=subprocess.STDOUT, text=True, encoding="utf-8", + cwd=directory, ).strip() - vcs = Git(Path(git_dir)) + vcs = Git((directory / rel_path_to_git_dir).resolve()) except (subprocess.CalledProcessError, OSError, RuntimeError): vcs = None - finally: - os.chdir(str(working_dir)) return vcs diff --git a/tests/vcs/test_vcs.py b/tests/vcs/test_vcs.py index 0f6dd96f3..24df14bf6 100644 --- a/tests/vcs/test_vcs.py +++ b/tests/vcs/test_vcs.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os import subprocess from pathlib import Path @@ -480,3 +481,47 @@ def test_get_vcs_encoding(tmp_path: Path) -> None: assert vcs is not None assert vcs._work_dir is not None assert vcs._work_dir.exists() + assert vcs._work_dir == repo_path + + +def test_get_vc_subdir(tmp_path: Path) -> None: + repo_path = tmp_path / "répö" + repo_path.mkdir() + assert repo_path.exists() + assert subprocess.check_call([executable(), "init"], cwd=repo_path) == 0 + subdir = repo_path / "subdir" + subdir.mkdir() + vcs = get_vcs(subdir) + assert vcs is not None + assert vcs._work_dir is not None + assert vcs._work_dir.exists() + assert vcs._work_dir == repo_path + + +def test_get_vcs_no_repo(tmp_path: Path, mocker: MockerFixture) -> None: + repo_path = tmp_path / "répö" + repo_path.mkdir() + assert repo_path.exists() + assert subprocess.check_call([executable(), "init"], cwd=repo_path) == 0 + + # This makes sure git fails to find the git directory even if one + # exists at some higher level in the filesystem + mocker.patch.dict(os.environ, {"GIT_DIR": os.devnull}) + + vcs = get_vcs(repo_path) + assert vcs is None + + +def test_get_vcs_ignored_subdir(tmp_path: Path) -> None: + # See https://github.com/python-poetry/poetry-core/pull/611 + repo_path = tmp_path / "répö" + repo_path.mkdir() + assert repo_path.exists() + assert subprocess.check_call([executable(), "init"], cwd=repo_path) == 0 + with open(repo_path / ".gitignore", "w", encoding="utf-8") as f: + f.write("/ignored") + subdir = repo_path / "ignored" + subdir.mkdir() + + vcs = get_vcs(subdir) + assert vcs is None