← Kursa Dön
📄 Text · 30 min

Git Hooks

Giriş — Otomatik Kalite Kontrol

Bir fabrikayı düşün. Üretim bandında her ürün ilerlerken, belirli kontrol noktalarından geçer: boyut doğru mu? Renk tutarlı mı? Kusur var mı? Bu kontroller otomatik — bir sensör tetiklenir, ürün standartlara uyuyorsa geçer, uymuyorsa durur.

Git hooks, Git işlemlerinin belirli noktalarında otomatik çalışan scriptlerdir. Commit öncesinde kodun lint'ten geçmesini, commit mesajının belirli bir formata uymasını, push öncesinde testlerin çalışmasını sağlayabilirsin. Manuel kontrol etmen gerekmez — Git senin yerine kontrol eder.

Bu derste Git'in hook mekanizmasını, yaygın hook türlerini, Husky ile modern hook yönetimini ve lint-staged ile verimli lint akışını öğreneceksin.


Hook Nedir?

Analoji — Kapı Sensörleri

Bir binanın giriş kapısında sensörler var:

  • Giriş öncesi: Kartını okut, kimliğin doğrulanır → geçebilirsin

  • Giriş sonrası: Sistem kaydı tutar, ışıklar yanar

  • Çıkış öncesi: Eşya kontrolü

  • Çıkış sonrası: Kapı otomatik kilitlenir

Git hooks de aynı mantıkta çalışır. Her Git işlemi (commit, push, merge...) öncesinde ve sonrasında tetiklenen "sensörler":

┌──────────────────────────────────────────────────────────┐
│                   GIT HOOK TETİKLEME                      │
├──────────────────────────────────────────────────────────┤
│                                                           │
│  git commit:                                              │
│    pre-commit ──► commit-msg ──► post-commit              │
│       ↑              ↑              ↑                     │
│    Lint/test     Mesaj kontrol   Bildirim                  │
│                                                           │
│  git push:                                                │
│    pre-push ──► (server tarafı) ──► post-receive          │
│       ↑                                  ↑                │
│    Test çalıştır                    Deploy tetikle         │
│                                                           │
│  git merge:                                               │
│    pre-merge-commit ──► post-merge                        │
│                              ↑                            │
│                         npm install                        │
│                                                           │
└──────────────────────────────────────────────────────────┘

Hook'lar Nerede?

ls .git/hooks/
# applypatch-msg.sample     pre-commit.sample
# commit-msg.sample         pre-merge-commit.sample
# fsmonitor-watchman.sample pre-push.sample
# post-update.sample        pre-rebase.sample
# pre-applypatch.sample     prepare-commit-msg.sample
# pre-auto-gc.sample        push-to-checkout.sample

# .sample uzantılı dosyalar → aktif DEĞİL
# .sample'ı kaldırınca aktif olur

Client-Side Hooks (İstemci Tarafı)

pre-commit — Commit Öncesi Kontrol

En çok kullanılan hook. Commit yapılmadan hemen önce çalışır. Hook 0 dışı bir değer döndürürse commit iptal edilir.

# .git/hooks/pre-commit dosyası oluştur
cat > .git/hooks/pre-commit << 'HOOK'
#!/bin/sh
# Pre-commit hook: Lint ve format kontrolü

echo "🔍 Pre-commit kontrolleri çalışıyor..."

# 1. ESLint kontrolü
echo "📋 Lint kontrolü..."
npx eslint --quiet .
if [ $? -ne 0 ]; then
    echo "❌ Lint hataları var! Düzeltip tekrar commit edin."
    exit 1
fi

# 2. Prettier format kontrolü
echo "🎨 Format kontrolü..."
npx prettier --check .
if [ $? -ne 0 ]; then
    echo "❌ Format hataları var! 'npm run format' çalıştırın."
    exit 1
fi

# 3. Hassas bilgi kontrolü
echo "🔒 Hassas bilgi kontrolü..."
if git diff --cached --name-only | xargs grep -l "API_KEY\|SECRET\|PASSWORD" 2>/dev/null; then
    echo "❌ Hassas bilgi tespit edildi! Commit iptal."
    exit 1
