| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -64,6 +64,17 @@ def hook_path(name: str, git_dir: PathLike) -> str: | |||
| 64 | 64 | return osp.join(git_dir, "hooks", name) | |
| 65 | 65 | ||
| 66 | 66 | ||
| 67 | + def _commit_hook_path(name: str, index: "IndexFile") -> str: | ||
| 68 | + """:return: path to the named commit hook, respecting Git's core.hooksPath.""" | ||
| 69 | + with index.repo.config_reader() as config: | ||
| 70 | + hooks_dir = config.get("core", "hooksPath", fallback="") | ||
| 71 | + | ||
| 72 | + if not hooks_dir: | ||
| 73 | + return hook_path(name, index.repo.git_dir) | ||
| 74 | + | ||
| 75 | + return osp.abspath(osp.join(index.repo.working_dir, osp.expanduser(hooks_dir), name)) | ||
| 76 | + | ||
| 77 | + | ||
| 67 | 78 | def _has_file_extension(path: str) -> str: | |
| 68 | 79 | return osp.splitext(path)[1] | |
| 69 | 80 | ||
@@ -82,7 +93,7 @@ def run_commit_hook(name: str, index: "IndexFile", *args: str) -> None: | |||
| 82 | 93 | ||
| 83 | 94 | :raise git.exc.HookExecutionError: | |
| 84 | 95 | """ | |
| 85 | - hp = hook_path(name, index.repo.git_dir) | ||
| 96 | + hp = _commit_hook_path(name, index) | ||
| 86 | 97 | if not os.access(hp, os.X_OK): | |
| 87 | 98 | return | |
| 88 | 99 | ||
@@ -94,8 +105,14 @@ def run_commit_hook(name: str, index: "IndexFile", *args: str) -> None: | |||
| 94 | 105 | if sys.platform == "win32" and not _has_file_extension(hp): | |
| 95 | 106 | # Windows only uses extensions to determine how to open files | |
| 96 | 107 | # (doesn't understand shebangs). Try using bash to run the hook. | |
| 97 | - relative_hp = Path(hp).relative_to(index.repo.working_dir).as_posix() | ||
| 98 | - cmd = ["bash.exe", relative_hp] | ||
| 108 | + try: | ||
| 109 | + bash_hp = osp.relpath(hp, index.repo.working_dir) | ||
| 110 | + except ValueError: | ||
| 111 | + # Different drives have no relative path on Windows. Git Bash accepts | ||
| 112 | + # an absolute path in this form, although a relative path is preferable | ||
| 113 | + # because it also works with the Windows Subsystem for Linux wrapper. | ||
| 114 | + bash_hp = hp | ||
| 115 | + cmd = ["bash.exe", Path(bash_hp).as_posix()] | ||
| 99 | 116 | ||
| 100 | 117 | process = safer_popen( | |
| 101 | 118 | cmd + list(args), | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1099,9 +1099,35 @@ class Mocked: | |||
| 1099 | 1099 | def test_run_commit_hook(self, rw_repo): | |
| 1100 | 1100 | index = rw_repo.index | |
| 1101 | 1101 | _make_hook(index.repo.git_dir, "fake-hook", "echo 'ran fake hook' >output.txt") | |
| 1102 | - run_commit_hook("fake-hook", index) | ||
| 1103 | - output = Path(rw_repo.git_dir, "output.txt").read_text(encoding="utf-8") | ||
| 1104 | - self.assertEqual(output, "ran fake hook\n") | ||
| 1102 | + output = Path(rw_repo.git_dir, "output.txt") | ||
| 1103 | + with mock.patch.object(Repo, "config_level", ("repository",)): | ||
| 1104 | + with mock.patch.object(Git, "execute", side_effect=AssertionError("hook lookup must not run git")): | ||
| 1105 | + run_commit_hook("fake-hook", index) | ||
| 1106 | + self.assertEqual(output.read_text(encoding="utf-8"), "ran fake hook\n") | ||
| 1107 | + | ||
| 1108 | + output.unlink() | ||
| 1109 | + with index.repo.config_writer() as writer: | ||
| 1110 | + writer.set_value("core", "hooksPath", "") | ||
| 1111 | + run_commit_hook("fake-hook", index) | ||
| 1112 | + | ||
| 1113 | + self.assertEqual(output.read_text(encoding="utf-8"), "ran fake hook\n") | ||
| 1114 | + | ||
| 1115 | + @with_rw_directory | ||
| 1116 | + def test_run_commit_hook_outside_worktree_on_windows(self, rw_dir): | ||
| 1117 | + root = Path(rw_dir).resolve() | ||
| 1118 | + repo = Repo.init(root / "repo") | ||
| 1119 | + hooks_dir = root / "hooks" | ||
| 1120 | + _make_hook(root, "fake-hook", "exit 0") | ||
| 1121 | + with repo.config_writer() as writer: | ||
| 1122 | + writer.set_value("core", "hooksPath", str(hooks_dir)) | ||
| 1123 | + | ||
| 1124 | + with mock.patch("git.index.fun.sys.platform", "win32"): | ||
| 1125 | + with mock.patch("git.index.fun.safer_popen") as popen, mock.patch("git.index.fun.handle_process_output"): | ||
| 1126 | + popen.return_value.returncode = 0 | ||
| 1127 | + run_commit_hook("fake-hook", repo.index) | ||
| 1128 | + | ||
| 1129 | + command = popen.call_args[0][0] | ||
| 1130 | + self.assertEqual(command, ["bash.exe", "../hooks/fake-hook"]) | ||
| 1105 | 1131 | ||
| 1106 | 1132 | @ddt.data((False,), (True,)) | |
| 1107 | 1133 | @with_rw_directory | |
@@ -1160,6 +1186,32 @@ def test_pre_commit_hook_success(self, rw_repo): | |||
| 1160 | 1186 | _make_hook(index.repo.git_dir, "pre-commit", "exit 0") | |
| 1161 | 1187 | index.commit("This should not fail") | |
| 1162 | 1188 | ||
| 1189 | + @pytest.mark.xfail( | ||
| 1190 | + type(_win_bash_status) is WinBashStatus.Absent, | ||
| 1191 | + reason="Can't run a hook on Windows without bash.exe.", | ||
| 1192 | + raises=HookExecutionError, | ||
| 1193 | + ) | ||
| 1194 | + @pytest.mark.xfail( | ||
| 1195 | + type(_win_bash_status) is WinBashStatus.WslNoDistro, | ||
| 1196 | + reason="Currently uses the bash.exe of WSL, even with no WSL distro installed", | ||
| 1197 | + raises=HookExecutionError, | ||
| 1198 | + ) | ||
| 1199 | + @with_rw_repo("HEAD") | ||
| 1200 | + def test_pre_commit_hook_respects_core_hooks_path(self, rw_repo): | ||
| 1201 | + index = rw_repo.index | ||
| 1202 | + hooks_dir = Path(index.repo.working_dir, "custom-hooks") | ||
| 1203 | + hooks_dir.mkdir() | ||
| 1204 | + hp = hooks_dir / "pre-commit" | ||
| 1205 | + hp.write_text(HOOKS_SHEBANG + "echo 'ran custom hook' >custom-hook-output.txt", encoding="utf-8") | ||
| 1206 | + os.chmod(hp, 0o744) | ||
| 1207 | + | ||
| 1208 | + with index.repo.config_writer() as writer: | ||
| 1209 | + writer.set_value("core", "hooksPath", "custom-hooks") | ||
| 1210 | + | ||
| 1211 | + index.commit("This should run the custom hook") | ||
| 1212 | + output = Path(rw_repo.working_dir, "custom-hook-output.txt").read_text(encoding="utf-8") | ||
| 1213 | + self.assertEqual(output, "ran custom hook\n") | ||
| 1214 | + | ||
| 1163 | 1215 | @pytest.mark.xfail( | |
| 1164 | 1216 | type(_win_bash_status) is WinBashStatus.WslNoDistro, | |
| 1165 | 1217 | reason="Currently uses the bash.exe of WSL, even with no WSL distro installed", | |
| Back | FazBrowse Home | New Git URL |
0 commit comments