Docker & DevOps

    Git Hooks ile Otomasyon

    Git kancalarıyla commit ve push öncesi otomatik denetim kurma ve sunucuya otomatik dağıtım rehberi.

    12 dk okuma Güncellendi: 25 Ağustos 2026

    Ekipteki herkesin "commit'ten önce testleri çalıştır", "syntax hatası olan dosyayı gönderme", "commit mesajını şu formatta yaz" kurallarını akılda tutmasını beklemek işe yaramaz. İnsanlar unutur, acelesi olan atlar, yeni gelen kuralı hiç duymaz. Git hooks tam olarak bu boşluğu doldurmak için vardır: Git'in belirli anlarda otomatik çalıştırdığı, sizin yazdığınız küçük betiklerdir. Bir kanca yerine oturduğunda kural artık bir wiki sayfasında değil, deponun içinde yaşar ve kendi kendini uygular.

    Bu rehberde git hooks mantığını sıfırdan kuracağız: kancaların nerede durduğunu, hangi olayda hangisinin tetiklendiğini, pre-commit ile söz dizimi denetimi, commit-msg ile mesaj formatı kontrolü, pre-push ile test kapısı ve sunucu tarafında post-receive ile "push ettiğim anda canlıya çıksın" tipi dağıtım kurmayı adım adım göstereceğim. Ayrıca kancaların klonlanmadığı gerçeğinden doğan klasik tuzağı ve bunu core.hooksPath ile nasıl çözeceğinizi anlatacağım.

    Git Hooks Nedir ve Nerede Yaşar#

    Git hooks, Git'in yaşam döngüsündeki belirli olaylara bağlanmış çalıştırılabilir dosyalardır. Bir depoyu ilk kez oluşturduğunuzda Git, .git/hooks/ dizinine bir sürü örnek dosya bırakır. Bunlar .sample uzantılıdır ve bu uzantı yüzünden çalışmazlar; uzantıyı kaldırıp dosyaya çalıştırma izni verdiğiniz anda kanca aktif hâle gelir. Kancanın hangi dilde yazıldığı Git'i ilgilendirmez; dosyanın ilk satırındaki shebang neyi işaret ediyorsa onunla çalıştırılır. Bash, Python, Node, hatta derlenmiş bir ikili dosya bile olabilir.

    Kancaların en önemli ortak davranışı çıkış kodudur: bir kanca 0 ile çıkarsa işlem devam eder, sıfırdan farklı bir kodla çıkarsa Git işlemi iptal eder. pre-commit betiğiniz exit 1 derse commit oluşmaz. Bu basit sözleşme, kancaları bir kalite kapısına dönüştüren şeydir.

    # Mevcut örnek kancaları görelim
    ls -1 .git/hooks/
    # applypatch-msg.sample
    # commit-msg.sample
    # post-update.sample
    # pre-commit.sample
    # pre-push.sample
    # pre-rebase.sample
    # ...
    
    # Bir kancayı aktif etmek: .sample uzantısını kaldır + çalıştırılabilir yap
    cp .git/hooks/pre-commit.sample .git/hooks/pre-commit
    chmod +x .git/hooks/pre-commit
    

    Burada hemen bilmeniz gereken kritik bir gerçek var: .git/hooks/ dizini versiyon kontrolüne dahil değildir. Yani siz mükemmel bir kanca yazıp commit etseniz bile, depoyu klonlayan arkadaşınızın makinesinde o kanca olmaz. Bu, git hooks konusundaki bir numaralı hayal kırıklığıdır ve çözümünü aşağıda ayrı bir bölümde ele alacağız.

    Hangi Olayda Hangi Kanca Çalışır#

    Git'in tanıdığı yirmiden fazla kanca vardır ama pratikte işinizi gören beş altı tanesidir. Aşağıdaki tablo günlük hayatta gerçekten kullandıklarımı, tetiklendikleri anı ve iptal yetkisi olup olmadığını özetliyor.

    KancaNerede çalışırNe zaman tetiklenirİşlemi iptal edebilir mi
    pre-commitİstemciCommit mesajı sorulmadan hemen önceEvet
    prepare-commit-msgİstemciMesaj editörü açılmadan önceEvet
    commit-msgİstemciMesaj yazıldıktan sonra, commit yazılmadan önceEvet
    post-commitİstemciCommit oluştuktan sonraHayır
    pre-pushİstemciNesneler karşıya gönderilmeden önceEvet
    pre-receiveSunucuPush alındı, referanslar güncellenmeden önceEvet
    updateSunucuHer referans için ayrı ayrıEvet (o referans için)
    post-receiveSunucuReferanslar güncellendikten sonraHayır

    Ayrım basit: istemci tarafı kancalar geliştiricinin kendi makinesinde çalışır ve öneri niteliğindedir çünkü --no-verify bayrağıyla atlanabilirler. Sunucu tarafı kancalar ise merkezî depoda çalışır, atlanamaz ve bu yüzden gerçek zorlayıcı kurallar oradadır. İyi bir düzen, ikisini birlikte kullanır: istemcide hızlı geri bildirim, sunucuda son söz.

    pre-commit ile commit-msg arasındaki sıralamayı da aklınızda tutun. Önce pre-commit çalışır ve içeriğe bakar; siz henüz mesaj bile yazmamışsınızdır. Mesajı yazıp editörü kapattığınızda commit-msg devreye girer ve mesaj dosyasının yolunu ilk argüman olarak alır. Bu iki kancayı karıştırmak, "mesajı kontrol etmeye çalışıyorum ama dosya boş geliyor" tipi kafa karışıklığının ana kaynağıdır.

    pre-commit ile Söz Dizimi ve Sır Denetimi#

    İlk gerçek kancamızı yazalım. Amaç şu: commit'e dahil edilen PHP ve JSON dosyalarının söz dizimini doğrula, ayrıca içeride API anahtarı benzeri bir şey varsa commit'i durdur. Dikkat edilecek nokta, yalnızca staged (indekse alınmış) dosyalara bakmaktır; çalışma dizinindeki henüz eklenmemiş dosyalar sizi ilgilendirmez.

    #!/usr/bin/env bash
    # .git/hooks/pre-commit
    set -euo pipefail
    
    # Sadece indekse alınmış, silinmemiş dosyaları al
    FILES=$(git diff --cached --name-only --diff-filter=ACM)
    [ -z "$FILES" ] && exit 0
    
    FAIL=0
    
    for f in $FILES; do
      case "$f" in
        *.php)
          # php -l sadece söz dizimi kontrolü yapar, kodu çalıştırmaz
          php -l "$f" > /dev/null || FAIL=1
          ;;
        *.json)
          python3 -m json.tool "$f" > /dev/null 2>&1 || {
            echo "Geçersiz JSON: $f"; FAIL=1;
          }
          ;;
      esac
    done
    
    # Basit sır taraması: uzun hex/base64 benzeri atamalar
    if git diff --cached | grep -nE '(api[_-]?key|secret|password)\s*=\s*.{16,}' ; then
      echo "Sızma riski: yukarıdaki satırda sabit kodlanmış bir sır olabilir."
      FAIL=1
    fi
    
    exit $FAIL
    

    Bu betiği .git/hooks/pre-commit olarak kaydedip chmod +x verdiğinizde, bozuk bir PHP dosyasını commit etmeye çalıştığınızda Git size hatayı gösterir ve commit oluşmaz. Sır taraması kısmı özellikle değerlidir: bir .env dosyasının ya da sabit kodlanmış bir anahtarın depoya girmesi, sonradan geçmişten temizlenmesi çok zahmetli bir kazadır. Bu konunun ne kadar ciddi olabileceğini merak ediyorsanız .git klasörü ve .env dosyası ifşası yazısı gerçek sonuçlarını anlatıyor.

    Performans konusunda bir uyarı: pre-commit her commit'te çalışır, dolayısıyla saniyeler süren işler koymayın. Tüm test paketini burada çalıştırmak yerine yalnızca değişen dosyalara dokunan hızlı denetimleri koyun; ağır testler pre-push kancasına aittir.

    commit-msg ile Mesaj Formatını Zorlamak#

    Ekip olarak bir commit mesajı standardı benimsediyseniz, bunu insan disiplinine bırakmak yerine makineye yaptırın. commit-msg kancası, mesajın yazıldığı geçici dosyanın yolunu ilk argüman olarak alır; siz o dosyayı okur, kurala uymuyorsa sıfırdan farklı kodla çıkarsınız.

    #!/usr/bin/env bash
    # .git/hooks/commit-msg
    MSG_FILE="$1"
    FIRST_LINE=$(head -n 1 "$MSG_FILE")
    
    # Merge ve revert commit'lerini muaf tut
    case "$FIRST_LINE" in
      Merge*|Revert*) exit 0 ;;
    esac
    
    # tip(kapsam): açıklama  — kapsam isteğe bağlı
    PATTERN='^(feat|fix|docs|style|refactor|perf|test|build|ci|chore)(\([a-z0-9._-]+\))?!?: .{1,72}$'
    
    if ! echo "$FIRST_LINE" | grep -qE "$PATTERN"; then
      echo "Commit mesajı formatı hatalı."
      echo "Beklenen: tip(kapsam): kısa açıklama"
      echo "Örnek   : fix(auth): oturum süresi yenilenmiyordu"
      exit 1
    fi
    exit 0
    

    Buradaki tip listesi rastgele seçilmedi; Conventional Commits standardından geliyor ve sürüm numaralarını otomatik üretmenin de temelini oluşturuyor. Neden bu kelimeler ve otomatik sürümlemeye nasıl bağlandıkları için Conventional Commits ve Semantic Versioning yazısına bakın. Dal isimlendirmeyi de aynı disiplinle ele almak istiyorsanız Git branch stratejileri yazısı iyi bir tamamlayıcıdır.

    Bir de pre-push örneği ekleyelim; bu kanca, nesneler uzak depoya gitmeden hemen önce çalışır ve ağır denetimler için doğru yerdir:

    #!/usr/bin/env bash
    # .git/hooks/pre-push
    # Ana dala doğrudan push'u engelle
    while read -r local_ref local_sha remote_ref remote_sha; do
      if [ "$remote_ref" = "refs/heads/main" ]; then
        echo "main dalına doğrudan push kapalı. Pull request açın."
        exit 1
      fi
    done
    exit 0
    

    Sunucu Tarafı: post-receive ile Otomatik Dağıtım#

    Asıl gücü sunucu tarafında görürsünüz. Sunucunuzda çıplak (bare) bir depo tutup, geliştiricilerin oraya push etmesini ve push'un ardından kodun otomatik olarak yayın dizinine açılmasını sağlayabilirsiniz. Bu, küçük ve orta ölçekli projeler için kurulumu on dakika süren, gayet sağlam bir dağıtım yöntemidir.

    Önce sunucuda çıplak depoyu oluşturun:

    # Sunucuda (VDS ya da sanal sunucu)
    mkdir -p /var/repo/site.git
    cd /var/repo/site.git
    git init --bare
    
    # Yayın dizini
    mkdir -p /var/www/firmaniz.com
    

    Sonra post-receive kancasını yazın. Çıplak depoda çalışma dizini olmadığı için --work-tree parametresiyle dosyaları hedef dizine açarız:

    #!/usr/bin/env bash
    # /var/repo/site.git/hooks/post-receive
    set -euo pipefail
    
    TARGET="/var/www/firmaniz.com"
    GIT_DIR="/var/repo/site.git"
    BRANCH="main"
    
    while read -r oldrev newrev ref; do
      if [ "$ref" = "refs/heads/$BRANCH" ]; then
        echo "--> $BRANCH dalı alındı, $TARGET dizinine açılıyor"
        git --work-tree="$TARGET" --git-dir="$GIT_DIR" checkout -f "$BRANCH"
    
        cd "$TARGET"
        # Projeye göre: bağımlılıklar, önbellek temizliği, izinler
        # composer install --no-dev --optimize-autoloader
        chown -R www-data:www-data "$TARGET"
        echo "--> Dağıtım tamam: $(git --git-dir=$GIT_DIR rev-parse --short $newrev)"
      else
        echo "--> $ref dalı yoksayıldı"
      fi
    done
    

    Kancayı çalıştırılabilir yapmayı unutmayın (chmod +x hooks/post-receive), yoksa Git onu sessizce atlar. Geliştirici tarafında ise sunucuyu ikinci bir uzak adres olarak ekleyip push edersiniz:

    git remote add canli ssh://[email protected]/var/repo/site.git
    git push canli main
    # remote: --> main dalı alındı, /var/www/firmaniz.com dizinine açılıyor
    # remote: --> Dağıtım tamam: a1b2c3d
    

    Paylaşımlı hosting kullanıyor ve kendi sunucusu olmayan bir ortamda benzer bir akış istiyorsanız, cPanel'in yerleşik Git özelliği aynı mantığı panel üzerinden sunar; ayrıntısı cPanel Git sürüm kontrolü yazısında. Kendi kancalarınızı özgürce yazmak istiyorsanız tam root erişimi gerekir; VDS veya sanal sunucu paketleri bu tür kurulumlar için uygundur.

    Kancaları Ekiple Paylaşmak: core.hooksPath#

    Şimdi en başta bahsettiğim soruna geri dönelim: .git/hooks/ klonlanmaz. Modern Git'in buna resmî çözümü core.hooksPath ayarıdır. Kancalarınızı depo içinde izlenen bir dizinde tutar, Git'e "kancaları oradan oku" dersiniz.

    1. Depoda .githooks/ adında bir dizin açın ve kancalarınızı oraya taşıyın.
    2. Dosyalara çalıştırma izni verin ve bunu Git'in de bilmesini sağlayın: git update-index --chmod=+x .githooks/pre-commit.
    3. Dizini normal şekilde commit'leyin; artık kancalar depo geçmişinde.
    4. Herkesin tek seferlik çalıştıracağı ayarı belgeleyin.
    # Depo kökünde, klonlamadan sonra bir kez
    git config core.hooksPath .githooks
    
    # Doğrulama
    git config --get core.hooksPath
    # .githooks
    

    Bu ayarı otomatikleştirmek için projenin kurulum betiğine (make setup, npm run prepare vb.) ekleyebilirsiniz. Node tabanlı projelerde bu işi paketleyen araçlar da vardır ama altta yatan mekanizma aynıdır: core.hooksPath ya da .git/hooks içine sembolik bağlantı.

    ⚠️ Şunu asla unutmayın: istemci kancaları bir güvenlik sınırı değildir. git commit --no-verify yazan herkes hepsini atlar. Kancalar hata yapmayı zorlaştırır, kötü niyeti engellemez. Zorlayıcı kural istiyorsanız sunucu tarafındaki pre-receive kancasına ya da barındırma platformunuzun dal koruma ayarlarına ihtiyacınız var.

    Sık Yapılan Hatalar ve Tuzaklar#

    Çalıştırma izni verilmemiş kanca. En sık karşılaşılan durum budur ve en sinsi olanıdır: Git izinsiz kancayı çalıştırmaz, ama size hiçbir uyarı da vermez. Kanca sanki yokmuş gibi davranır. Bir kanca "çalışmıyor" diyorsanız ilk bakacağınız yer ls -l çıktısındaki x bitidir.

    Yanlış shebang veya CRLF satır sonu. Windows'ta düzenlenmiş bir betik CRLF satır sonlarıyla kaydedilirse Linux tarafında bad interpreter: /usr/bin/env bash^M hatası alırsınız. file .githooks/pre-commit çıktısında "CRLF line terminators" görüyorsanız dos2unix ile düzeltin ve depoya bir .gitattributes ekleyerek bu dosyaları text eol=lf olarak işaretleyin.

    Kancanın içinde etkileşim beklemek. read ile kullanıcıya soru sormaya çalışan bir kanca, GUI istemcilerde ya da CI ortamında kilitlenir. Kancalar sessiz ve kendi kendine karar veren betikler olmalıdır.

    Sunucu kancasında ortam değişkenlerine güvenmek. post-receive çalışırken PATH ve çalışma dizini beklediğinizden farklıdır; ayrıca Git, GIT_DIR gibi değişkenleri kendisi doldurur ve bu, alt komutlarınızı şaşırtabilir. Bu yüzden yukarıdaki örnekte yolları tam (mutlak) yazdım ve --git-dir parametresini açıkça verdim.

    Ağır işleri yanlış kancaya koymak. Tüm test paketini pre-commit'e koyarsanız ekip iki gün sonra --no-verify alışkanlığı edinir ve kancanın hiçbir faydası kalmaz. Hızlı olanı commit'e, uzun süreni push'a, en kapsamlısını CI'ya bırakın.

    BelirtiOlası nedenKontrol
    Kanca hiç çalışmıyorÇalıştırma izni yokls -l .git/hooks/
    bad interpreter hatasıCRLF satır sonufile <kanca>
    Klonlayanda kanca yok.git/hooks izlenmiyorgit config core.hooksPath
    Commit yine de oluştu--no-verify kullanılmışSunucu tarafına taşıyın
    post-receive çıktısı görünmüyorecho yerine stderr gerekiyorÇıktıyı >&2 ile yazın

    Kanca çıktılarının push sırasında remote: önekiyle geldiğini de bilin; hata mesajlarınızı anlaşılır yazın, çünkü karşı taraf yalnızca o satırları görecek.

    Sıkça Sorulan Sorular#

    Git hooks depoyla birlikte klonlanır mı#

    Hayır. .git/hooks/ dizini deponun çalışma ağacının dışındadır ve versiyon kontrolüne dahil edilmez, dolayısıyla klonlayan kişiye geçmez. Çözüm, kancaları depo içinde izlenen bir dizinde (örneğin .githooks/) tutmak ve herkesin bir kez git config core.hooksPath .githooks komutunu çalıştırmasını sağlamaktır. Bu komutu projenin kurulum adımına eklerseniz kimsenin ayrıca hatırlaması gerekmez.

    pre-commit kancası nasıl devre dışı bırakılır#

    Tek seferlik atlamak için git commit --no-verify (kısaca -n) kullanabilirsiniz; bu, pre-commit ve commit-msg kancalarını birlikte atlar. Kalıcı olarak kapatmak isterseniz dosyanın çalıştırma iznini kaldırmanız (chmod -x) yeterlidir. Ancak bu bayrağın varlığı, istemci kancalarının neden bir güvenlik önlemi sayılamayacağını da gösterir; zorlayıcı kurallar sunucu tarafına aittir.

    Git hooks ile CI/CD arasındaki fark nedir#

    Kancalar geliştiricinin makinesinde ya da Git sunucusunda, push anında çalışan yerel betiklerdir; geri bildirimi anında verirler ve kurulumları çok basittir. CI/CD ise ayrı bir çalıştırıcı ortamda, izole ve tekrarlanabilir biçimde çalışır; uzun testleri, çoklu ortam matrislerini ve yayın süreçlerini oraya taşırsınız. İkisi rakip değildir: kancalar hızlı ön eleme, CI kapsamlı doğrulama yapar.

    post-receive ile canlı sunucuya dağıtım güvenli mi#

    Küçük ve orta ölçekli projeler için gayet kullanışlıdır, ancak birkaç kurala uymanız gerekir. Yayın dizinini çıplak deponun dışında tutun, checkout -f sonrası dosya sahipliğini ve izinleri düzeltin, yalnızca belirlediğiniz dalı dağıtın ve .env gibi ortama özel dosyaları depoya koymayıp sunucuda tutun. Sıfır kesinti isteyen daha büyük kurulumlarda ise sürüm dizinleri ve sembolik bağlantı yaklaşımına geçmek daha doğru olur.

    Kanca çalışmıyor, nereden başlamalıyım#

    Sırasıyla üç şeye bakın: dosya adı doğru mu (uzantı olmamalı, pre-commit.sample değil pre-commit), çalıştırma izni var mı (ls -l çıktısında x), shebang satırı doğru mu ve dosya LF satır sonlarıyla mı kayıtlı. core.hooksPath ayarlanmışsa Git .git/hooks içine hiç bakmaz, o yüzden git config --get core.hooksPath çıktısını da kontrol edin. Betiği doğrudan elle çalıştırıp hata alıp almadığınızı görmek de hızlı bir teşhis yöntemidir.

    Kancalarda hangi dili kullanmalıyım#

    Git için fark etmez; belirleyici olan shebang satırıdır. Sunucu tarafı kancalarda mümkün olduğunca ek bağımlılık gerektirmeyen POSIX uyumlu kabuk betiği tercih edin, çünkü o ortamda Node veya Python kurulu olmayabilir. Geliştirici makinelerinde ise projenin zaten kullandığı dil (Node, Python, PHP) tamamen mantıklıdır; ekip o çalışma zamanına nasılsa sahiptir.

    Kanca çıktısı ne kadar ayrıntılı olmalı#

    Başarılı durumda mümkün olduğunca sessiz kalın; her commit'te ekranı dolduran bir kanca kısa sürede göz ardı edilir. Hata durumunda ise tam tersini yapın: neyin yanlış olduğunu, hangi dosyada olduğunu ve nasıl düzeltileceğini açıkça yazın. İyi bir hata mesajı, kullanıcının --no-verify yazmak yerine sorunu çözmesini sağlayan şeydir.

    Kapanış#

    Git hooks, ekip kurallarını belgeden koda taşımanın en ucuz yoludur. Aklınızda kalması gereken dört alışkanlık şunlar: hızlı denetimleri pre-commit'e, ağır testleri pre-push'a koyun; mesaj formatını commit-msg ile makineye doğrulatın; kancaları core.hooksPath üzerinden depo içinde paylaşın; ve zorlayıcı olması gereken hiçbir kuralı istemci kancasına emanet etmeyin, çünkü --no-verify her zaman oradadır. Sunucu tarafında post-receive ise küçük projeler için sürpriz derecede sağlam bir dağıtım kanalı sunar.

    Bu akışları kurmak için kendi sunucunuza tam erişim gerekiyorsa VDS ve sanal sunucu paketlerimizde root yetkisiyle çıplak depo, kanca ve dağıtım betiklerinizi özgürce kurabilirsiniz. Kurulum ve bakım kısmını üstlenmemizi isterseniz sunucu yönetimi hizmetimiz devreye girer; mevcut projenizi yeni bir sunucuya taşırken dağıtım akışının da beraber gelmesini istiyorsanız site taşıma sayfamıza göz atabilirsiniz. Diğer DevOps konuları için wiki bölümümüzde ilgili rehberleri bulabilirsiniz.

    GitOtomasyonDevOps

    Uygulamaya geçmeye hazır mısınız?

    NVMe SSD, ücretsiz SSL ve %99.9 uptime garantisiyle Clou.TR hosting ve sunucu çözümleriyle projenizi hayata geçirin.