fi

echo "✅ Tüm kontroller geçti!"
exit 0
HOOK

chmod +x .git/hooks/pre-commit
# Test et:
git add .
git commit -m "Test commit"
# 🔍 Pre-commit kontrolleri çalışıyor...
# 📋 Lint kontrolü...
# ❌ Lint hataları var! Düzeltip tekrar commit edin.

# Hook'u atlamak (acil durumlarda):
git commit --no-verify -m "Emergency fix"
# veya
git commit -n -m "Emergency fix"

commit-msg — Commit Mesajı Kontrolü

Commit mesajının belirli bir formata uymasını zorunlu kılar:

cat > .git/hooks/commit-msg << 'HOOK'
#!/bin/sh
# Commit mesajı format kontrolü: Conventional Commits

commit_msg_file=$1
commit_msg=$(cat "$commit_msg_file")

# Conventional Commits pattern:
# type(scope): description
# type: feat, fix, docs, style, refactor, test, chore, ci, perf, build
pattern="^(feat|fix|docs|style|refactor|test|chore|ci|perf|build)(\(.+\))?: .{1,}"

if ! echo "$commit_msg" | grep -qE "$pattern"; then
    echo "❌ Commit mesajı Conventional Commits formatına uymuyor!"
    echo ""
    echo "Doğru format: <type>(<scope>): <description>"
    echo ""
    echo "Örnekler:"
    echo "  feat: Add user authentication"
    echo "  fix(auth): Resolve login redirect issue"
    echo "  docs: Update API documentation"
    echo "  refactor(core): Simplify error handling"
    echo ""
    echo "Geçerli tipler: feat, fix, docs, style, refactor, test, chore, ci, perf, build"
    exit 1
fi

