git hook — commit, push oldidan avtomatik ishlar bajarish

~20 daq · Git · Ilg'or Git

Muammodan boshlaylik

PR ochdingiz. 2 daqiqadan keyin CI (38-dars) qizil:

  • ESLint: 3 ta xato
  • Prettier: 12 ta fayl formatlanmagan
  • Test buzilgan — console.log qolib ketgan

Tuzatish, push, yana kutish. Kuniga bir necha marta. Bu xatolarning hammasini commit qilish paytidayoq, lokalda, bir soniyada ushlash mumkin edi.

Git hooks — Git hodisalari (commit, push, merge) oldidan yoki keyin avtomatik ishga tushadigan skriptlar.


Hook nima?

Har bir repozitoriyda .git/hooks/ papkasi bor (5-dars):

ls .git/hooks
applypatch-msg.sample     pre-commit.sample      pre-push.sample
commit-msg.sample         pre-rebase.sample      prepare-commit-msg.sample
post-update.sample        pre-merge-commit.sample  ...

Bular — namunalar. .sample ni olib tashlab, faylni bajariladigan qilsangiz — hook faol bo'ladi.

Asosiy hook lar

Hook Qachon Tipik foydalanish
pre-commit Commit yaratilishidan oldin Lint, formatlash, sirlarni qidirish
commit-msg Xabar yozilgandan keyin Xabar formatini tekshirish (34-dars)
prepare-commit-msg Muharrir ochilishidan oldin Xabarga branch dagi issue raqamini qo'shish
pre-push Push dan oldin Testlar
post-merge Merge (va pull) dan keyin package-lock.json o'zgargan bo'lsa — npm install
post-checkout Branch almashtirilgandan keyin Xuddi shu

Qoida: pre-* hook 0 dan farqli kod bilan chiqsa — Git harakatni bekor qiladi (Linux kursi, 38-dars: chiqish kodlari).


Birinchi hook — qo'lda

console.log qolgan commitlarni taqiqlash:

cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
# Staging dagi JS fayllarda console.log bormi?
if git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|ts|jsx|tsx)
#39; \ | xargs -r grep -n "console\.log" ; then echo "" echo "❌ Commit to'xtatildi: console.log topildi (yuqorida)." echo " Ataylab bo'lsa: git commit --no-verify" exit 1 fi exit 0 EOF chmod +x .git/hooks/pre-commit
git commit -m "feat: savat"
src/savat.js:42:  console.log('DEBUG', items);

