diff --git a/pre_commit/commands/install_uninstall.py b/pre_commit/commands/install_uninstall.py index 73c8d605..f40b92b4 100644 --- a/pre_commit/commands/install_uninstall.py +++ b/pre_commit/commands/install_uninstall.py @@ -39,9 +39,15 @@ SYS_EXE = os.path.basename(os.path.realpath(sys.executable)) def _hook_paths( hook_type: str, git_dir: Optional[str] = None, + core_hookspath: str = '', ) -> Tuple[str, str]: - git_dir = git_dir if git_dir is not None else git.get_git_dir() - pth = os.path.join(git_dir, 'hooks', hook_type) + if core_hookspath: + pth = os.path.join(core_hookspath, hook_type) + + else: + git_dir = git_dir if git_dir is not None else git.get_git_dir() + pth = os.path.join(git_dir, 'hooks', hook_type) + return pth, f'{pth}.legacy' @@ -79,8 +85,13 @@ def _install_hook_script( overwrite: bool = False, skip_on_missing_config: bool = False, git_dir: Optional[str] = None, + core_hookspath: str = '', ) -> None: - hook_path, legacy_path = _hook_paths(hook_type, git_dir=git_dir) + hook_path, legacy_path = _hook_paths( + hook_type, + git_dir=git_dir, + core_hookspath=core_hookspath, + ) os.makedirs(os.path.dirname(hook_path), exist_ok=True) @@ -126,14 +137,18 @@ def install( overwrite: bool = False, hooks: bool = False, skip_on_missing_config: bool = False, + follow_hooks_path: bool = False, git_dir: Optional[str] = None, ) -> int: - if git_dir is None and git.has_core_hookpaths_set(): - logger.error( - 'Cowardly refusing to install hooks with `core.hooksPath` set.\n' - 'hint: `git config --unset-all core.hooksPath`', - ) - return 1 + core_hookspaths_set, core_hookspath = \ + git.has_core_hookpaths_set() + if git_dir is None and core_hookspaths_set: + if not follow_hooks_path: + logger.error( + 'Cowardly refusing to install hooks with `core.hooksPath` set.' + '\nhint: `git config --unset-all core.hooksPath`', + ) + return 1 for hook_type in hook_types: _install_hook_script( @@ -141,6 +156,7 @@ def install( overwrite=overwrite, skip_on_missing_config=skip_on_missing_config, git_dir=git_dir, + core_hookspath=core_hookspath, ) if hooks: diff --git a/pre_commit/git.py b/pre_commit/git.py index 6264529d..35a7220a 100644 --- a/pre_commit/git.py +++ b/pre_commit/git.py @@ -6,6 +6,7 @@ from typing import List from typing import MutableMapping from typing import Optional from typing import Set +from typing import Tuple from pre_commit.errors import FatalError from pre_commit.util import CalledProcessError @@ -176,9 +177,9 @@ def has_diff(*args: str, repo: str = '.') -> bool: return cmd_output_b(*cmd, cwd=repo, retcode=None)[0] == 1 -def has_core_hookpaths_set() -> bool: +def has_core_hookpaths_set() -> Tuple[bool, str]: _, out, _ = cmd_output_b('git', 'config', 'core.hooksPath', retcode=None) - return bool(out.strip()) + return bool(out.strip()), out.strip().decode() def init_repo(path: str, remote: str) -> None: diff --git a/pre_commit/main.py b/pre_commit/main.py index ad3d8737..4b075375 100644 --- a/pre_commit/main.py +++ b/pre_commit/main.py @@ -267,6 +267,14 @@ def main(argv: Optional[Sequence[str]] = None) -> int: ), ) + install_parser.add_argument( + '--follow-custom-hooks-path', action='store_true', default=False, + help=( + 'Whether to follow a custom core.hooksPath when attempting ' + 'to install the pre-comit hook.' + ), + ) + install_hooks_parser = subparsers.add_parser( 'install-hooks', help=( @@ -373,6 +381,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int: overwrite=args.overwrite, hooks=args.install_hooks, skip_on_missing_config=args.allow_missing_config, + follow_hooks_path=args.follow_custom_hooks_path, ) elif args.command == 'init-templatedir': return init_templatedir(