برای راه اندازی Pre-commit Hooks کافی است یکی از این سه مسیر را انتخاب کنید: 1) ساخت فایل اجرایی .git/hooks/pre-commit و اجرای اسکریپت‌های موردنظرتان، 2) استفاده از ابزار pre-commit برای مدیریت چندین چک استاندارد، 3) استفاده از Husky و lint-staged در پروژه‌های JS/TS. در همه روش‌ها، قوانین لینت، فرمت، تست و جلوگیری از فایل‌های حجیم را مشخص کنید، هوک را نصب و اجرایی کنید و نتیجه را با یک commit آزمایشی بررسی کنید. این راهنمای عملی، مراحل را قدم‌به‌قدم با نمونه اسکریپت و کانفیگ کامل نشان می‌دهد.

انتخاب روش مناسب

سه گزینه رایج در ابزارهای برنامه نویسی وجود دارد. بر اساس اکوسیستم و سطح استانداردسازی تیم انتخاب کنید.

روشمناسب برایمزیتمحدودیت
Git Hooks داخلیهر زبانی، اسکریپت‌های سفارشیبدون وابستگی، سریع و سادهمدیریت و به‌روزرسانی تیمی دشوارتر
pre-commit (ابزار Python)پروژه‌های چندزبانه یا Pythonمدیریت متمرکز چک‌ها، قابل حملنیاز به نصب ابزار اضافی
Husky + lint-stagedJS/TS و فرانت‌اندتجربه عالی با ESLint/Prettierوابسته به اکوسیستم Node

راه اندازی با Git Hooks داخلی (انعطاف‌پذیر و بدون وابستگی)

مراحل

  1. در ریشه مخزن، فایل .git/hooks/pre-commit را بسازید.
  2. دستورهای موردنظر (لینت، فرمت، تست، جلوگیری از فایل‌های بزرگ) را داخل آن بنویسید.
  3. فایل را اجرایی کنید: chmod +x .git/hooks/pre-commit
  4. با یک commit آزمایشی، عملکرد را بررسی کنید.

نمونه اسکریپت pre-commit چندزبانه

این اسکریپت فقط روی فایل‌های staged کار می‌کند، فرمت/لینت را برای JS/TS و Python اجرا می‌کند، فایل‌های بزرگ را مسدود می‌کند و در صورت تعریف شدن، تست‌ها را اجرا می‌کند.

#!/usr/bin/env bash
set -euo pipefail

# فایل‌های staged
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)

# اگر چیزی staged نیست، خروج
[ -z "$STAGED_FILES" ] && exit 0

# JS/TS: Prettier و ESLint
if command -v npx >/dev/null 2>&1; then
  JS_FILES=$(echo "$STAGED_FILES" | grep -E '\.(js|jsx|ts|tsx)$' || true)
  if [ -n "$JS_FILES" ]; then
    echo "Running Prettier on JS/TS..."
    npx prettier --write $JS_FILES
    git add $JS_FILES
    if command -v npx >/dev/null 2>&1; then
      echo "Running ESLint on JS/TS..."
      npx eslint $JS_FILES
    fi
  fi
fi

# Python: Black و Flake8
PY_FILES=$(echo "$STAGED_FILES" | grep -E '\.py$' || true)
if [ -n "$PY_FILES" ]; then
  if command -v black >/dev/null 2>&1; then
    echo "Running Black on Python..."
    black $PY_FILES
    git add $PY_FILES
  fi
  if command -v flake8 >/dev/null 2>&1; then
    echo "Running Flake8 on Python..."
    flake8 $PY_FILES
  fi
fi

# جلوگیری از فایل‌های بزرگ (> 5MB)
LARGE_FILES=""
for f in $STAGED_FILES; do
  if [ -f "$f" ]; then
    SIZE=$(wc -c "$f" | awk '{print $1}')
    if [ "$SIZE" -gt 5242880 ]; then
      LARGE_FILES="$LARGE_FILES\n$f"
    fi
  fi
done
if [ -n "$LARGE_FILES" ]; then
  echo "این فایل‌ها بزرگ‌تر از 5MB هستند و بلاک شدند:"
  echo -e "$LARGE_FILES"
  echo "از Git LFS استفاده کنید یا اندازه را کاهش دهید."
  exit 1
fi

# اجرای تست‌ها (اختیاری)
if [ -f package.json ] && command -v npm >/dev/null 2>&1; then
  echo "Running npm test (if present)..."
  npm test --silent --if-present
fi

echo "Pre-commit checks passed."

بررسی نتیجه

یک فایل با فرمت نادرست را تغییر دهید، آن را stage کنید و commit بزنید. اسکریپت باید فرمت کند یا خطای لینت را گزارش دهد. برای عبور موقت (در شرایط اضطراری) می‌توانید از –no-verify استفاده کنید؛ این گزینه را به عادت تبدیل نکنید.

راه اندازی با pre-commit (مدیریت استاندارد و چندزبانه)

pre-commit ابزاری برای تعریف و اجرای هوک‌ها با یک فایل کانفیگ واحد است. در این مثال از repo: local استفاده می‌کنیم تا بدون URL هم قابل استفاده باشد.

پیش‌نیاز

  • نصب Python و pip (یا pipx)
  • نصب ابزارهایی که قرار است اجرا شوند؛ مثلا black و flake8 برای Python، یا prettier برای فرانت‌اند
pip install pre-commit black flake8
# در صورت نیاز به prettier:
# npm install -D prettier

مراحل

  1. در ریشه مخزن، فایل .pre-commit-config.yaml را بسازید.
  2. کانفیگ زیر را قرار دهید؛ چک‌های Python و فرمت فرانت‌اند را پوشش می‌دهد.
  3. pre-commit را نصب کنید: pre-commit install
  4. برای بررسی همه فایل‌ها یک بار اجرا کنید: pre-commit run –all-files
