blog.dopana

Back

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
FeatureHusky / Third-partyNative Git Hook (core.hooksPath)
DependenciesRequires npm packagesZero dependencies
Execution SpeedNode wrapper startup overheadInstant Shell execution (<10ms)
Git SharedYesYes (committed inside repository)

Complete Pre-commit Hook Script#

Place this script at .githooks/pre-commit:

Detailed 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/"
  }
}
json

Make the script executable:

chmod +x .githooks/pre-commit
bun run prepare
bash

When 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.

References#