Yasin Ateş

Git Hooks ile Commit Mesajlarına Otomatik Branch İsmi Eklemek

Git Hooks ile Commit Mesajlarına Otomatik Branch İsmi Eklemek

🪝 Git Hook ile Commit Mesajlarına Otomatik Branch İsmi Eklemek

Commit mesajlarınıza her seferinde branch adını elle yazmaktan bıktınız mı? Bir Git Hook ile bu işlemi tamamen otomatik hale getirebilirsiniz. Bu yazıda prepare-commit-msg hook'unu lokal ve global olarak kurmayı, Conventional Commits formatıyla birlikte kullanmayı adım adım göreceğiz.

🪝 Git Hooks Nedir?

Git’in en az konuşulan ama en güçlü özelliklerinden biri Git Hooks mekanizmasıdır. Kısaca söylemek gerekirse, Git’in belirli olaylarında (commit, push, merge vb.) otomatik olarak çalışan script’lerdir.

Git, .git/hooks dizininde bazı hook'larla birlikte gelir. Bu yazıda biz prepare-commit-msg hook'una odaklanacağız. Bu hook, commit mesajı oluşturulduktan sonra ama henüz gönderilmeden önce çalışır. Yani commit mesajını "araya girip" değiştirebiliriz.

🛠️ Lokal Kurulum: prepare-commit-msg Hook’u

Local olarak prepare-commit-msg hooku kurulumu

Bu bölümde, mevcut branch adından önce ticket numarasını (örneğin PROJ-123), yoksa branch adının anlamlı kısmını çıkarıp commit mesajının başına otomatik olarak ekleyen bir hook yazacağız.

Adım 1: Hook Dosyasını Oluşturalım

Projenizin kök dizininde aşağıdaki komutu çalıştırarak hook dosyasını oluşturabilirsiniz:

touch .git/hooks/prepare-commit-msg
chmod +x .git/hooks/prepare-commit-msg
  • touch komutu hook dosyasını oluşturur.
  • chmod +x komutu dosyayı çalıştırılabilir hale getirir. Bu adımı atlarsanız hook çalışmaz!

Adım 2: Hook Script’ini Yazalım

Şimdi .git/hooks/prepare-commit-msg dosyasını açın ve aşağıdaki script'i yapıştırın:

#!/bin/bash

# Mevcut branch adını al
BRANCH_NAME=$(git symbolic-ref --short HEAD 2>/dev/null)

# Branch alınamadıysa (detached HEAD vb.) çık
if [ -z "$BRANCH_NAME" ]; then
 exit 0
fi

# Önce branch adından ticket numarasını çıkarmayı dene (PROJ-123, JIRA-456 gibi)
TICKET=$(echo "$BRANCH_NAME" | grep -oE '[A-Z]+-[0-9]+')

# Ticket yoksa prefix sonrasını kullan (feature/discover → discover)
if [ -z "$TICKET" ]; then
 TICKET=$(echo "$BRANCH_NAME" | cut -d'/' -f2-)
 # Slash yoksa (yani branch adında / karakteri hiç geçmiyorsa) çık
 if [ -z "$TICKET" ] || [ "$TICKET" = "$BRANCH_NAME" ]; then
 exit 0
 fi
fi

COMMIT_MSG_FILE=$1
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")

# Merge commit, squash gibi durumlarda müdahale etme
COMMIT_SOURCE=$2
if [ "$COMMIT_SOURCE" = "merge" ] || [ "$COMMIT_SOURCE" = "squash" ]; then
 exit 0
fi

# Etiket zaten mesajda varsa tekrar ekleme
if echo "$COMMIT_MSG" | grep -q "$TICKET"; then
 exit 0
fi

# Commit mesajının başına etiketi ekle
echo "[$TICKET] $COMMIT_MSG" > "$COMMIT_MSG_FILE"

