Skip to main content

Git Audit Pre-Push Hook

Pre-push guard that runs a quick git audit to warn about dirty sibling repos and submodule drift before pushing.


Purpose

Before pushing to a remote, verify that:

  1. No sibling repos have uncommitted changes that should be pushed together
  2. Submodule pointers are current — avoid pushing parent with stale submodule refs
  3. No staged-but-uncommitted files exist that were forgotten

Trigger

Event: PreToolUse:Bash (when command matches git push)

Conditions:

  • Command contains git push
  • Current directory is within a CODITECT project

Behavior

1. Detect: git push command invoked
2. Quick scan: Run git_audit.py on parent project (JSON mode, 7-day threshold)
3. Filter: Check only repos adjacent to the push target
4. Warn: If dirty repos found, display warning with list
5. Proceed: Allow push (warning only, non-blocking)

Implementation

#!/usr/bin/env python3
"""Pre-push hook: warn about dirty sibling repos."""

import json
import subprocess
import sys
from pathlib import Path


def find_project_root(start: Path) -> Path:
"""Walk up to find the project root (has .git and submodules/)."""
current = start.resolve()
while current != current.parent:
if (current / ".git").exists() and (current / "submodules").is_dir():
return current
current = current.parent
return start


def main():
cwd = Path.cwd()
root = find_project_root(cwd)

# Quick audit
result = subprocess.run(
["python3", "scripts/git_audit.py", str(root), "--json", "--days", "7"],
capture_output=True, text=True, timeout=60,
cwd=str(root / ".coditect") if (root / ".coditect").exists() else str(root),
)

if result.returncode != 0:
return # Don't block on audit failure

repos = json.loads(result.stdout)
dirty = [r for r in repos if not r["is_clean"]]

if dirty:
print(f"\n⚠️ Git Audit Warning: {len(dirty)} repo(s) have uncommitted changes:")
for r in dirty[:10]:
issues = ", ".join(r["issues"])
print(f" • {r['path']}{issues}")
if len(dirty) > 10:
print(f" ... and {len(dirty) - 10} more")
print()


if __name__ == "__main__":
main()

Configuration

SettingValue
BlockingNo (warning only)
Timeout60 seconds
ScopeParent project root
Threshold7 days

Integration

ComponentRole
scripts/git_audit.pyAudit engine
/git-audit commandFull on-demand audit
hooks/git-audit-scheduled.mdPeriodic background audit
skills/git-audit-patternsPatterns and workflow