Skip to content

Commit 804f7a4

Browse files
authored
Merge pull request #2199 from gitpython-developers/fix-bash-exe-lookup-on-windows
Resolve Windows hook Bash through PATH
2 parents 04960cf + 7131bcc commit 804f7a4

2 files changed

Lines changed: 26 additions & 3 deletions

File tree

git/index/fun.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,21 @@ def _has_file_extension(path: str) -> str:
7979
return osp.splitext(path)[1]
8080

8181

82+
def _which_from_path(command: str) -> Union[str, None]:
83+
"""Resolve an executable 'command' from PATH without considering the current directory."""
84+
cwd = osp.normcase(osp.abspath(os.curdir))
85+
for directory in os.get_exec_path():
86+
if not directory:
87+
continue
88+
directory = osp.abspath(directory)
89+
if osp.normcase(directory) == cwd:
90+
continue
91+
candidate = osp.join(directory, command)
92+
if osp.isfile(candidate) and os.access(candidate, os.X_OK):
93+
return candidate
94+
return None
95+
96+
8297
def run_commit_hook(name: str, index: "IndexFile", *args: str) -> None:
8398
"""Run the commit hook of the given name. Silently ignore hooks that do not exist.
8499
@@ -112,7 +127,11 @@ def run_commit_hook(name: str, index: "IndexFile", *args: str) -> None:
112127
# an absolute path in this form, although a relative path is preferable
113128
# because it also works with the Windows Subsystem for Linux wrapper.
114129
bash_hp = hp
115-
cmd = ["bash.exe", Path(bash_hp).as_posix()]
130+
# Resolve through PATH before spawning. On Windows, CreateProcess searches
131+
# the current and system directories before PATH for a bare executable name,
132+
# which can otherwise select an impostor or the WSL launcher instead of Git
133+
# for Windows' Bash.
134+
cmd = [_which_from_path("bash.exe") or "bash.exe", Path(bash_hp).as_posix()]
116135

117136
process = safer_popen(
118137
cmd + list(args),

test/test_index.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,16 +1128,20 @@ def test_run_commit_hook_outside_worktree_on_windows(self, rw_dir):
11281128
repo = Repo.init(root / "repo")
11291129
hooks_dir = root / "hooks"
11301130
_make_hook(root, "fake-hook", "exit 0")
1131+
bash = root / "git" / "bin" / "bash.exe"
11311132
with repo.config_writer() as writer:
11321133
writer.set_value("core", "hooksPath", str(hooks_dir))
11331134

1134-
with mock.patch("git.index.fun.sys.platform", "win32"):
1135+
with mock.patch("git.index.fun.sys.platform", "win32"), mock.patch(
1136+
"git.index.fun._which_from_path", return_value=str(bash)
1137+
) as which:
11351138
with mock.patch("git.index.fun.safer_popen") as popen, mock.patch("git.index.fun.handle_process_output"):
11361139
popen.return_value.returncode = 0
11371140
run_commit_hook("fake-hook", repo.index)
11381141

1142+
which.assert_called_once_with("bash.exe")
11391143
command = popen.call_args[0][0]
1140-
self.assertEqual(command, ["bash.exe", "../hooks/fake-hook"])
1144+
self.assertEqual(command, [str(bash), "../hooks/fake-hook"])
11411145

11421146
@ddt.data((False,), (True,))
11431147
@with_rw_directory

0 commit comments

Comments
 (0)