Skip to content

Git Hooks

Utilities for managing and running git hooks directly.

logger module-attribute

logger = getLogger(__name__)

HOOK_TYPES module-attribute

HOOK_TYPES = Literal[
	"pre-commit",
	"post-commit",
	"commit-msg",
	"pre-push",
	"post-checkout",
]

get_git_hooks_dir

get_git_hooks_dir(repo_root: Path | None = None) -> Path

Get the .git/hooks directory for the current or given repo.

Parameters:

Name Type Description Default
repo_root Path | None

Path to the repo root. Defaults to cwd.

None

Returns:

Type Description
Path

Path to the .git/hooks directory.

Source code in src/codemap/utils/git_hooks.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
def get_git_hooks_dir(repo_root: Path | None = None) -> Path:
	"""
	Get the .git/hooks directory for the current or given repo.

	Args:
	    repo_root: Path to the repo root. Defaults to cwd.

	Returns:
	    Path to the .git/hooks directory.
	"""
	root = repo_root or Path.cwd()
	# Find .git directory
	git_dir = root / ".git"
	if not git_dir.exists():
		msg = f".git directory not found at {git_dir}"
		raise FileNotFoundError(msg)
	hooks_dir = git_dir / "hooks"
	if not hooks_dir.exists():
		msg = f".git/hooks directory not found at {hooks_dir}"
		raise FileNotFoundError(msg)
	return hooks_dir

hook_exists

hook_exists(
	hook_type: HOOK_TYPES, repo_root: Path | None = None
) -> bool

Check if a given hook exists and is executable.

Parameters:

Name Type Description Default
hook_type HOOK_TYPES

The hook type ("pre-commit" or "pre-push").

required
repo_root Path | None

Path to the repo root. Defaults to cwd.

None

Returns:

Type Description
bool

True if the hook exists and is executable, False otherwise.

Source code in src/codemap/utils/git_hooks.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
def hook_exists(hook_type: HOOK_TYPES, repo_root: Path | None = None) -> bool:
	"""
	Check if a given hook exists and is executable.

	Args:
	    hook_type: The hook type ("pre-commit" or "pre-push").
	    repo_root: Path to the repo root. Defaults to cwd.

	Returns:
	    True if the hook exists and is executable, False otherwise.
	"""
	hooks_dir = get_git_hooks_dir(repo_root)
	hook_path = hooks_dir / hook_type
	return bool(hook_path.exists() and hook_path.is_file() and (hook_path.stat().st_mode & 0o111))

run_hook

run_hook(
	hook_type: HOOK_TYPES, repo_root: Path | None = None
) -> int

Run the specified git hook directly using bash.

Parameters:

Name Type Description Default
hook_type HOOK_TYPES

The hook type ("pre-commit" or "pre-push").

required
repo_root Path | None

Path to the repo root. Defaults to cwd.

None

Returns:

Type Description
int

The exit code of the hook process (0 if hook doesn't exist).

Source code in src/codemap/utils/git_hooks.py
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def run_hook(hook_type: HOOK_TYPES, repo_root: Path | None = None) -> int:
	"""
	Run the specified git hook directly using bash.

	Args:
	    hook_type: The hook type ("pre-commit" or "pre-push").
	    repo_root: Path to the repo root. Defaults to cwd.

	Returns:
	    The exit code of the hook process (0 if hook doesn't exist).
	"""
	hooks_dir = get_git_hooks_dir(repo_root)
	hook_path = hooks_dir / hook_type
	if not hook_path.exists():
		logger.debug(f"{hook_type} hook not found at {hook_path}")
		return 0
	if not (hook_path.stat().st_mode & 0o111):
		logger.warning(f"{hook_type} hook at {hook_path} is not executable")
		return 1
	try:
		logger.info(f"Running git hook: {hook_path}")
		result = subprocess.run(["bash", str(hook_path)], capture_output=True, text=True, check=False)  # noqa: S603, S607
		if result.stdout:
			logger.info(f"{hook_type} hook output:\n{result.stdout}")
		if result.stderr:
			logger.warning(f"{hook_type} hook error:\n{result.stderr}")
		return result.returncode
	except Exception:
		logger.exception(f"Failed to run {hook_type} hook")
		return 1

run_all_hooks

run_all_hooks(
	repo_root: Path | None = None,
) -> dict[str, int]

Run all supported hooks (pre-commit, pre-push) if they exist.

Parameters:

Name Type Description Default
repo_root Path | None

Path to the repo root. Defaults to cwd.

None

Returns:

Type Description
dict[str, int]

Dict mapping hook type to exit code.

Source code in src/codemap/utils/git_hooks.py
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
def run_all_hooks(repo_root: Path | None = None) -> dict[str, int]:
	"""
	Run all supported hooks (pre-commit, pre-push) if they exist.

	Args:
	    repo_root: Path to the repo root. Defaults to cwd.

	Returns:
	    Dict mapping hook type to exit code.
	"""
	results = {}
	hooks: list[HOOK_TYPES] = ["pre-commit", "post-commit", "commit-msg", "pre-push", "post-checkout"]
	for hook in hooks:
		if hook_exists(hook, repo_root):
			results[hook] = run_hook(hook, repo_root)
	return results