repos:
  - repo: local
    hooks:
      - id: black
        name: black (format python)
        entry: black
        language: system
        types: [python]
      - id: flake8
        name: flake8 (lint python)
        entry: flake8
        language: system
        types: [python]
        args: ["--max-line-length=100"]
      - id: prettier
        name: prettier (format web)
        entry: npx prettier --write
        language: system
        types_or: [javascript, jsx, ts, tsx, css, scss, json, markdown, yaml]
      - id: check-large-files
        name: block large files (>5MB)
        entry: bash -c 'for f in "$@"; do [ -f "$f" ] || continue; s=$(wc -c "$f" | awk "{print \\$1}"); [ "$s" -le 5242880 ] || { echo "$f is too large"; exit 1; }; done'
        language: system
        files: .*

اگر در تیم به نسخه‌بندی دقیق ابزارها نیاز دارید، نسخه آن‌ها را در ابزار مدیریت بسته (pip/requirements، npm) قفل کنید. مزیت این روش، یکپارچگی و قابلیت تکرار روی همه ماشین‌ها و CI است.

راه اندازی با Husky و lint-staged (JS/TS)

مراحل

  1. نصب وابستگی‌ها:
npm install -D husky lint-staged eslint prettier
  1. فعال‌سازی Husky در پروژه و اطمینان از اجرای خودکار پس از نصب:
npx husky install
# برای اجرا در نصب‌های بعدی:
npm pkg set scripts.prepare="husky install"
  1. ساخت هوک pre-commit که lint-staged را اجرا کند:
npx husky add .husky/pre-commit "npx lint-staged"
  1. تنظیم lint-staged در package.json:
{
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "prettier --write",
      "eslint"
    ],
    "*.{json,css,scss,md,yaml,yml}": [
      "prettier --write"
    ]
  }
}

با این تنظیم، فقط فایل‌های staged بررسی می‌شوند و در صورت فرمت شدن، دوباره به stage اضافه می‌شوند.

روش بررسی صحیح بودن نتیجه

  • commit آزمایشی: یک فایل با خطای لینت اضافه کنید، stage و commit بزنید؛ انتظار دارید commit بلاک شود.
  • فرمت خودکار: یک فایل نیازمند فرمت را stage و commit کنید؛ باید فرمت شود و commit ادامه یابد.
  • بازگشت کد خروج:故 اگر ابزارها خطا دهند، commit باید متوقف شود. کد خروج صفر به معنی موفقیت است.
  • اجرای دستی: در pre-commit می‌توانید pre-commit run –all-files را اجرا کنید تا وضعیت کل مخزن را ببینید.

چک لیست اجرایی برای هوک‌های قابل اتکا

  • سریع نگه دارید: فقط فایل‌های staged را بررسی کنید و از اجرای تست‌های سنگین پرهیز کنید.
  • قوانین شفاف: خطاهایی که باید بلاک شوند را روشن تعیین کنید؛ موارد اطلاع‌رسانی را به pre-push یا CI بسپارید.
  • قابل تکرار: نسخه ابزارها را در فایل‌های قفل (lock) یا requirements مشخص کنید.
  • محافظت از مخزن: از هوک اندازه فایل، جلوگیری از نشتی کلیدها/اسرار و کشف نشانه‌های درگیری مرج استفاده کنید.
  • هماهنگی با CI: همان فرمان‌ها در CI اجرا شوند تا از دور زدن محلی جلوگیری شود.

خطاهای رایج و راه حل

  • Permission denied برای pre-commit: با chmod +x فایل را اجرایی کنید.
  • اختلاف انتهای خط (CRLF/LF) در ویندوز: core.autocrlf را در Git تنظیم کنید یا قوانین فرمت‌کننده را هماهنگ کنید.
  • یافت نشدن ابزارها: مطمئن شوید مسیر ابزارها در PATH است؛ در Python از virtualenv/venv فعال استفاده کنید.
  • کندی اجرا: فقط فایل‌های staged را هدف بگیرید، از lint-staged یا فیلترهای pre-commit استفاده کنید و کارهای سنگین را به pre-push/CI منتقل کنید.
  • نیاز به عبور موقت: از –no-verify فقط در موارد استثنایی استفاده کنید و دلیل را در توضیح commit بنویسید.

ادغام با CI

همان مجموعه چک‌ها را در CI اجرا کنید تا استانداردها قابل اجرا و غیرقابل دور زدن باشند. برای pre-commit از اجرای pre-commit run –all-files استفاده کنید. در JS/TS، اجرای npm run lint و npm test را به pipeline اضافه کنید. شکست هرکدام باید build را متوقف کند.

نکات عملکردی و محدودیت‌ها

  • هوک‌های pre-commit برای بازخورد سریع هستند؛ تحلیل‌های استاتیک سنگین و تست‌های یکپارچه را به pre-push یا CI منتقل کنید.
  • برای تیم‌های بزرگ، پیکربندی اشتراکی بسازید و آن را در قالب (template) مخزن پایه نگه دارید تا همه مخازن منسجم باشند.
  • اگر به اجبار سمت سرور نیاز دارید، از هوک‌های سمت سرور (pre-receive) در کنار هوک‌های محلی استفاده کنید.

گام بعدی چیست؟

یک روش را انتخاب کنید، قوانین حداقلی اما ضروری (فرمت، لینت، جلوگیری از فایل‌های حجیم) را بنویسید، آن را در مخزن اصلی خود فعال کنید و همان فرمان‌ها را به CI اضافه کنید. با این کار، کیفیت کد قبل از هر commit به صورت خودکار تضمین می‌شود.