الخطوات
-
1
افهم النهج الخام للـ hook
كل مستودع Git يحتوي مجلد
.git/hooks/مع سكريبتات نموذجية. أنشئ ملفًا قابلًا للتنفيذ باسمpre-commitهناك وسيشغّله Git قبل كل commit. القيد الجوهري:.git/لا يُضاف إلى الـ commit أبدًا، لذا يعيش الـ hook على جهازك فقط ولا يُشارك مع الفريق.bashls .git/hooks/ # pre-commit.sample commit-msg.sample post-merge.sample ... # أنشئ hook خامًا (ليس النهج الموصى به على المدى الطويل) cp .git/hooks/pre-commit.sample .git/hooks/pre-commit chmod +x .git/hooks/pre-commit -
2
اكتب hook bash خامًا يحجب عند الأخطاء
الـ pre-commit hook سكريبت shell عادي. اخرج بـ
0للسماح بالـ commit؛ اخرج بأي قيمة أخرى لإلغائه. يشغّل هذا المثال ESLint على كل ملف.jsفي الـ staging ويحجب إذا وُجدت أخطاء.bash#!/bin/bash # .git/hooks/pre-commit STAGED_JS=$(git diff --cached --name-only --diff-filter=ACMR | grep '\.js$') if [ -n "$STAGED_JS" ]; then echo "Running ESLint on staged files..." npx eslint $STAGED_JS if [ $? -ne 0 ]; then echo "ESLint failed. Commit aborted." exit 1 fi fi exit 0 -
3
ثبّت Husky لإضافة الـ hooks إلى المستودع
يخزن Husky الـ hooks في مجلد
.husky/الذي يُضاف إلى الـ commit، لذا يحصل عليها كل مطور تلقائيًا عند الـ clone أو الـ pull. إنه المعيار الصناعي لمشاريع Node.js.bashnpm install --save-dev husky # هيّئ Husky (ينشئ .husky/ ويضيف prepare script إلى package.json) npx husky-init # بعد التهيئة، ثبّت الاعتماديات لتشغيل prepare script: npm install -
4
اضبط Husky pre-commit hook
بعد
husky-init، يُنشأ ملف.husky/pre-commitبمحتوى نموذجي. استبدل النموذج بأمرك الفعلي. هذا الملف يُضاف إلى الـ commit ويُشارك مع الفريق كله.bash# .husky/pre-commit #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" npx lint-staged -
5
أضف lint-staged للتحقق من الملفات المُغيَّرة فقط
تشغيل الـ linter على كامل قاعدة الكود عند كل commit بطيء ويُنتج ضجيجًا من ملفات لم تلمسها. يشغّل
lint-stagedالـ linters فقط على الملفات الموجودة فعلًا في الـ staging — مما يُبقي الـ hook سريعًا والتغذية الراجعة ذات صلة.bashnpm install --save-dev lint-staged -
6
اضبط lint-staged في package.json
أضف مفتاح
lint-stagedإلىpackage.json. كل مفتاح نمط glob؛ والقيمة مصفوفة أوامر تُشغَّل على الملفات المطابقة في الـ staging. يعمل Prettier أولًا (تنسيق)، ثم ESLint (فحص الأخطاء).bash// package.json { "lint-staged": { "*.{js,ts,tsx,jsx}": [ "prettier --write", "eslint --fix" ], "*.{css,scss}": [ "prettier --write" ], "*.php": [ "./vendor/bin/phpcs --standard=PSR12" ] } } -
7
اختبر الـ hook وتجاوزه عند الضرورة
ضع في الـ staging ملفًا به خطأ lint وشغّل
git commit— يجب أن يحجبه الـ hook. إذا احتجت تجاوز الـ hook بشكل مشروع (مثلًا commit لملف تصحيح مؤقت أثناء حادثة)، استخدم--no-verify. استخدمه باعتدال؛ جعله عادةً يُفرغ الغرض منه.bash# commit عادي — يعمل الـ hook تلقائيًا git add src/broken.js git commit -m "test: trigger the hook" # ✘ ESLint: 3 errors found. Commit aborted. # تجاوز الـ hook — فقط حين تقصد ذلك فعلًا git commit --no-verify -m "chore: temp debug commit" -
8
أبقِ الـ hook أقل من ثلاث ثوانٍ
سيتجاوز المطورون الـ hook الذي يستغرق أكثر من بضع ثوانٍ. يساعد
lint-stagedبالفعل بتحديد النطاق على الملفات في الـ staging. نصائح إضافية: شغّل الـ linters بالتوازي حيث أمكن، وتجنب فحص الأنواع (type-checking) في الـ pre-commit hook (شغّلtsc --noEmitفي CI بدلًا من ذلك)، وتجنب تثبيت الحزم داخل سكريبت الـ hook.bash# قِس سرعة الـ hook لتأكد أنه سريع time git commit --allow-empty -m "timing test" # إذا كان لا يزال بطيئًا، شغّل فحص أنواع tsc في CI فقط لا في الـ hook: # في CI (.github/workflows/ci.yml): # - run: npx tsc --noEmit
نصائح ومحاذير
- الـ hooks في <code>.git/hooks/</code> لا تُستنسخ ولا تُرفع — استخدم دائمًا Husky (أو أداة مشابهة مثل <code>lefthook</code>) لمشاركة الـ hooks مع فريقك.
- أضف <code>node_modules/.bin</code> إلى PATH داخل سكريبت الـ hook حتى تستطيع استدعاء <code>eslint</code> بدلًا من <code>./node_modules/.bin/eslint</code>.
- لمشاريع PHP بدون Node، استخدم <a href="https://github.com/captainhookphp/captainhook" target="_blank" rel="noopener noreferrer">CaptainHook</a> — يفعل الشيء نفسه بملف إعداد JSON.
- شغّل <code>git stash --include-untracked</code> في بداية الـ hook و<code>git stash pop</code> في نهايته إذا أردت فحص شجرة عمل نظيفة (ليس الملفات في الـ staging فقط).
خاتمة
نهج .git/hooks/pre-commit الخام مناسب للمشاريع الفردية؛ استخدم Husky + lint-staged فور أن يصبح لديك فريق. قاعدة الثلاث ثوانٍ غير قابلة للتفاوض: الـ hook الذي يتجاوزه المطورون لبطئه أسوأ من عدم وجود hook على الإطلاق. أبقِ الـ hook مركّزًا على الفحوصات المحلية السريعة (التنسيق + lint) وادفع الفحوصات البطيئة (فحص الأنواع، مجموعة الاختبارات الكاملة) إلى CI حيث تنتمي.