Tag ve Release
Giriş — Sürüm Kavramı
Bir yazılım projesi geliştirirken düşün: commit'lerin akıyor, branch'ler açılıp kapanıyor, her gün onlarca değişiklik yapılıyor. Ama bir noktada "Tamam, bu sürüm yayınlanabilir" diyorsun. Kullanıcılara sunduğun, production'a deploy ettiğin, "v1.0 budur" dediğin o ana bir işaret koymalısın.
İşte tag bu işaretin ta kendisi. Commit tarihçesindeki belirli bir noktaya yapıştırılan kalıcı bir etiket. Branch'ler hareket eder (her commit'le ilerler), ama tag'ler sabit kalır. "Bu commit v1.0'dır" — nokta.
Bu derste tag'lerin ne olduğunu, nasıl oluşturulacağını, semantic versioning kavramını ve GitHub Releases ile tag'leri nasıl birleştireceğini öğreneceksin.
Tag Nedir?
Analoji — Kitaptaki Sayfa İmi
Bir kitap okuyorsun (commit tarihçesi). Sayfalar ilerliyor (commit'ler). Önemli bir sayfaya geldiğinde bir ayraç koyuyorsun — "Burası önemli, sonra dönmem gerekebilir."
Tag tam olarak bu ayraç. Commit akışındaki belirli bir noktayı işaretler:
Commit tarihçesi:
A───B───C───D───E───F───G───H───I───J───K
↑ ↑ ↑
v0.1 v1.0 v2.0
(beta) (release) (major update)Tag'ler branch'lerden farklıdır:
Branch (dal): Büyüyen, hareket eden bir işaretçi
Her commit'le ilerler
Geçici — iş bitince silinir
Tag (etiket): Sabit bir işaretçi
Bir commit'e yapışır, hareket etmez
Kalıcı — sürümü temsil ederİki Tür Tag
Git'te iki tür tag var: lightweight (hafif) ve annotated (açıklamalı).
Lightweight Tag
Sadece bir commit'e işaret eden basit bir işaretçi. Ek bilgi içermez.
# Lightweight tag oluştur
git tag v1.0
# Belirli bir commit'e tag koy
git tag v0.9 abc1234# Lightweight tag'in yapısı:
# Sadece bir ref dosyası: .git/refs/tags/v1.0
# İçeriği: commit hash'i
cat .git/refs/tags/v1.0
# abc1234def5678ghi9012jkl3456mno78901Annotated Tag
Tag'e ek bilgi ekler: kim oluşturdu, ne zaman, açıklama mesajı. Git veritabanında ayrı bir tag nesnesi olarak saklanır.
# Annotated tag oluştur
git tag -a v1.0 -m "Version 1.0: İlk stabil sürüm"
# Belirli bir commit'e annotated tag
git tag -a v0.9 -m "Beta release" abc1234
# Tag bilgilerini görüntüle
git show v1.0git show v1.0 çıktısı:
tag v1.0
Tagger: Ahmet <ahmet@email.com>
Date: Sun Mar 2 14:30:00 2025 +0300
Version 1.0: İlk stabil sürüm
commit abc1234def5678
Author: Ahmet <ahmet@email.com>
Date: Sat Mar 1 18:00:00 2025 +0300
feat: Finalize v1.0 features
diff --git a/...Lightweight vs Annotated — Hangisini Kullanmalı?
┌──────────────────┬───────────────────┬───────────────────┐
│ Özellik │ Lightweight │ Annotated │
├──────────────────┼───────────────────┼───────────────────┤
│ Oluşturan bilgisi│ Yok │ Var (Tagger) │
│ Tarih │ Yok │ Var │
│ Mesaj │ Yok │ Var │
│ GPG imzalama │ Yok │ Var │
│ Git nesnesi │ Hayır (sadece ref)│ Evet (tag object) │
│ Komut │ git tag v1.0 │ git tag -a v1.0 │
│ Kullanım │ Geçici, kişisel │ Resmi sürümler │
└──────────────────┴───────────────────┴───────────────────┘Kural: Resmi sürümler için HER ZAMAN annotated tag kullan.
Lightweight tag'ler sadece geçici, kişisel işaretler için.💡 İpucu:
git describekomutu sadece annotated tag'lerle çalışır. CI/CD pipeline'larında sürüm numarası almak içingit describe --tagskullanılır — bu yüzden annotated tag önemlidir.
Tag Yönetimi
Tag'leri Listeleme
# Tüm tag'ler
git tag
# v0.1
# v0.2
# v1.0
# v1.1
# v2.0
# Pattern ile filtreleme
git tag -l "v1.*"
# v1.0
# v1.1
# Detaylı listeleme (tarih ve mesajla)
git tag -l -n1
# v0.1 Beta release
# v1.0 İlk stabil sürüm
# v1.1 Bug fix release
# v2.0 Major update with new UI
# Daha fazla mesaj satırı
git tag -l -n5Tag Silme
# Yerel tag silme
git tag -d v1.0
# Deleted tag 'v1.0' (was abc1234)
# Remote'tan tag silme
git push origin --delete v1.0
# veya
git push origin :refs/tags/v1.0Tag Push'lama
Tag'ler otomatik olarak push edilmez! Manuel olarak göndermelisin:
# Tek bir tag'i push et
git push origin v1.0
# Tüm tag'leri push et
git push origin --tags
# Sadece annotated tag'leri push et (önerilen)
git push origin --follow-tags⚠️ Dikkat:
git push --tagshem lightweight hem annotated tüm tag'leri gönderir. Production ortamında--follow-tagstercih et — sadece annotated tag'leri gönderir, geçici lightweight tag'leri göndermez.
Varsayılan Olarak Tag Push'lama
# Her push'ta annotated tag'leri otomatik gönder
git config --global push.followTags trueSemantic Versioning (SemVer)
Sürüm Numarası Nasıl Belirlenir?
"v1.0" dedik ama bu numara nasıl belirlenir? Rastgele mi? Hayır. Yazılım dünyasında Semantic Versioning (SemVer) adında bir standart var.
SemVer Formatı
MAJOR.MINOR.PATCH
│ │ │
│ │ └── Bug düzeltmeleri (geriye uyumlu)
│ └──────── Yeni özellikler (geriye uyumlu)
└────────────── Kırılma değişiklikleri (geriye uyumsuz!)Ne Zaman Hangi Numara Artar?
┌──────────────────────────────────────────────────────────┐
│ SEMANTIC VERSIONING KURALLARI │
├──────────────────────────────────────────────────────────┤
│ │
│ PATCH (1.0.X): Bug fix, güvenlik yaması │
│ ───────────────────────────────────── │
│ • Mevcut API değişmez │
│ • Kullanıcıların kodu bozulmaz │
│ • Örnek: 1.0.0 → 1.0.1 │
│ • "Login'deki typo düzeltildi" │
│ │
│ MINOR (1.X.0): Yeni özellik (geriye uyumlu) │
│ ───────────────────────────────────── │
│ • Yeni fonksiyonlar/endpoint'ler eklenir │
│ • Mevcut API bozulmaz │
│ • Örnek: 1.0.1 → 1.1.0 │
│ • "Dark mode eklendi" │
│ • PATCH sıfırlanır! │
│ │
│ MAJOR (X.0.0): Kırılma değişikliği (breaking change) │
│ ───────────────────────────────────── │
│ • API değişir, eski kod çalışmayabilir │
│ • Kullanıcıların kodunu güncellemesi gerekir │
│ • Örnek: 1.1.0 → 2.0.0 │
│ • "Tüm API endpoint'leri /v2/ altına taşındı" │
│ • MINOR ve PATCH sıfırlanır! │
│ │
└──────────────────────────────────────────────────────────┘SemVer Örnekleri
1.0.0 → İlk stabil sürüm
1.0.1 → Bug fix: login hatası düzeltildi
1.0.2 → Bug fix: güvenlik yaması
1.1.0 → Yeni özellik: arama fonksiyonu eklendi (PATCH sıfırlandı)
1.1.1 → Bug fix: arama sonuçlarında sıralama hatası
1.2.0 → Yeni özellik: dark mode eklendi
2.0.0 → Breaking change: API v2'ye geçiş (MINOR ve PATCH sıfırlandı)
2.0.1 → Bug fix: API v2'de küçük düzeltmePre-release ve Build Metadata
1.0.0-alpha → Alfa sürüm (erken geliştirme)
1.0.0-alpha.1 → İkinci alfa
1.0.0-beta → Beta sürüm (test aşaması)
1.0.0-beta.2 → İkinci beta
1.0.0-rc.1 → Release Candidate (son test)
1.0.0 → Stabil sürüm
Sıralama: alpha < beta < rc < release
Build metadata (bilgi amaçlı, sıralamayı etkilemez):
1.0.0+build.123
1.0.0+20250301v Öneki
# Teknik olarak SemVer "v" öneki içermez
# Ama pratikte çoğu proje "v" kullanır:
git tag v1.0.0 # Yaygın tercih ✅
git tag 1.0.0 # Teknik olarak doğru ama daha az yaygın
# Projenizde tutarlı olun — ya hep v kullanın ya hiçGit Describe — Otomatik Sürüm Bilgisi
git describe, en yakın annotated tag'e göre mevcut commit'in konumunu açıklar:
git describe
# v1.2.0-5-gabc1234
# │ │ │
# │ │ └── Commit hash'inin kısaltması
# │ └─────── Son tag'den bu yana 5 commit geçmiş
# └────────────── En yakın annotated tag# Eğer tam tag üzerindeysen:
git describe
# v1.2.0
# Tag'ler dahil (lightweight da)
git describe --tags
# Her zaman uzun format
git describe --long
# v1.2.0-0-gabc1234 (0 commit uzakta = tam tag üzerinde)Bu bilgi CI/CD'de çok yararlı:
# Docker image'a sürüm numarası ver
VERSION=$(git describe --tags --always)
docker build -t myapp:$VERSION .
# Package.json'daki sürümü güncelle
npm version $(git describe --tags --abbrev=0)GitHub Releases
GitHub Releases, tag'leri kullanıcı dostu bir arayüzle sunar. Tag'in üzerine açıklama, değişiklik listesi ve dosya ekleri (binary, installer) ekleyebilirsin.
Release vs Tag
Tag: Git seviyesinde bir işaretçi (commit'e referans)
Release: GitHub seviyesinde bir paket (tag + açıklama + dosyalar)
Her release bir tag'e dayanır.
Ama her tag bir release olmak zorunda değil.Release Oluşturma (Web Arayüzü)
1. Repo → Releases → "Draft a new release"
2. Tag seç (veya yeni oluştur)
3. Release başlığı: "v1.0.0 — İlk Stabil Sürüm"
4. Açıklama yaz (Changelog)
5. Dosya ekle (binary, installer, documentation)
6. Pre-release mı? (alpha/beta için)
7. "Publish release"Release Oluşturma (GitHub CLI)
# Basit release
gh release create v1.0.0 --title "v1.0.0" --notes "İlk stabil sürüm"
# Detaylı release
gh release create v1.0.0 \
--title "v1.0.0 — İlk Stabil Sürüm" \
--notes "## Yenilikler
- ✨ Kullanıcı kayıt sistemi
- 🔐 JWT authentication
- 📊 Dashboard
## Bug Düzeltmeleri
- 🐛 Login redirect sorunu çözüldü
- 🐛 Şifre sıfırlama maili düzeltildi
## Breaking Changes
- Yok
Full Changelog: https://github.com/user/repo/compare/v0.9...v1.0.0"
# Dosya ekleyerek release
gh release create v1.0.0 ./dist/app-linux.tar.gz ./dist/app-macos.zip \
--title "v1.0.0" \
--notes-file CHANGELOG.md
# Pre-release (alpha/beta)
gh release create v2.0.0-beta.1 --prerelease \
--title "v2.0.0 Beta 1" \
--notes "Bu bir beta sürümüdür. Production'da kullanmayın."
# Draft release (yayınlamadan hazırla)
gh release create v1.1.0 --draft \
--title "v1.1.0" \
--notes "Yayınlanmayı bekliyor..."Auto-generated Release Notes
GitHub, PR'lardan otomatik release notes oluşturabilir:
# GitHub'ın otomatik release notes'unu kullan
gh release create v1.1.0 --generate-notes
# Bu, son release'den bu yana merge edilen PR'ları listeler:
# ## What's Changed
# * feat: Add dark mode by @ahmet in #45
# * fix: Resolve login issue by @ayse in #47
# * docs: Update API documentation by @ali in #48
#
# ## New Contributors
# * @ali made their first contribution in #48
#
# **Full Changelog**: https://github.com/user/repo/compare/v1.0.0...v1.1.0Release Notes Yapılandırma
# .github/release.yml dosyası ile kategorileri özelleştir
cat > .github/release.yml << 'EOF'
changelog:
exclude:
labels:
- skip-changelog
authors:
- dependabot
categories:
- title: 🚀 Yeni Özellikler
labels:
- enhancement
- feature
- title: 🐛 Bug Düzeltmeleri
labels:
- bug
- fix
- title: 📝 Dokümantasyon
labels:
- documentation
- title: ⬆️ Bağımlılık Güncellemeleri
labels:
- dependencies
- title: 🏗️ Diğer Değişiklikler
labels:
- "*"
EOFBu yapılandırma ile auto-generated release notes şöyle görünür:
## 🚀 Yeni Özellikler
* feat: Add dark mode by @ahmet in #45
* feat: Add search functionality by @ayse in #50
## 🐛 Bug Düzeltmeleri
* fix: Resolve login issue by @ali in #47
## 📝 Dokümantasyon
* docs: Update API docs by @veli in #48CHANGELOG.md — Değişiklik Günlüğü
Release'lerle birlikte, projenin kök dizininde bir CHANGELOG.md dosyası tutmak yaygın bir pratiktir.
CHANGELOG Formatı (Keep a Changelog)
# Changelog
Tüm önemli değişiklikler bu dosyada belgelenir.
Bu proje [Semantic Versioning](https://semver.org/) kullanır.
## [Unreleased]
### Added
- Bildirim sistemi (PR #55)
## [1.1.0] - 2025-03-01
### Added
- Dark mode desteği (#45)
- Arama fonksiyonu (#50)
### Fixed
- Login redirect sorunu (#47)
### Changed
- Dashboard layout güncellendi (#49)
## [1.0.0] - 2025-02-01
### Added
- Kullanıcı kayıt sistemi
- JWT authentication
- Dashboard
- Profil sayfası
## [0.9.0] - 2025-01-15
### Added
- İlk beta sürümü
- Temel CRUD işlemleri
[Unreleased]: https://github.com/user/repo/compare/v1.1.0...HEAD
[1.1.0]: https://github.com/user/repo/compare/v1.0.0...v1.1.0
[1.0.0]: https://github.com/user/repo/compare/v0.9.0...v1.0.0
[0.9.0]: https://github.com/user/repo/releases/tag/v0.9.0CHANGELOG Kategorileri
Added — Yeni özellikler
Changed — Mevcut fonksiyonalitedeki değişiklikler
Deprecated — Yakında kaldırılacak özellikler
Removed — Kaldırılan özellikler
Fixed — Bug düzeltmeleri
Security — Güvenlik yamaları💡 İpucu: CHANGELOG'u elle yazmak yerine, conventional commits kullanarak otomatik oluşturabilirsin.
standard-version,conventional-changelog,release-pleasegibi araçlar commit mesajlarından otomatik CHANGELOG üretir.
Tag Workflow — Gerçek Dünya Akışı
Basit Sürüm Çıkma Akışı
# 1. Tüm değişiklikler main'de (veya release branch'te)
git checkout main
git pull origin main
# 2. Son testleri çalıştır
npm test
npm run build
# 3. Sürüm numarasını güncelle (package.json vb.)
npm version minor # veya major, patch
# Bu otomatik olarak:
# - package.json'daki version'ı günceller
# - Commit oluşturur: "1.1.0"
# - Tag oluşturur: v1.1.0
# 4. Push et (commit + tag)
git push origin main --follow-tags
# 5. GitHub Release oluştur
gh release create v1.1.0 --generate-notesRelease Branch ile Sürüm Çıkma
main ────A───B───C───D───E───F───G───H
\
release/1.0 ──── C'──D'──v1.0.0──v1.0.1
↑
hotfix
Bu yaklaşımda:
1. main'den release branch açılır
2. Release branch'te son düzeltmeler yapılır
3. Hazır olunca tag konur ve release yapılır
4. Hotfix'ler de bu branch'e uygulanır
5. Değişiklikler main'e geri merge edilir# Release branch oluştur
git checkout -b release/1.0 main
# Son düzeltmeleri yap
git commit -m "fix: Final adjustments for v1.0"
# Tag koy
git tag -a v1.0.0 -m "Release v1.0.0"
# Push
git push origin release/1.0 --follow-tags
# Main'e geri merge
git checkout main
git merge release/1.0Pratik: Tam Sürüm Döngüsü
# ═══════════════════════════════════════
# PROJEYİ HAZIRLA
# ═══════════════════════════════════════
mkdir version-demo && cd version-demo
git init
# İlk commit
echo '{ "name": "my-app", "version": "0.1.0" }' > package.json
echo "# My App" > README.md
git add .
git commit -m "Initial commit"
# İlk tag: v0.1.0 (alpha)
git tag -a v0.1.0 -m "Alpha release"
# ═══════════════════════════════════════
# GELİŞTİRME
# ═══════════════════════════════════════
# Yeni özellik ekle
echo "console.log('Hello!');" > index.js
git add .
git commit -m "feat: Add main entry point"
echo "function login() {}" > auth.js
git add .
git commit -m "feat: Add authentication"
# ═══════════════════════════════════════
# İLK STABIL SÜRÜM
# ═══════════════════════════════════════
# package.json güncelle
cat > package.json << 'EOF'
{ "name": "my-app", "version": "1.0.0" }
EOF
git add .
git commit -m "chore: Bump version to 1.0.0"
# Annotated tag
git tag -a v1.0.0 -m "v1.0.0 — İlk Stabil Sürüm
Yenilikler:
- Ana giriş noktası eklendi
- Kullanıcı kimlik doğrulama sistemi
- Temel proje yapısı"
# ═══════════════════════════════════════
# BUG FIX
# ═══════════════════════════════════════
echo "function login(email, password) {}" > auth.js
git add .
git commit -m "fix: Add parameters to login function"
cat > package.json << 'EOF'
{ "name": "my-app", "version": "1.0.1" }
EOF
git add .
git commit -m "chore: Bump version to 1.0.1"
git tag -a v1.0.1 -m "v1.0.1 — Bug fix: login parametreleri eklendi"
# ═══════════════════════════════════════
# YENİ ÖZELLİK
# ═══════════════════════════════════════
echo "function search(query) {}" > search.js
git add .
git commit -m "feat: Add search functionality"
cat > package.json << 'EOF'
{ "name": "my-app", "version": "1.1.0" }
EOF
git add .
git commit -m "chore: Bump version to 1.1.0"
git tag -a v1.1.0 -m "v1.1.0 — Arama özelliği eklendi"
# ═══════════════════════════════════════
# KONTROL
# ═══════════════════════════════════════
# Tüm tag'ler
git tag -l -n1
# v0.1.0 Alpha release
# v1.0.0 v1.0.0 — İlk Stabil Sürüm
# v1.0.1 v1.0.1 — Bug fix: login parametreleri eklendi
# v1.1.0 v1.1.0 — Arama özelliği eklendi
# Şu an neredeyiz?
git describe
# v1.1.0
# Grafik tarihçe
git log --oneline --graph --decorate
# * abc1234 (HEAD -> main, tag: v1.1.0) chore: Bump version to 1.1.0
# * def5678 feat: Add search functionality
# * ghi9012 (tag: v1.0.1) chore: Bump version to 1.0.1
# * jkl3456 fix: Add parameters to login function
# * mno7890 (tag: v1.0.0) chore: Bump version to 1.0.0
# * pqr1234 feat: Add authentication
# * stu5678 feat: Add main entry point
# * vwx9012 (tag: v0.1.0) Initial commit
# İki sürüm arasındaki değişiklikler
git log v1.0.0..v1.1.0 --oneline
# abc1234 chore: Bump version to 1.1.0
# def5678 feat: Add search functionality
# ghi9012 chore: Bump version to 1.0.1
# jkl3456 fix: Add parameters to login functionTag ile İlgili İleri Konular
GPG İmzalı Tag'ler
Güvenlik açısından tag'leri GPG anahtarınla imzalayabilirsin. Bu, tag'in gerçekten senden geldiğini kanıtlar:
# GPG anahtarını ayarla
git config --global user.signingkey YOUR_GPG_KEY_ID
# İmzalı tag oluştur
git tag -s v1.0.0 -m "Signed release v1.0.0"
# İmzayı doğrula
git tag -v v1.0.0
# gpg: Signature made Sun Mar 2 14:30:00 2025
# gpg: Good signature from "Ahmet <ahmet@email.com>"Tag'i Taşımak (Önerilmez ama Bazen Gerekli)
# Yanlış commit'e tag koyduysan:
git tag -d v1.0.0 # Yerel tag'i sil
git push origin --delete v1.0.0 # Remote tag'i sil
git tag -a v1.0.0 DOĞRU_COMMIT -m "..." # Doğru commit'e koy
git push origin v1.0.0 # Tekrar push et⚠️ Dikkat: Yayınlanmış tag'leri taşımak, bu tag'e bağlı CI/CD pipeline'ları, Docker image'ları ve kullanıcı referanslarını bozabilir. Mümkünse yeni bir patch sürüm çıkar (v1.0.1).
Belirli Bir Tag'e Checkout
# Tag'in işaret ettiği commit'e git
git checkout v1.0.0
# "Detached HEAD" durumuna geçersin — branch'te değilsin!
# Bu noktadan yeni bir branch oluşturmak istersen:
git checkout -b hotfix/v1.0.0-fix v1.0.0Yaygın Hatalar
1. Tag'i Push Etmeyi Unutmak
# ❌ Tag oluşturdun ama push etmedin
git tag -a v1.0.0 -m "Release"
git push origin main
# Tag remote'a gitmedi!
# ✅ Tag'i de push et
git push origin v1.0.0
# veya
git push origin --follow-tags2. Yanlış Sürüm Numarası
# ❌ Breaking change yaptın ama minor sürüm artırdın
# v1.0.0 → v1.1.0 (Yanlış! Breaking change = MAJOR)
# ✅ Doğrusu:
# v1.0.0 → v2.0.0 (Breaking change = MAJOR artmalı)3. CHANGELOG Güncellemesini Unutmak
# ❌ Tag ve release var ama CHANGELOG güncellenmedi
# Kullanıcılar ne değişti bilmiyor
# ✅ Tag koymadan ÖNCE CHANGELOG'u güncelle
# veya conventional-changelog gibi araçlarla otomatize etÖzet
Bu derste sürüm yönetiminin tüm boyutlarını öğrendik:
Tag, commit tarihçesindeki belirli bir noktayı kalıcı olarak işaretler — branch'ler hareket eder, tag'ler sabit kalır
Lightweight tag sadece bir işaretçi iken, annotated tag kim oluşturduğu, ne zaman ve neden bilgisini de içerir — resmi sürümler için her zaman annotated tag kullan
Semantic Versioning (MAJOR.MINOR.PATCH) ile sürüm numaralarını anlamlı kıl — breaking change = MAJOR, yeni özellik = MINOR, bug fix = PATCH
GitHub Releases, tag'lerin üzerine açıklama ve dosya ekleri ekler —
--generate-notesile PR'lardan otomatik release notes oluşturabilirsinCHANGELOG.md ile değişiklikleri belgelemen gerekir — "Keep a Changelog" formatını takip et
`git describe` ile mevcut commit'in en yakın tag'e göre konumunu öğrenirsin — CI/CD'de sürüm bilgisi almak için kritik
Bir sonraki bölümde Git'in motorunun kapağını açıyoruz: Git Internals — blob, tree, commit nesneleri ve Git'in aslında nasıl çalıştığı.
AI Asistan
Sorularını yanıtlamaya hazır