# Mesaj uzunluğu kontrolü
first_line=$(echo "$commit_msg" | head -1)
if [ ${#first_line} -gt 72 ]; then
    echo "❌ Commit mesajının ilk satırı 72 karakterden uzun!"
    echo "   Mevcut: ${#first_line} karakter"
    exit 1
fi

echo "✅ Commit mesajı formatı doğru!"
exit 0
HOOK

chmod +x .git/hooks/commit-msg

prepare-commit-msg — Mesaj Hazırlama

Commit mesajı editörde açılmadan önce çalışır. Otomatik mesaj ekleme için:

cat > .git/hooks/prepare-commit-msg << 'HOOK'
#!/bin/sh
# Branch adından issue numarasını çıkar ve commit mesajına ekle

commit_msg_file=$1
branch_name=$(git symbolic-ref --short HEAD)

# Branch adı "feature/PROJ-123-login" formatındaysa
issue_id=$(echo "$branch_name" | grep -oE '[A-Z]+-[0-9]+')

if [ -n "$issue_id" ]; then
    # Mesajın sonuna issue referansı ekle
    echo "" >> "$commit_msg_file"
    echo "Ref: $issue_id" >> "$commit_msg_file"
fi
HOOK

chmod +x .git/hooks/prepare-commit-msg

pre-push — Push Öncesi Kontrol

cat > .git/hooks/pre-push << 'HOOK'
#!/bin/sh
# Push öncesi test çalıştır

echo "🧪 Push öncesi testler çalışıyor..."

npm test
if [ $? -ne 0 ]; then
    echo "❌ Testler başarısız! Push iptal."
    exit 1
fi

echo "✅ Testler geçti, push devam ediyor..."
exit 0
HOOK

chmod +x .git/hooks/pre-push

post-merge — Merge Sonrası

cat > .git/hooks/post-merge << 'HOOK'
#!/bin/sh
# Merge sonrası bağımlılıkları güncelle

# package.json değişti mi?
changed_files=$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)

if echo "$changed_files" | grep -q "package.json"; then
    echo "📦 package.json değişti, npm install çalıştırılıyor..."
    npm install
fi

if echo "$changed_files" | grep -q "requirements.txt"; then
    echo "🐍 requirements.txt değişti, pip install çalıştırılıyor..."
    pip install -r requirements.txt
fi
HOOK

chmod +x .git/hooks/post-merge

post-checkout — Branch Değiştirdikten Sonra

cat > .git/hooks/post-checkout << 'HOOK'
#!/bin/sh
# Branch değiştirince .env dosyasını kontrol et

prev_head=$1
new_head=$2
branch_checkout=$3

# Sadece branch değişikliğinde çalış (dosya checkout değil)
if [ "$branch_checkout" = "1" ]; then
    if [ ! -f .env ]; then
        echo "⚠️ .env dosyası yok! .env.example'dan kopyala:"
        echo "   cp .env.example .env"
    fi
fi
HOOK

chmod +x .git/hooks/post-checkout

Server-Side Hooks (Sunucu Tarafı)

Server-side hook'lar remote repository'de çalışır. GitHub/GitLab gibi platformlarda bunlar genellikle platform ayarlarıyla yönetilir.

┌─────────────────┬────────────────────────────────────────┐
│ Hook            │ Kullanım                                │
├─────────────────┼────────────────────────────────────────┤
│ pre-receive     │ Push kabul edilmeden önce çalışır       │
│                 │ Politika kontrolü, format kontrolü      │
│                 │ Reddederse push başarısız olur          │
├─────────────────┼────────────────────────────────────────┤
│ update          │ Her branch güncellemesi için ayrı çalışır│
│                 │ Branch bazlı izin kontrolü              │
├─────────────────┼────────────────────────────────────────┤
│ post-receive    │ Push kabul edildikten sonra çalışır     │
│                 │ CI/CD tetikleme, bildirim, deploy       │
│                 │ Reddetme şansı yok (bilgi amaçlı)      │
└─────────────────┴────────────────────────────────────────┘

💡 İpucu: GitHub'da server-side hook'ları doğrudan yazamazsın. Bunun yerine GitHub Actions, branch protection rules ve webhooks kullanırsın. GitLab ise self-hosted sürümde server hook'larını destekler.


Hook'ların Sorunu — Paylaşım

.git/hooks/ dizini Git tarafından track edilmez. Yani hook'lar repo ile paylaşılmaz. Her geliştirici kendi hook'larını kurmalıdır.

❌ Sorun:
  - Ahmet hook'u kurdu, her commit'te lint çalışıyor ✅
  - Ayşe hook'u kurmadı, lint hatalarıyla commit ediyor ❌
  - Ali hook'un varlığından habersiz

Bu, ekip genelinde tutarsızlığa yol açar.

Çözüm Yolları

1. core.hooksPath ile paylaşılan dizin kullan
2. Husky (Node.js projeler için) ← En yaygın
3. pre-commit (Python projeler için)
4. lefthook (Go tabanlı, dil bağımsız)

core.hooksPath

# Hook'ları repo'nun içinde bir dizine koy
mkdir -p .githooks
cp .git/hooks/pre-commit .githooks/pre-commit

# Tüm ekip için hook dizinini ayarla
git config core.hooksPath .githooks

# .githooks dizini repo ile paylaşılır!
git add .githooks/
git commit -m "Add shared Git hooks"

Husky — Modern Hook Yönetimi

Husky, Node.js projelerinde Git hook'larını yönetmenin en popüler yoludur. npm install yaptığında hook'lar otomatik kurulur — ekipteki herkes aynı hook'lara sahip olur.

Husky Kurulumu

# 1. Husky'yi kur
npm install --save-dev husky

# 2. Husky'yi başlat
npx husky init

# Bu komut:
# - .husky/ dizinini oluşturur
# - package.json'a "prepare" scripti ekler
# - Örnek pre-commit hook'u oluşturur
Proje yapısı:
my-project/
├── .husky/
│   ├── _/              ← Husky internal
│   │   ├── .gitignore
│   │   └── husky.sh
│   └── pre-commit      ← Hook dosyası (düzenle!)
├── package.json
└── ...

Husky Hook'ları Tanımlama

# pre-commit hook'u
cat > .husky/pre-commit << 'HOOK'
npm run lint
npm test
HOOK

# commit-msg hook'u
cat > .husky/commit-msg << 'HOOK'
npx --no -- commitlint --edit $1
HOOK

# pre-push hook'u
cat > .husky/pre-push << 'HOOK'
npm run build
npm run test:e2e
HOOK

package.json Entegrasyonu

{
  "name": "my-project",
  "scripts": {
    "prepare": "husky",
    "lint": "eslint .",
    "format": "prettier --write .",
    "format:check": "prettier --check .",
    "test": "jest",
    "build": "tsc"
  },
  "devDependencies": {
    "husky": "^9.0.0",
    "eslint": "^8.0.0",
    "prettier": "^3.0.0",
    "jest": "^29.0.0"
  }
}

Akış:

Yeni ekip üyesi:
1. git clone ...
2. npm install
   → "prepare" scripti çalışır
   → husky hook'ları otomatik kurulur ✅
3. git commit ...
   → pre-commit hook çalışır
   → lint + test otomatik çalışır

lint-staged — Sadece Değişen Dosyaları Kontrol Et

Büyük bir projede eslint . komutu tüm dosyaları kontrol eder — bu dakikalar sürebilir. Ama sen sadece 2 dosya değiştirdin. Tüm projeyi lint etmek gereksiz.

lint-staged, sadece staged (git add edilmiş) dosyaları kontrol eder. Hızlı ve verimli.

Kurulum

npm install --save-dev lint-staged

Yapılandırma

// package.json'a ekle:
{
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{css,scss}": [
      "prettier --write"
    ],
    "*.md": [
      "prettier --write"
    ],
    "*.json": [
      "prettier --write"
    ]
  }
}

