Git Tag Kullanımı ile Sürüm Etiketleme
Sürüm yönetimi konusunda ekiplerle çalışırken fark ettiğim bir şey var: herkes branch kullanmayı biliyor, commit atmayı biliyor, ama tag konusu hep ihmal ediliyor. “Zaten tag ne işe yarayacak ki, branch yeterli değil mi?” diye soruyorlar. Sonra production’a hangi sürümün gittiğini bulmak için commit hash’leri karıştırıyorlar, o “düzeltmemiz gereken commit” yi ararken kayboluyorlar. Tag’ların değerini genellikle bu noktada anlıyorlar. Bu yazıda Git tag’larını sıfırdan ele alacağız, ama sadece sözdizimi düzeyinde değil, gerçekten nasıl ve neden kullanmanız gerektiğini konuşacağız.
Git Tag Nedir ve Neden Önemlidir?
Git’te bir tag, tarihinizdeki belirli bir commit’e verilen kalıcı bir etikettir. Branch’lerden temel farkı şu: branch’ler hareket eder, yeni commit geldikçe ilerler. Tag’lar ise sabittir. Bir kez bir commit’i işaret eden tag, o commit’i sonsuza kadar işaret eder.
Bu basit fark, sürüm yönetimi açısından devrim niteliğindedir. v1.4.2 dediğinizde, altı ay sonra bile tam olarak hangi kodun production’a gittiğini bilirsiniz. Hangi özelliğin hangi sürümde eklendiğini, hangi bug’ın hangi release’te kapatıldığını izleyebilirsiniz. Özellikle CI/CD pipeline’larınızda, deployment’larınızda ve changelog üretiminde tag’lar olmadan işler kaotik hale gelir.
Tag Türleri: Lightweight ve Annotated
Git’te iki tip tag vardır ve aralarındaki farkı bilmek önemli.
Lightweight tag, bir commit’e yapıştırılmış basit bir etikeттен ibarettir. Ek meta veri içermez, sadece commit hash’ini işaret eder. Geçici notlar veya kişisel kullanım için uygundur.
Annotated tag ise tam anlamıyla bir Git nesnesidir. İçinde tagger bilgisi (kim oluşturdu), tarih, tag mesajı ve isteğe bağlı olarak GPG imzası bulunur. Production release’leri için her zaman annotated tag kullanmanızı öneririm.
# Lightweight tag oluşturma
git tag v1.0.0
# Annotated tag oluşturma (-a flagı ile)
git tag -a v1.0.0 -m "İlk kararlı sürüm - temel özellikler tamamlandı"
# Mevcut tag'ları listeleme
git tag
# Tag detaylarını görme (annotated tag için anlamlıdır)
git show v1.0.0
git show v1.0.0 komutunu çalıştırdığınızda annotated tag için şunu görürsünüz: kim oluşturdu, ne zaman oluşturdu, hangi mesajı yazdı ve ardından commit detayları. Lightweight tag’da ise direkt commit bilgisine atlarsınız, tagger bilgisi yoktur.
Semantik Sürümleme ile Tag Kullanımı
Tag’lardan maksimum fayda almak için Semantic Versioning (SemVer) standardını benimsemenizi şiddetle tavsiye ederim. Format şu şekilde: MAJOR.MINOR.PATCH
- MAJOR: Geriye dönük uyumsuz API değişiklikleri
- MINOR: Geriye dönük uyumlu yeni özellikler
- PATCH: Geriye dönük uyumlu hata düzeltmeleri
# İlk kararlı sürüm
git tag -a v1.0.0 -m "v1.0.0: Kullanıcı kimlik doğrulama modülü tamamlandı"
# Yeni bir özellik eklendi, geriye uyumlu
git tag -a v1.1.0 -m "v1.1.0: OAuth2 entegrasyonu eklendi"
# Kritik bir bug düzeltildi
git tag -a v1.1.1 -m "v1.1.1: JWT token yenileme hatası giderildi"
# Büyük değişiklik, eski API'ler kaldırıldı
git tag -a v2.0.0 -m "v2.0.0: REST API v2 - eski endpoint'ler kaldırıldı"
Pre-release sürümler için de bir konvansiyona ihtiyaç duyarsınız. SemVer bunu da karşılar:
# Alpha sürüm - henüz test aşamasında
git tag -a v2.0.0-alpha.1 -m "v2.0.0-alpha.1: Yeni API yapısı - dahili test"
# Beta sürüm - seçili kullanıcılara açık
git tag -a v2.0.0-beta.1 -m "v2.0.0-beta.1: Beta test süreci başladı"
# Release candidate - son test aşaması
git tag -a v2.0.0-rc.1 -m "v2.0.0-rc.1: Release candidate, production öncesi son test"
Geçmiş Commit’lere Tag Ekleme
Bu özelliği bilmeyenler gerçekten şaşırıyor. Tag’ı sadece son commit’e değil, geçmişteki herhangi bir commit’e ekleyebilirsiniz. Diyelim ki iki hafta önce production’a bir sürüm çıkardınız ama o sırada tag eklemeyi unutтunuz. Sorun değil.
# Commit geçmişine bak
git log --oneline
# Çıktı örneği:
# a3f8c21 (HEAD -> main) Loglama iyileştirmeleri
# 7b2d934 Performans optimizasyonu
# 4e9a1b7 v1.2.0 için deployment yapıldı <-- bunu tag'lamak istiyoruz
# c82f3d5 Database bağlantı havuzu güncellendi
# 1a4b8e2 Kullanıcı profil sayfası eklendi
# Geçmişteki commit'e tag ekle
git tag -a v1.2.0 4e9a1b7 -m "v1.2.0: Performans iyileştirmeleri ve bug düzeltmeleri"
# Doğrulama
git show v1.2.0
Bu özellik, özellikle acele deployment’lardan sonra sürüm geçmişini düzenlemek için hayat kurtarır.
Tag’ları Remote’a Push Etme
İşte birçok kişinin düştüğü tuzak: git push komutu tag’ları otomatik olarak göndermez. Bunu açıkça yapmanız gerekir.
# Tek bir tag'ı push et
git push origin v1.2.0
# Tüm local tag'ları push et
git push origin --tags
# Belirli bir pattern'deki tag'ları push et
git push origin 'refs/tags/v1.*'
CI/CD pipeline’larınızda genellikle --tags flagını kullanmak isteyebilirsiniz, ama dikkatli olun: eğer test veya geçici tag’larınız varsa, bunlar da push edilecektir. Bu yüzden naming convention’ınızı baştan oturmak önemlidir.
Remote’tan tag almak için:
# Fetch ile tag'ları al (genellikle otomatik gelir)
git fetch --tags
# Tüm remote değişiklikleri ve tag'ları çek
git pull --tags
Tag’ları Silme
Yanlış bir tag oluşturduğunuzda ya da bir tag’ı kaldırmanız gerektiğinde:
# Local tag'ı sil
git tag -d v1.0.0-beta
# Remote'taki tag'ı sil
git push origin --delete v1.0.0-beta
# Eski syntax ile remote tag silme (bazı eski git versiyonlarında)
git push origin :refs/tags/v1.0.0-beta
Önemli uyarı: Eğer o tag’ı zaten başkaları kullanıyorsa, remote’tan silmek ciddi sorunlara yol açabilir. Özellikle CI/CD pipeline’ları o tag’ı referans alıyorsa, silmeden önce ekibinizi bilgilendirin. Production tag’larını silmek neredeyse hiçbir zaman doğru bir karar değildir.
Tag’lara Göre Checkout ve Kod İnceleme
Tag’ların en kullanışlı özelliklerinden biri, belirli bir sürüme geri dönebilmektir:
# Belirli bir tag'a checkout yap
git checkout v1.1.0
# Bu noktada "detached HEAD" modundasınızdır
# Değişiklik yapmak istiyorsanız yeni bir branch açın
git checkout -b hotfix/v1.1.0-security v1.1.0
# İki tag arasındaki farkı görme
git diff v1.0.0 v1.1.0
# İki tag arasındaki commit listesi
git log v1.0.0..v1.1.0 --oneline
# Belirli bir dosyanın iki sürüm arasındaki değişimi
git diff v1.0.0 v1.1.0 -- src/auth/login.py
Bu git log v1.0.0..v1.1.0 --oneline komutu özellikle changelog oluştururken çok işe yarar. İki sürüm arasında tam olarak ne değişti, bunu tek komutla görürsünüz.
Gerçek Dünya Senaryosu: E-Ticaret Projesi Sürüm Yönetimi
Gerçek bir senaryoyu adım adım inceleyelim. Diyelim ki bir e-ticaret platformunun backend’ini yönetiyorsunuz ve düzenli release’ler yapmanız gerekiyor.
# Proje başlangıcı - ilk alpha
git tag -a v0.1.0-alpha -m "v0.1.0-alpha: Ürün listeleme ve sepet modülleri"
# İlk kararlı sürüm - temel alışveriş akışı tamamlandı
git tag -a v1.0.0 -m "v1.0.0: MVP release - temel alışveriş akışı production'da"
# Ödeme entegrasyonu eklendi
git tag -a v1.1.0 -m "v1.1.0: İyzico ödeme entegrasyonu, kargo takip API'si"
# Kritik güvenlik açığı kapatıldı
git tag -a v1.1.1 -m "v1.1.1: CRITICAL - SQL injection açığı kapatıldı (CVE-2024-XXXX)"
# Büyük yenileme - mobil API v2
git tag -a v2.0.0-rc.1 -m "v2.0.0-rc.1: Mobil API yeniden yazıldı, GraphQL desteği"
git tag -a v2.0.0-rc.2 -m "v2.0.0-rc.2: Performance testleri geçildi, son review"
git tag -a v2.0.0 -m "v2.0.0: Mobil API v2 ve GraphQL endpoint'leri production'da"
# Hangi sürümler var?
git tag -l "v*" --sort=-version:refname | head -10
Güvenlik açığı tag mesajına CVE numarasını eklemek, ileride o açığın ne zaman kapatıldığını bulmayı kolaylaştırır. Bunu küçük ama hayat kurtaran bir detay olarak görüyorum.
CI/CD Pipeline’larında Tag Tetiklemeli Deployment
Modern bir deployment akışında tag’lar kritik rol oynar. GitLab CI örneği:
# Tag oluşturma ve push etme - bu CI pipeline'ını tetikler
git tag -a v1.5.0 -m "v1.5.0: Quarterly release - Q1 2024"
git push origin v1.5.0
# Pipeline'da hangi tag'dan build alındığını görmek için
echo "Build alınan sürüm: $(git describe --tags --abbrev=0)"
# git describe komutu - en yakın tag'ı ve o tag'dan bu yana kaç commit olduğunu gösterir
git describe --tags
# Örnek çıktı: v1.5.0-3-ga4f8c21
# v1.5.0 tag'ından bu yana 3 commit yapılmış, son commit hash'i a4f8c21
git describe --tags komutunu application’ınızın version endpoint’inde kullanabilirsiniz. Deployment sırasında hangi tag’dan build alındığını, o tag’dan bu yana kaç commit yapıldığını görmek son derece kullanışlıdır. Özellikle hotfix durumlarında “bu servis tam olarak hangi kodu çalıştırıyor?” sorusunu saniyeler içinde yanıtlamanızı sağlar.
Tag Listeleme ve Filtreleme
Zaman içinde tag sayısı artar, bunları yönetmek için birkaç teknik:
# Tüm tag'ları listele
git tag
# Pattern ile filtreleme
git tag -l "v1.*"
git tag -l "v2.0.*"
git tag -l "*-rc*"
# Sürüm sırasına göre sıralı listeleme (en yeni en üstte)
git tag -l --sort=-version:refname
# Sadece son 5 tag
git tag -l --sort=-version:refname | head -5
# Tag tarihlerini göster
git log --tags --simplify-by-decoration --pretty="format:%ai %d" | grep "tag:"
# Belirli bir tarih aralığındaki tag'lar
git log --tags --simplify-by-decoration --pretty="format:%ai %d"
--after="2024-01-01" --before="2024-06-30" | grep "tag:"
Tag’ları GPG ile İmzalama
Yüksek güvenlik gerektiren projelerde, tag’ların gerçekten yetkili biri tarafından oluşturulduğunu doğrulamak istersiniz. GPG imzalı tag’lar bu ihtiyacı karşılar:
# GPG anahtarınızı git config'e ekleyin
git config --global user.signingkey YOUR_GPG_KEY_ID
# İmzalı tag oluştur (-s flagı)
git tag -s v3.0.0 -m "v3.0.0: Major release - imzalı"
# İmzayı doğrula
git tag -v v3.0.0
# Başarılı doğrulama çıktısı şuna benzer:
# object 4a5b6c7...
# type commit
# tag v3.0.0
# tagger Ad Soyad <[email protected]> 1704067200 +0300
# v3.0.0: Major release - imzalı
# gpg: Signature made...
# gpg: Good signature from "Ad Soyad <[email protected]>"
Özellikle açık kaynak projelerde veya kurumsal ortamlarda release yetkisinin tek bir kişide veya belirli kişilerde olduğu durumlar için GPG imzalama pratik bir güvenlik katmanıdır.
Tag Tabanlı Changelog Otomasyonu
Tag’ların gerçek gücü, otomasyon ile birleştiğinde ortaya çıkar. Basit bir changelog scripti:
#!/bin/bash
# generate_changelog.sh
PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD^)
CURRENT_TAG=$(git describe --tags --abbrev=0 HEAD)
echo "## Changelog: $PREVIOUS_TAG -> $CURRENT_TAG"
echo ""
echo "### Tüm Değişiklikler"
git log ${PREVIOUS_TAG}..${CURRENT_TAG} --oneline --no-merges
echo ""
echo "### Özellikler (feat:)"
git log ${PREVIOUS_TAG}..${CURRENT_TAG} --oneline --no-merges | grep "^[a-f0-9]* feat:" || echo "Yeni özellik yok"
echo ""
echo "### Hata Düzeltmeleri (fix:)"
git log ${PREVIOUS_TAG}..${CURRENT_TAG} --oneline --no-merges | grep "^[a-f0-9]* fix:" || echo "Bug fix yok"
echo ""
echo "**Sürüm tarihi:** $(git log -1 --format=%ai $CURRENT_TAG)"
echo "**Katkıda bulunanlar:** $(git log ${PREVIOUS_TAG}..${CURRENT_TAG} --format='%an' | sort -u | tr 'n' ', ')"
Bu scripti çalıştırdığınızda iki tag arasındaki tüm commit’leri, özellik ve hata düzeltmelerini kategorize edilmiş şekilde alırsınız. Conventional Commits standardını kullanıyorsanız (feat:, fix:, docs: vb.) bu otomasyonu çok daha da güçlendirebilirsiniz.
Ekip İçin Tag Konvansiyonu Oluşturma
Tag kullanımının değeri, ekibin tutarlı bir şekilde uygulamasıyla ortaya çıkar. Birkaç temel kural önerisi:
- Sadece annotated tag kullanın: Lightweight tag’ları yalnızca kişisel geçici notlar için, hiçbir zaman shared repository’ye push etmeyin.
- Tag mesajlarını anlamlı yazın: “minor fixes” yerine “v1.2.1: Ödeme sayfasındaki döviz kursu hesaplama hatası giderildi” gibi mesajlar yazın.
- Tag yetkisini belirleyin: Her developer tag oluşturabilmeli mi? Yoksa sadece senior developer veya release manager mı? Bunu yazılı politika olarak belirleyin.
- Tag’ları asla force-push ile değiştirmeyin: Bir tag’ı değiştirmeniz gerekiyorsa, silersiniz ve yeniden oluşturursunuz. Ama production tag’larını silmemek prensibini hatırlayın.
- Pre-release tag’ları temizleyin:
v2.0.0production’a çıktıktan sonrav2.0.0-alphavev2.0.0-betatag’larını saklamak mı, silmek mi? Bunu politika olarak belirleyin. Ben genellikle silmemeyi tercih ederim, tarihsel referans olarak kalsinlar. - Tag naming’i CI’a entegre edin: Branch protection gibi, belirli pattern’lere uymayan tag’ları reddeden hook’lar veya CI kuralları yazabilirsiniz.
.git/hooks ile Tag Validasyonu
Local olarak tag konvansiyonunuzu zorlamak için git hook kullanabilirsiniz:
#!/bin/bash
# .git/hooks/update - server-side hook (veya pre-push için uyarlayın)
# Tag format validasyonu: vX.Y.Z veya vX.Y.Z-{alpha|beta|rc}.N
TAG_NAME=$1
if [[ $TAG_NAME =~ ^refs/tags/ ]]; then
TAG=$(echo $TAG_NAME | sed 's|refs/tags/||')
# SemVer pattern kontrolü
if ! [[ $TAG =~ ^v[0-9]+.[0-9]+.[0-9]+(-[a-z]+.[0-9]+)?$ ]]; then
echo "HATA: Tag '$TAG' geçerli format değil."
echo "Geçerli formatlar: v1.2.3 veya v1.2.3-alpha.1 veya v1.2.3-rc.2"
exit 1
fi
echo "Tag formatı geçerli: $TAG"
fi
exit 0
Bu hook’u server tarafında yapılandırırsanız, format kurallarına uymayan tag’ların repository’ye girmesini engellersiniz.
Sonuç
Git tag kullanımı, “opsiyonel bir özellik” değil, profesyonel sürüm yönetiminin temel taşıdır. Annotated tag’lar ile her release’inizi kalıcı ve izlenebilir hale getirirsiniz, SemVer ile sürüm numaralarınıza anlam katarsınız, CI/CD entegrasyonu ile deployment süreçlerinizi tag tetiklemeli hale getirirsiniz.
Başlangıç olarak şu üç şeyi yapmanızı öneririm: mevcut projenizin önemli geçmiş commit’lerine geriye dönük tag ekleyin, annotated tag kullanımını ekip genelinde standart hale getirin ve git describe --tags çıktısını uygulama version endpoint’inizde sergileyin.
Altı ay sonra production’da koşan kodun tam olarak hangi sürüm olduğunu tek komutla bilmek istediğinizde, bugün attığınız bu temele minnettar olacaksınız.
