Git Pre-commit Hook — Auto Lint & Format Staged Files
Build a dependency-free Git pre-commit hook: auto-format Markdown, run ESLint, and validate Mermaid syntax on staged files.
Ensuring consistent code formatting and clean documentation before committing to Git is essential for maintainable projects. Instead of relying on bulky third-party tools, you can leverage native Git Hooks with core.hooksPath for a lightweight, zero-dependency pre-commit pipeline.
flowchart TD
Start(["Developer runs git commit -m '...'"]) --> Hook[".githooks/pre-commit triggered"]
Hook --> Step1{"1. Check staged Markdown<br/>(.md, .mdx)"}
Step1 -->|Files found| Formatter["Run agent-md format<br/>and git add file"]
Step1 -->|None| Step2{"2. Check staged Code<br/>(.ts, .tsx, .astro...)"}
Formatter --> Step2
Step2 -->|Files found| Linter["Run bun lint (ESLint)<br/>and git add file"]
Step2 -->|None| Step3{"3. Check staged Docs/Diagrams"}
Linter --> Step3
Step3 -->|Files found| MermaidLinter["Run bun lint:mermaid (maid)"]
Step3 -->|None| Done(["Commit Successful ✅"])
MermaidLinter -->|Syntax Error| Abort(["Commit Aborted ❌<br/>Fix syntax before committing"])
MermaidLinter -->|Valid| Done
Why Native Git Hooks Over Husky?#
flowchart LR
subgraph HuskyApproach["Husky Approach"]
H1["Requires npm package installation"]
H2["Bloats node_modules"]
H3["Depends on external Node.js wrappers"]
end
subgraph NativeApproach["Native Git Hook (core.hooksPath)"]
N1["Single shell script in .githooks/"]
N2["Zero external dependencies"]
N3["Version controlled across team"]
end
| Feature | Husky / Third-party | Native Git Hook (core.hooksPath) |
|---|---|---|
| Dependencies | Requires npm packages | Zero dependencies |
| Execution Speed | Node wrapper startup overhead | Instant Shell execution (<10ms) |
| Git Shared | Yes | Yes (committed inside repository) |
Complete Pre-commit Hook Script#
Place this script at .githooks/pre-commit:
#!/usr/bin/env bash
set -e
# 1. Format staged markdown files with agent-md if available
if command -v agent-md >/dev/null 2>&1; then
STAGED_MD_FILES=$(git diff --cached --name-only --diff-filter=d | grep -E '\.(md|mdx)$' || true)
if [ -n "$STAGED_MD_FILES" ]; then
echo "[git-hook] Running agent-md format on staged markdown files..."
for file in $STAGED_MD_FILES; do
if [ -f "$file" ]; then
agent-md "$file"
git add "$file"
fi
done
fi
fi
# 2. Lint changed code files with bun lint
STAGED_CODE_FILES=$(git diff --cached --name-only --diff-filter=d | grep -E '\.(js|jsx|ts|tsx|astro)$' || true)
if [ -n "$STAGED_CODE_FILES" ]; then
echo "[git-hook] Running bun lint on staged code files..."
bun lint
for file in $STAGED_CODE_FILES; do
if [ -f "$file" ]; then
git add "$file"
fi
done
fi
# 3. Lint mermaid diagrams with bun lint:mermaid
STAGED_DOC_FILES=$(git diff --cached --name-only --diff-filter=d | grep -E '\.(md|mdx|html|astro)$' || true)
if [ -n "$STAGED_DOC_FILES" ]; then
echo "[git-hook] Running bun lint:mermaid on diagrams..."
bun lint:mermaid
fibashDetailed Logic Breakdown#
1. Staged Files Filtering via git diff --cached#
flowchart LR
GitIndex["Git Index (Staged Area)"] --> Filter["git diff --cached --name-only --diff-filter=d"]
Filter --> Regex["grep -E '\\.(ext)$'"]
Regex --> Files["Target list of files"]
--cached: Restricts checks strictly to staged files ready to be committed.--diff-filter=d: Excludes deleted files so linters don’t throw errors trying to open missing files.set -e: Halts execution and aborts the commit if any linter exits with non-zero status.
2. Automated Re-staging#
Whenever formatting tools (agent-md or eslint --fix) adjust staged files, git add "$file" bundles the fixes directly into the current commit.
Setting Up Repository Automation#
Add the prepare script in package.json:
{
"scripts": {
"prepare": "git config core.hooksPath .githooks",
"lint": "eslint --fix 'src/**/*.{js,ts,jsx,tsx,astro}'",
"lint:mermaid": "maid docs/ src/"
}
}jsonMake the script executable:
chmod +x .githooks/pre-commit
bun run preparebashWhen teammates install dependencies via bun install, the prepare script runs automatically and activates the hook path.
Conclusion#
With a clean shell script:
- All Markdown files stay neatly formatted.
- Source code is automatically linted.
- Mermaid diagrams are verified to prevent broken documentation.