Veya ayrı bir .lintstagedrc.json dosyası:

{
  "*.{js,ts}": ["eslint --fix", "prettier --write"],
  "*.css": ["stylelint --fix", "prettier --write"],
  "*.md": ["prettier --write"]
}

Husky + lint-staged Birlikte

# .husky/pre-commit
npx lint-staged
Akış:
1. git add src/auth.js src/login.js
2. git commit -m "feat: Update auth"
3. pre-commit hook tetiklenir
4. lint-staged çalışır
5. SADECE src/auth.js ve src/login.js lint edilir
6. Hata varsa → commit iptal
7. Hata yoksa → commit başarılı ✅

Tüm projeyi lint etmek: 45 saniye ⏱️
Sadece değişen dosyaları: 2 saniye ⚡

lint-staged Terminal Çıktısı

✔ Preparing lint-staged...
✔ Running tasks for staged files...
  ✔ Running tasks for *.{js,ts}
    ✔ eslint --fix
    ✔ prettier --write
  ✔ Running tasks for *.css
    ✔ prettier --write
✔ Applying modifications from tasks...
✔ Cleaning up temporary files...

Commitlint — Commit Mesajı Standardı

Commitlint, commit mesajlarının belirli bir standarda uymasını zorunlu kılar:

# Kurulum
npm install --save-dev @commitlint/cli @commitlint/config-conventional

# Yapılandırma dosyası
cat > commitlint.config.js << 'EOF'
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [2, 'always', [
      'feat', 'fix', 'docs', 'style', 'refactor',
      'test', 'chore', 'ci', 'perf', 'build', 'revert'
    ]],
    'subject-max-length': [2, 'always', 72],
    'body-max-line-length': [2, 'always', 100],
  }
};
EOF

# Husky hook'u
echo 'npx --no -- commitlint --edit $1' > .husky/commit-msg
# Test:
git commit -m "fixed stuff"
# ❌ ✖   subject may not be empty [subject-empty]
#    ✖   type may not be empty [type-empty]

git commit -m "fix: Resolve login redirect issue"
# ✅ Commit başarılı!

Tam Kurulum: Profesyonel Hook Pipeline

# 1. Gerekli paketleri kur
npm install --save-dev \
  husky \
  lint-staged \
  @commitlint/cli \
  @commitlint/config-conventional \
  eslint \
  prettier

# 2. Husky'yi başlat
npx husky init