❌ Commit to'xtatildi: console.log topildi (yuqorida).
  • git diff --cached --name-only — faqat staged fayllar
  • --diff-filter=ACM — qo'shilgan, nusxalangan, o'zgargan (o'chirilganlar emas)
  • xargs -r — fayl bo'lmasa grep ni ishga tushirmaslik (Linux kursi, 15-dars)

Hook har qanday tilda bo'lishi mumkin — shebang ga qarab (#!/usr/bin/env node, #!/usr/bin/env python3).


Muammo: .git/hooks commit qilinmaydi

.git/ ichidagi hech narsa repozitoriyga tushmaydi. Hook ni yozdingiz — u faqat sizda ishlaydi. Hamkasblar clone qilganda — hook yo'q.

Yechim 1: core.hooksPath

Hook larni oddiy papkada saqlash va Git ga ko'rsatish:

mkdir .githooks
mv .git/hooks/pre-commit .githooks/
git config core.hooksPath .githooks
git add .githooks && git commit -m "chore: git hook lar"

Har bir dasturchi clone dan keyin bir marta git config core.hooksPath .githooks qilishi kerak (README da yozing yoki npm skriptiga qo'shing).

Yechim 2: Husky (JavaScript loyihalari)

Eng ommabop vosita — npm install paytida hook larni avtomatik sozlaydi:

npm install -D husky
npx husky init

Bu .husky/pre-commit faylini yaratadi va package.json ga "prepare": "husky" skriptini qo'shadi — endi har bir npm install hook larni avtomatik o'rnatadi.

# .husky/pre-commit
npx lint-staged
# .husky/commit-msg
npx --no -- commitlint --edit "$1"
# .husky/pre-push
npm test

lint-staged — faqat o'zgargan fayllar

Butun loyihani har commit da lint qilish — sekin. lint-staged faqat staged fayllarni tekshiradi va avtomatik tuzatadi:

// package.json
{
  "lint-staged": {
    "*.{js,ts,jsx,tsx}": ["eslint --fix", "prettier --write"],
    "*.{css,md,json}": "prettier --write"
  }
}
npm install -D lint-staged
$ git commit -m "feat: savat"
✔ Preparing lint-staged...
✔ Running tasks for staged files...
  ✔ *.{js,ts,jsx,tsx} — 3 files
    ✔ eslint --fix
    ✔ prettier --write
✔ Applying modifications from tasks...
[feature/savat a1b2c3d] feat: savat

Formatlash avtomatik tuzatildi va commit ga kiritildi. Xato bo'lsa — commit to'xtaydi.

Boshqa tillar uchun

  • pre-commit (Python, lekin har qanday til uchun): .pre-commit-config.yaml
  • lefthook — tez, tilga bog'liq emas
  • PHP — CaptainHook, GrumPHP

Foydali hook g'oyalari

# post-merge: bog'liqliklar o'zgargan bo'lsa — eslatish yoki o'rnatish
#!/bin/sh
if git diff --name-only HEAD@{1} HEAD | grep -q "package-lock.json"; then
  echo "📦 package-lock.json o'zgardi — npm install bajarilmoqda..."
  npm install
fi
# prepare-commit-msg: branch nomidagi issue raqamini xabarga qo'shish
#!/bin/sh
BRANCH=$(git symbolic-ref --short HEAD)
ISSUE=$(echo "$BRANCH" | grep -oE '[0-9]+' | head -1)
if [ -n "$ISSUE" ] && ! grep -q "#$ISSUE" "$1"; then
  printf '\n\nRefs #%s\n' "$ISSUE" >> "$1"
fi

feature/87-qidiruv branch ida → xabar oxiriga Refs #87 (25-dars).


Hook ni chetlab o'tish

git commit --no-verify -m "..."      # pre-commit va commit-msg ishlamaydi
git push --no-verify

[!WARNING] Lokal hook lar — qulaylik, xavfsizlik emas. Har kim --no-verify qila oladi yoki hook larni umuman o'rnatmaydi. Shuning uchun haqiqiy tekshiruvlar — CI da (38-dars) va branch protection da (23-dars). Hook — xatolarni erta va tez ushlash uchun; CI — ularni main ga o'tkazmaslik uchun. Ikkalasi ham kerak.

Hook larni tez saqlang

Sekin hook (masalan, har commit da 2 daqiqalik barcha testlar) — dasturchilarni --no-verify ga majbur qiladi. Qoida:

  • pre-commit — bir necha soniya: faqat staged fayllar, lint, formatlash
  • pre-push — bir daqiqagacha: tez testlar
  • To'liq test to'plami — CI da

Server tomonidagi hook lar

pre-receive, update, post-receive — serverda (push qabul qiluvchi repozitoriyda) ishlaydi. Ularni chetlab o'tib bo'lmaydi. O'z Git serveringizda (49-dars) — masalan, post-receive bilan push dan keyin avtomatik deploy. GitHub da ularning o'rnini Actions va branch protection / rulesets egallaydi.


Nima uchun shunday?

Nima uchun tekshiruvlarni CI ga qo'shimcha ravishda lokal hook larda ham bajarish kerak — takrorlanish emasmi?

Takrorlanish — lekin turli vaqtda va turli narxda. Hook xatoni commit paytida ushlaydi: 2 soniya, kontekst hali boshingizda, tuzatish — darhol. CI esa uni push dan bir necha daqiqa keyin ushlaydi: siz allaqachon boshqa ishga o'tgansiz, PR qizil, reviewer kutyapti, yana bir "fix lint" commit kerak. Xatoni qanchalik erta ushlasangiz — u shunchalik arzon. Bu "shift left" tamoyili: tekshiruvlarni jarayonning boshiga — dasturchiga eng yaqin joyga — ko'chirish. CI esa xavfsizlik to'ri bo'lib qoladi: hook o'tkazib yuborgan narsa main ga baribir o'tmaydi.


Xulosa

Eslab qoling:

  • Hook — Git hodisasi oldidan/keyin avtomatik skript; .git/hooks/
  • pre-commit, commit-msg, pre-push — eng muhimlari; 0 dan farqli chiqish — harakat bekor
  • .git/hooks commit qilinmaydi → core.hooksPath .githooks yoki Husky
  • Husky + lint-staged — faqat staged fayllarni lint va formatlash
  • Husky + commitlint — Conventional Commits tekshiruvi
  • --no-verify — chetlab o'tish; shuning uchun haqiqiy nazorat — CI da
  • Hook lar tez bo'lsin: pre-commit — soniyalar

Keyingi dars

48-dars: git archive — loyihani arxivlash