Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use the correct .git path in case of worktrees. #2252

Merged
merged 1 commit into from Apr 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion pre_commit/commands/install_uninstall.py
Expand Up @@ -36,7 +36,7 @@ def _hook_paths(
hook_type: str,
git_dir: str | None = None,
) -> tuple[str, str]:
git_dir = git_dir if git_dir is not None else git.get_git_dir()
git_dir = git_dir if git_dir is not None else git.get_git_common_dir()
pth = os.path.join(git_dir, 'hooks', hook_type)
return pth, f'{pth}.legacy'

Expand Down
26 changes: 19 additions & 7 deletions pre_commit/git.py
Expand Up @@ -57,13 +57,15 @@ def get_root() -> str:
root = os.path.abspath(
cmd_output('git', 'rev-parse', '--show-cdup')[1].strip(),
)
git_dir = os.path.abspath(get_git_dir())
inside_git_dir = cmd_output(
'git', 'rev-parse', '--is-inside-git-dir',
)[1].strip()
except CalledProcessError:
raise FatalError(
'git failed. Is it installed, and are you in a Git repository '
'directory?',
)
if os.path.samefile(root, git_dir):
if inside_git_dir != 'false':
raise FatalError(
'git toplevel unexpectedly empty! make sure you are not '
'inside the `.git` directory of your repository.',
Expand All @@ -72,15 +74,25 @@ def get_root() -> str:


def get_git_dir(git_root: str = '.') -> str:
opts = ('--git-common-dir', '--git-dir')
_, out, _ = cmd_output('git', 'rev-parse', *opts, cwd=git_root)
for line, opt in zip(out.splitlines(), opts):
if line != opt: # pragma: no branch (git < 2.5)
return os.path.normpath(os.path.join(git_root, line))
opt = '--git-dir'
_, out, _ = cmd_output('git', 'rev-parse', opt, cwd=git_root)
git_dir = out.strip()
if git_dir != opt:
return os.path.normpath(os.path.join(git_root, git_dir))
else:
raise AssertionError('unreachable: no git dir')


def get_git_common_dir(git_root: str = '.') -> str:
opt = '--git-common-dir'
_, out, _ = cmd_output('git', 'rev-parse', opt, cwd=git_root)
git_common_dir = out.strip()
if git_common_dir != opt:
return os.path.normpath(os.path.join(git_root, git_common_dir))
else: # pragma: no cover (git < 2.5)
return get_git_dir(git_root)


def get_remote_url(git_root: str) -> str:
_, out, _ = cmd_output('git', 'config', 'remote.origin.url', cwd=git_root)
return out.strip()
Expand Down
30 changes: 30 additions & 0 deletions tests/git_test.py
Expand Up @@ -21,6 +21,20 @@ def test_get_root_deeper(in_git_dir):
assert os.path.normcase(git.get_root()) == expected


def test_get_root_in_git_sub_dir(in_git_dir):
expected = os.path.normcase(in_git_dir.strpath)
with pytest.raises(FatalError):
with in_git_dir.join('.git/objects').ensure_dir().as_cwd():
assert os.path.normcase(git.get_root()) == expected


def test_get_root_not_in_working_dir(in_git_dir):
expected = os.path.normcase(in_git_dir.strpath)
with pytest.raises(FatalError):
with in_git_dir.join('..').ensure_dir().as_cwd():
assert os.path.normcase(git.get_root()) == expected


def test_in_exactly_dot_git(in_git_dir):
with in_git_dir.join('.git').as_cwd(), pytest.raises(FatalError):
git.get_root()
Expand All @@ -40,6 +54,22 @@ def test_get_root_bare_worktree(tmpdir):
assert git.get_root() == os.path.abspath('.')


def test_get_git_dir(tmpdir):
"""Regression test for #1972"""
src = tmpdir.join('src').ensure_dir()
cmd_output('git', 'init', str(src))
git_commit(cwd=str(src))

worktree = tmpdir.join('worktree').ensure_dir()
cmd_output('git', 'worktree', 'add', '../worktree', cwd=src)

with worktree.as_cwd():
assert git.get_git_dir() == src.ensure_dir(
'.git/worktrees/worktree',
)
assert git.get_git_common_dir() == src.ensure_dir('.git')


def test_get_root_worktree_in_git(tmpdir):
src = tmpdir.join('src').ensure_dir()
cmd_output('git', 'init', str(src))
Expand Down