# 3. pre-commit hook (lint-staged)
echo 'npx lint-staged' > .husky/pre-commit

# 4. commit-msg hook (commitlint)
echo 'npx --no -- commitlint --edit $1' > .husky/commit-msg

# 5. package.json ayarları
{
  "scripts": {
    "prepare": "husky",
    "lint": "eslint .",
    "lint:fix": "eslint --fix .",
    "format": "prettier --write .",
    "test": "jest"
  },
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{css,scss,md,json,yml,yaml}": [
      "prettier --write"
    ]
  }
}
# 6. commitlint yapılandırması
cat > commitlint.config.js << 'EOF'
module.exports = {
  extends: ['@commitlint/config-conventional']
};
EOF

# 7. Commit et
git add .
git commit -m "chore: Setup linting and commit hooks"

Pipeline Diyagramı

Developer: git commit -m "feat: Add login"
     │
     ▼
pre-commit hook
     │
     ├── lint-staged
     │   ├── eslint --fix (sadece değişen .js/.ts dosyaları)
     │   ├── prettier --write (format düzelt)
     │   └── Hata varsa? → ❌ Commit iptal
     │
     ▼
commit-msg hook
     │
     ├── commitlint
     │   ├── Mesaj formatı doğru mu? (Conventional Commits)
     │   └── Hata varsa? → ❌ Commit iptal
     │
     ▼
✅ Commit başarılı!
     │
     ▼
git push
     │
     ▼
pre-push hook (opsiyonel)
     │
     ├── npm test (birim testleri)
     ├── npm run build (build kontrolü)
     └── Hata varsa? → ❌ Push iptal

Yaygın Hatalar

1. Hook'u Atlamak

# ❌ Acil diye her zaman --no-verify kullanmak
git commit --no-verify -m "quick fix"

# Bu, hook'ların amacını ortadan kaldırır!

# ✅ Gerçekten acil mi? O zaman:
# 1. --no-verify kullan
# 2. Sonra düzelt ve temiz commit yap
# 3. Ekibe bildir

2. Yavaş Hook'lar

# ❌ pre-commit'te tüm projeyi lint + test etmek
# 5 dakika süren commit → kimse commit yapmak istemez!

# ✅ lint-staged ile sadece değişen dosyaları lint et
# ✅ Ağır testleri pre-push veya CI'a bırak
# ✅ pre-commit: hızlı kontroller (lint, format)
#    pre-push: orta kontroller (unit test)
#    CI: ağır kontroller (integration test, e2e)

3. Hook'ları Commit Etmemek

# ❌ Hook'lar sadece .git/hooks/'ta — paylaşılmıyor
# Yeni ekip üyesi hook'suz çalışıyor

# ✅ Husky kullan — npm install ile otomatik kurulum
# veya core.hooksPath ile paylaşılan dizin kullan

Özet

Bu derste Git hook'larının tüm boyutlarını öğrendik:

  • Git hooks, Git işlemlerinin belirli noktalarında otomatik çalışan scriptlerdir — commit öncesi lint, mesaj formatı kontrolü, push öncesi test gibi kalite kontrolleri otomatize eder

  • Client-side hooks (pre-commit, commit-msg, pre-push) geliştirici makinesinde çalışır; server-side hooks (pre-receive, post-receive) remote'ta çalışır

  • Husky Node.js projelerinde hook yönetiminin standart aracıdır — npm install ile hook'lar otomatik kurulur, tüm ekip aynı hook'lara sahip olur

  • lint-staged sadece staged dosyaları kontrol eder — tüm projeyi lint etmek yerine saniyeler içinde biter

  • Commitlint commit mesajlarının Conventional Commits standardına uymasını zorunlu kılar

  • pre-commit → commit-msg → pre-push pipeline'ı ile aşamalı kalite kontrol uygula: hızlı kontrolleri commit'te, ağır kontrolleri push'ta veya CI'da yap

Bir sonraki bölümde GitHub'ın CI/CD platformunu göreceğiz: GitHub Actions — push yaptığında otomatik test, build ve deploy!