Yukarıdaki komutları açıklayayım:

  • ★ git symbolic-ref --short HEAD mevcut branch adını döner. Detached HEAD durumunda hata vermemesi için 2>/dev/null ekliyoruz.
  • ★ grep -oE '[A-Z]+-[0-9]+' regex ile branch adındaki ticket numarasını yakalar. Örneğin feature/PROJ-123-add-login branch'inden PROJ-123 değerini çıkarır.
  • ★ Ticket bulunamazsa cut -d'/' -f2- ile branch adının prefix sonrasını alır. feature/discover için discover, feature/user-profile-page için user-profile-page değerini üretir.
  • ★ COMMIT_SOURCE kontrolü sayesinde merge ve squash commit'lerine müdahale etmiyoruz.
  • ★ Son grep -q kontrolü ile aynı etiketin tekrar tekrar eklenmesini engelliyoruz.

Adım 3: Test Edelim

Birkaç farklı branch ile deneyelim:

Ticket numarası olan branch:

# Branch: feature/PROJ-123-add-login
git commit -m "add login form validation"
# Sonuç: [PROJ-123] add login form validation

Ticket numarası olmayan branch:

# Branch: feature/discover
git commit -m "add search results page"
# Sonuç: [discover] add search results page

Göreceğimiz üzere her commit’te ilgili etiket otomatik olarak ekleniyor.

📐 Conventional Commits ile Kullanım

Git Conventional Commits

Conventional Commits, commit mesajlarına standart bir yapı kazandıran bir spesifikasyondur. feat:, fix:, chore: gibi prefix'ler kullanarak commit'lerin ne tür bir değişiklik içerdiğini ilk bakışta anlamanızı sağlar.

#!/bin/bash

BRANCH_NAME=$(git symbolic-ref --short HEAD 2>/dev/null)

if [ -z "$BRANCH_NAME" ]; then
 exit 0
fi

# Önce ticket numarasını çıkarmayı dene
TICKET=$(echo "$BRANCH_NAME" | grep -oE '[A-Z]+-[0-9]+')

# Ticket yoksa prefix sonrasını kullan (feature/discover → discover)
if [ -z "$TICKET" ]; then
 TICKET=$(echo "$BRANCH_NAME" | cut -d'/' -f2-)
 if [ -z "$TICKET" ] || [ "$TICKET" = "$BRANCH_NAME" ]; then
 exit 0
 fi
fi

# Branch prefix'inden commit tipini belirle
PREFIX=$(echo "$BRANCH_NAME" | cut -d'/' -f1)

case "$PREFIX" in
 feature) TYPE="feat" ;;
 bugfix) TYPE="fix" ;;
 fix) TYPE="fix" ;;
 hotfix) TYPE="hotfix" ;;
 chore) TYPE="chore" ;;
 docs) TYPE="docs" ;;
 refactor) TYPE="refactor" ;;
 test) TYPE="test" ;;
 *) TYPE="chore" ;;
esac

COMMIT_MSG_FILE=$1
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")

COMMIT_SOURCE=$2
if [ "$COMMIT_SOURCE" = "merge" ] || [ "$COMMIT_SOURCE" = "squash" ]; then
 exit 0
fi

# Mesaj zaten conventional formattaysa dokunma
if echo "$COMMIT_MSG" | grep -qE '^(feat|fix|hotfix|chore|docs|refactor|test)\('; then
 exit 0
fi

# Conventional Commits formatında yaz
echo "$TYPE($TICKET): $COMMIT_MSG" > "$COMMIT_MSG_FILE"

Bu script’teki önemli farkları inceleyelim:

  • ★ Ticket arama mantığı lokal kurulum script’iyle aynıdır: önce PROJ-123 formatını dener, bulamazsa prefix sonrasını (discover, user-profile gibi) kullanır.
  • ★ cut -d'/' -f1 komutu branch adını / karakterinden böler ve ilk kısmı (prefix) alır.
  • ★ case bloğu ile branch prefix'ini Conventional Commits tipine eşliyoruz. Örneğin feature prefix'i feat tipine dönüşür.
  • ★ Son kontrol satırında, mesaj zaten conventional formatta ise tekrar dönüştürmüyoruz.
