Skip to content

Commit d650160

Browse files
authored
Merge pull request #2252 from daschuer/worktree_fix
Use the correct .git path in case of worktrees.
2 parents 934afb8 + ba132f0 commit d650160

File tree

3 files changed

+50
-8
lines changed

3 files changed

+50
-8
lines changed

pre_commit/commands/install_uninstall.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def _hook_paths(
3636
hook_type: str,
3737
git_dir: str | None = None,
3838
) -> tuple[str, str]:
39-
git_dir = git_dir if git_dir is not None else git.get_git_dir()
39+
git_dir = git_dir if git_dir is not None else git.get_git_common_dir()
4040
pth = os.path.join(git_dir, 'hooks', hook_type)
4141
return pth, f'{pth}.legacy'
4242

pre_commit/git.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,15 @@ def get_root() -> str:
5757
root = os.path.abspath(
5858
cmd_output('git', 'rev-parse', '--show-cdup')[1].strip(),
5959
)
60-
git_dir = os.path.abspath(get_git_dir())
60+
inside_git_dir = cmd_output(
61+
'git', 'rev-parse', '--is-inside-git-dir',
62+
)[1].strip()
6163
except CalledProcessError:
6264
raise FatalError(
6365
'git failed. Is it installed, and are you in a Git repository '
6466
'directory?',
6567
)
66-
if os.path.samefile(root, git_dir):
68+
if inside_git_dir != 'false':
6769
raise FatalError(
6870
'git toplevel unexpectedly empty! make sure you are not '
6971
'inside the `.git` directory of your repository.',
@@ -72,15 +74,25 @@ def get_root() -> str:
7274

7375

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

8385

86+
def get_git_common_dir(git_root: str = '.') -> str:
87+
opt = '--git-common-dir'
88+
_, out, _ = cmd_output('git', 'rev-parse', opt, cwd=git_root)
89+
git_common_dir = out.strip()
90+
if git_common_dir != opt:
91+
return os.path.normpath(os.path.join(git_root, git_common_dir))
92+
else: # pragma: no cover (git < 2.5)
93+
return get_git_dir(git_root)
94+
95+
8496
def get_remote_url(git_root: str) -> str:
8597
_, out, _ = cmd_output('git', 'config', 'remote.origin.url', cwd=git_root)
8698
return out.strip()

tests/git_test.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,20 @@ def test_get_root_deeper(in_git_dir):
2121
assert os.path.normcase(git.get_root()) == expected
2222

2323

24+
def test_get_root_in_git_sub_dir(in_git_dir):
25+
expected = os.path.normcase(in_git_dir.strpath)
26+
with pytest.raises(FatalError):
27+
with in_git_dir.join('.git/objects').ensure_dir().as_cwd():
28+
assert os.path.normcase(git.get_root()) == expected
29+
30+
31+
def test_get_root_not_in_working_dir(in_git_dir):
32+
expected = os.path.normcase(in_git_dir.strpath)
33+
with pytest.raises(FatalError):
34+
with in_git_dir.join('..').ensure_dir().as_cwd():
35+
assert os.path.normcase(git.get_root()) == expected
36+
37+
2438
def test_in_exactly_dot_git(in_git_dir):
2539
with in_git_dir.join('.git').as_cwd(), pytest.raises(FatalError):
2640
git.get_root()
@@ -40,6 +54,22 @@ def test_get_root_bare_worktree(tmpdir):
4054
assert git.get_root() == os.path.abspath('.')
4155

4256

57+
def test_get_git_dir(tmpdir):
58+
"""Regression test for #1972"""
59+
src = tmpdir.join('src').ensure_dir()
60+
cmd_output('git', 'init', str(src))
61+
git_commit(cwd=str(src))
62+
63+
worktree = tmpdir.join('worktree').ensure_dir()
64+
cmd_output('git', 'worktree', 'add', '../worktree', cwd=src)
65+
66+
with worktree.as_cwd():
67+
assert git.get_git_dir() == src.ensure_dir(
68+
'.git/worktrees/worktree',
69+
)
70+
assert git.get_git_common_dir() == src.ensure_dir('.git')
71+
72+
4373
def test_get_root_worktree_in_git(tmpdir):
4474
src = tmpdir.join('src').ensure_dir()
4575
cmd_output('git', 'init', str(src))

0 commit comments

Comments
 (0)