Skip to content

Commit b719e18

Browse files
idbriiByron
authored andcommitted
Use bash to open extensionless hooks on windows
Fix #971. Partly resolve #703. If the hook doesn't have a file extension, then Windows won't know how to run it and you'll get "[WinError 193] %1 is not a valid Win32 application". It's very likely that it's a shell script of some kind, so use bash.exe (commonly installed via Windows Subsystem for Linux). We don't want to run all hooks with bash because they could be .bat files. Update tests to get several hook ones working. More work necessary to get commit-msg hook working. The hook writes to the wrong file because it's not using forward slashes in the path: C:\Users\idbrii\AppData\Local\Temp\bare_test_commit_msg_hook_successy5fo00du\CUsersidbriiAppDataLocalTempbare_test_commit_msg_hook_successy5fo00duCOMMIT_EDITMSG
1 parent fac6037 commit b719e18

File tree

2 files changed

+20
-4
lines changed

2 files changed

+20
-4
lines changed

git/index/fun.py

+14-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
# NOTE: Autodoc hates it if this is a docstring
44

55
from io import BytesIO
6+
from pathlib import Path
67
import os
78
from stat import (
89
S_IFDIR,
@@ -21,6 +22,7 @@
2122
force_text,
2223
force_bytes,
2324
is_posix,
25+
is_win,
2426
safe_decode,
2527
)
2628
from git.exc import (
@@ -76,6 +78,10 @@ def hook_path(name: str, git_dir: PathLike) -> str:
7678
return osp.join(git_dir, 'hooks', name)
7779

7880

81+
def _has_file_extension(path):
82+
return osp.splitext(path)[1]
83+
84+
7985
def run_commit_hook(name: str, index: 'IndexFile', *args: str) -> None:
8086
"""Run the commit hook of the given name. Silently ignores hooks that do not exist.
8187
:param name: name of hook, like 'pre-commit'
@@ -89,8 +95,15 @@ def run_commit_hook(name: str, index: 'IndexFile', *args: str) -> None:
8995
env = os.environ.copy()
9096
env['GIT_INDEX_FILE'] = safe_decode(str(index.path))
9197
env['GIT_EDITOR'] = ':'
98+
cmd = [hp]
9299
try:
93-
cmd = subprocess.Popen([hp] + list(args),
100+
if is_win and not _has_file_extension(hp):
101+
# Windows only uses extensions to determine how to open files
102+
# (doesn't understand shebangs). Try using bash to run the hook.
103+
relative_hp = Path(hp).relative_to(index.repo.working_dir).as_posix()
104+
cmd = ["bash.exe", relative_hp]
105+
106+
cmd = subprocess.Popen(cmd + list(args),
94107
env=env,
95108
stdout=subprocess.PIPE,
96109
stderr=subprocess.PIPE,

test/test_index.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
)
1414
import tempfile
1515
from unittest import skipIf
16+
import shutil
1617

1718
from git import (
1819
IndexFile,
@@ -52,8 +53,9 @@
5253

5354
HOOKS_SHEBANG = "#!/usr/bin/env sh\n"
5455

56+
is_win_without_bash = is_win and not shutil.which('bash.exe')
57+
5558

56-
@skipIf(HIDE_WINDOWS_KNOWN_ERRORS, "TODO: fix hooks execution on Windows: #703")
5759
def _make_hook(git_dir, name, content, make_exec=True):
5860
"""A helper to create a hook"""
5961
hp = hook_path(name, git_dir)
@@ -881,7 +883,7 @@ def test_pre_commit_hook_fail(self, rw_repo):
881883
try:
882884
index.commit("This should fail")
883885
except HookExecutionError as err:
884-
if is_win:
886+
if is_win_without_bash:
885887
self.assertIsInstance(err.status, OSError)
886888
self.assertEqual(err.command, [hp])
887889
self.assertEqual(err.stdout, '')
@@ -896,6 +898,7 @@ def test_pre_commit_hook_fail(self, rw_repo):
896898
else:
897899
raise AssertionError("Should have caught a HookExecutionError")
898900

901+
@skipIf(HIDE_WINDOWS_KNOWN_ERRORS, "TODO: fix hooks execution on Windows: #703")
899902
@with_rw_repo('HEAD', bare=True)
900903
def test_commit_msg_hook_success(self, rw_repo):
901904
commit_message = "commit default head by Frèderic Çaufl€"
@@ -920,7 +923,7 @@ def test_commit_msg_hook_fail(self, rw_repo):
920923
try:
921924
index.commit("This should fail")
922925
except HookExecutionError as err:
923-
if is_win:
926+
if is_win_without_bash:
924927
self.assertIsInstance(err.status, OSError)
925928
self.assertEqual(err.command, [hp])
926929
self.assertEqual(err.stdout, '')

0 commit comments

Comments
 (0)