💡 Bunu neden tercih ettiğimi açıklayayım: Ekipte herkes farklı formatta commit atarken, bu hook sayesinde tüm commit geçmişiniz tutarlı ve okunabilir hale geliyor. Changelog üretimi, release note’ları ve code review süreci ciddi anlamda kolaylaşıyor.

Şimdi farklı branch senaryolarının sonuçlarına bakalım:

  1. ticket id ile feature branch
  • Branch: feature/PROJ-123-add-login
  • Komut: git commit -m "add login form"
  • Sonuç: feat(PROJ-123): add login form

2. ticket id ile hotfix branch

Branch: bugfix/PROJ-456-fix-header

Komut: git commit -m "fix header alignment"

Sonuç: fix(PROJ-456): fix header alignment

3. ticket id olmayan feature branch

Branch: feature/discover

Komut: git commit -m "add search results page"

Sonuç: feat(discover): add search results page

4. ticket id olmayan hotfix branch

Branch: bugfix/fix-header

Komut: git commit -m "fix navbar overlap"

Sonuç: fix(fix-header): fix navbar overlap

Peki ama her projede aynı hook dosyasını tekrar tekrar oluşturmak istemiyorsanız ne yapacaksınız? İşte tam bu noktada “Global Git Hooks” devreye giriyor.

Global Git Hooks: Tüm Projelere Kurmak İstersek 😁

Git 2.9 ve sonrasında core.hooksPath ayarı ile tüm projeleriniz için ortak bir hooks dizini tanımlayabilirsiniz.

Adım 1: Global Hooks Dizinini Oluşturalım

mkdir -p ~/.git-hooks

Adım 2: Hook Script’ini Bu Dizine Kopyalayalım

Yukarıda yazdığımız script’lerden hangisini tercih ediyorsanız onu ~/.git-hooks/prepare-commit-msg olarak kaydedin:

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

Adım 3: Git’e Global Hooks Dizinini Tanıtalım

git config --global core.hooksPath ~/.git-hooks
  • ★ Bu komut, bilgisayarınızdaki tüm Git projelerinde ~/.git-hooks dizinindeki hook'ları kullanmasını sağlar.
  • ★ Projeye özel .git/hooks dizini artık devre dışı kalır.

Burada dikkat etmeniz gereken bir nokta var: Global hooks aktifken, belirli bir projede farklı hook’lar kullanmak isterseniz o projede lokal olarak core.hooksPath ayarını override edebilirsiniz:

# Belirli bir projede lokal hooks'a geri dön
git config --local core.hooksPath .git/hooks

Sonuç olarak, global hooks tek seferlik bir kurulumla tüm projelerinizi kapsar.

📬 Geri Bildirim

Makaleyi yazarken, kaynakları belirleme ve araştırma için kendi notlarımı, yazım denetimi ve ek araştırma için Claude Opus 4.6 modelini kullandım. Resimleri üretmek için ise Gemini 3 Pro Preview 2k (Nano Banana Pro) modelini kullandım.

Yazı ile ilgili tavsiye, öneri, eleştirileri dikkate alıyorum. İletişime geçmek isterseniz bana websitemdeki sosyal medya adreslerimden veya Linkedin üzerinden ulaşabilirsiniz.

Sevgiyle kalın, Yasin 🤗

📚 Makaleyi Yazarken Kullandığım Kaynaklar

  1. Git Hooks Documentation — Git hook türleri ve prepare-commit-msg hook’unun çalışma mantığı için
  2. Conventional Commits Specification — Conventional Commits formatı ve kuralları için
  3. Git Config Documentation — core.hooksPath ayarı ve global konfigürasyon detayları için
  4. Pro Git Book — Customizing Git Hooks — Git Hooks’un detaylı kullanım senaryoları için