Cherry-Pick ve Bisect
Giriş — Git'in Dedektif Araçları
Yazılım geliştirme her zaman düz bir yol değildir. Bazen acil bir hotfix'i sadece production branch'ine almak istersin — tüm feature branch'i değil, sadece tek bir commit'i. Bazen "bu bug ne zaman girdi?" diye sorar ve binlerce commit arasından suçluyu bulman gerekir. Bazen de bir satıra bakıp "bunu kim, neden yazdı?" diye merak edersin.
İşte bu üç senaryo için Git'in üç güçlü aracı var: cherry-pick, bisect ve blame. Bunlar Git'in "dedektif araçları"dır — tarihçeyi analiz eder, spesifik commit'leri taşır ve sorunların kaynağını bulursun.
Git Cherry-Pick — Seçici Hasat
Analoji — Kiraz Toplama
Bir kiraz bahçesin var. Ağaçlar dolu kirazla. Ama sen tüm ağacı söküp taşımak istemiyorsun — sadece en güzel, en olgun kirazları seçip topluyorsun.
git cherry-pick tam olarak bunu yapar: bir branch'ten belirli bir commit'i alıp başka bir branch'e uygular. Tüm branch'i merge etmeden, sadece istediğin değişikliği taşırsın.
feature A───B───C───D───E
↑
Bu commit'i istiyorum!
main X───Y───Z───C' (C commit'i kopyalandı)Temel Kullanım
# Önce hedef branch'e geç
git checkout main
# Belirli bir commit'i bu branch'e uygula
git cherry-pick abc1234
# Birden fazla commit
git cherry-pick abc1234 def5678
# Bir aralıktaki commit'ler (abc dahil değil, ghi dahil)
git cherry-pick abc1234..ghi9012
# Bir aralıktaki commit'ler (abc dahil)
git cherry-pick abc1234^..ghi9012Terminal çıktısı:
[main 5f3d2e1] Fix critical payment bug
Date: Sun Mar 2 14:30:00 2025 +0300
1 file changed, 5 insertions(+), 2 deletions(-)Yaygın Cherry-Pick Senaryoları
Senaryo 1: Hotfix — Acil Düzeltme
# Bug fix develop branch'inde yapıldı ama production'da da lazım
# develop branch'inde:
git log --oneline
# abc1234 feat: Add new feature
# def5678 fix: Fix critical payment calculation bug ← BU!
# ghi9012 feat: Redesign checkout page
# Production branch'ine geç
git checkout production
# Sadece bug fix commit'ini al
git cherry-pick def5678
# Push et
git push origin productionDurum:
develop ... ─── feat ─── fix ─── feat
│
cherry-pick
│
▼
production ... ─────────── fix' ← Sadece bu commit geldiSenaryo 2: Yanlış Branch'e Commit Etme
# Ups! main branch'e commit ettim, feature branch'e yapmalıydım
# main'deki yanlış commit'in hash'ini not al
git log --oneline -1
# abc1234 feat: Add search functionality
# Feature branch'e geç
git checkout feature/search
# Commit'i buraya al
git cherry-pick abc1234
# main'e dönüp yanlış commit'i geri al
git checkout main
git reset --hard HEAD~1Senaryo 3: Birden Fazla Branch'e Aynı Değişikliği Uygulama
# Güvenlik yaması hem v1.x hem v2.x branch'lerine lazım
git checkout release/v1.x
git cherry-pick SECURITY_FIX_HASH
git checkout release/v2.x
git cherry-pick SECURITY_FIX_HASHCherry-Pick Seçenekleri
# Commit'i uygula ama commit etme (staging'de bırak)
git cherry-pick --no-commit abc1234
# Bu, birden fazla cherry-pick'i tek commit'te birleştirmek için yararlı
# Commit mesajına kaynak bilgisi ekle
git cherry-pick -x abc1234
# Mesaja "(cherry picked from commit abc1234)" notu eklenir
# Merge conflict çıkarsa:
git cherry-pick abc1234
# CONFLICT!
# Çöz ve devam et:
git add .
git cherry-pick --continue
# Vazgeç:
git cherry-pick --abort⚠️ Dikkat: Cherry-pick yeni bir commit oluşturur (farklı hash). Aynı değişiklik iki branch'te farklı commit olarak bulunur. İleride bu iki branch merge edildiğinde conflict çıkabilir. Cherry-pick'i sık kullanıyorsan, belki branch stratejini gözden geçirmelisin.
Git Bisect — İkili Arama ile Bug Bulma
Analoji — Sözlükte Kelime Arama
Bir sözlükte "merkür" kelimesini arıyorsun. Sözlüğü ortadan açarsın. "M" harfi mi? Hayırsa sol tarafta mı sağ tarafta mı karar verirsin. Tekrar ortadan açarsın. Birkaç adımda binlerce sayfa arasından doğru sayfayı bulursun.
Bu ikili arama (binary search) algoritmasıdır ve git bisect aynı mantığı commit tarihçesine uygular:
1000 commit var. Bug bir yerde girdi.
İkili arama ile: log₂(1000) ≈ 10 adımda suçlu commit'i bulursun!Temel Kullanım
# 1. Bisect'i başlat
git bisect start
# 2. Şu anki durum: BUG VAR (bad)
git bisect bad
# 3. Bu commit'te bug yoktu (good) — bildiğin son çalışan commit
git bisect good v1.0
# veya
git bisect good abc1234Bisect ortadaki commit'e checkout yapar:
Bisecting: 500 revisions left to test after this (roughly 9 steps)
[def5678] feat: Add caching layer# 4. Test et. Bug var mı?
# Bug VARSA:
git bisect bad
# Bug YOKSA:
git bisect goodBisect aralığı daraltır ve yeni bir commit'e checkout yapar. Bu süreci birkaç adım tekrarla:
Bisecting: 250 revisions left to test after this (roughly 8 steps)
[ghi9012] refactor: Update database queries
# Test et...
git bisect good
Bisecting: 125 revisions left to test after this (roughly 7 steps)
[jkl3456] feat: Change user validation
# Test et...
git bisect bad
# ... birkaç adım daha ...
# SONUÇ:
abc1234def5678ghi9012 is the first bad commit
commit abc1234def5678ghi9012
Author: Ahmet <ahmet@email.com>
Date: Mon Feb 15 14:30:00 2025 +0300
fix: Update email regex pattern
Changed email validation regex to accept
new TLD formats.# 5. Bisect'i bitir (önceki branch'e dön)
git bisect resetBisect Görsel Akışı
Commit tarihçesi (1000 commit):
good bad
▼ ▼
A────────────────────M────────────────────────Z
↑
1. Adım: Ortayı test et
│
┌────────┴────────┐
│ │
good ise bad ise
│ │
M────────Z A────────M
↑ ↑
2. Adım 2. Adım
│ │
...devam... ...devam...
10 adımda 1000 commit arasından suçluyu bulursun!Otomatik Bisect (Script ile)
Test scriptinin varsa, bisect'i tamamen otomatize edebilirsin:
# Test scripti: başarılıysa exit 0, başarısızsa exit 1
git bisect start
git bisect bad HEAD
git bisect good v1.0
# Otomatik çalıştır — Git her commit'te testi koşar
git bisect run npm test
# veya özel bir test scripti:
git bisect run ./test-login.sh
# veya belirli bir test:
git bisect run pytest tests/test_payment.pyrunning npm test
Tests passed ✅ → git bisect good (otomatik)
running npm test
Tests FAILED ❌ → git bisect bad (otomatik)
... 8 adım sonra ...
abc1234 is the first bad commit💡 İpucu: Otomatik bisect, CI'da test altyapısı olan projeler için mükemmeldir. "Bu test ne zaman kırıldı?" sorusuna dakikalar içinde cevap alırsın.
Bisect ile Skip
Bazı commit'ler test edilemez (build kırık, derleme hatası vb.):
# Bu commit test edilemiyor, atla
git bisect skip
# Bisect bir sonraki yakın commit'i denerGit Blame — Satır Dedektifi
Analoji — Parmak İzi Analizi
Bir olay yerinde (kodda) sorun var. Her satırın üzerinde bir "parmak izi" var: kim yazdı, ne zaman yazdı, hangi commit'le yazdı. git blame bu parmak izlerini okur.
Temel Kullanım
git blame src/auth.jsÇıktı:
abc1234 (Ahmet 2025-01-15 14:30:00 +0300 1) const jwt = require('jsonwebtoken');
abc1234 (Ahmet 2025-01-15 14:30:00 +0300 2) const bcrypt = require('bcrypt');
abc1234 (Ahmet 2025-01-15 14:30:00 +0300 3)
def5678 (Ayşe 2025-02-01 09:15:00 +0300 4) const SECRET = process.env.JWT_SECRET;
def5678 (Ayşe 2025-02-01 09:15:00 +0300 5) const EXPIRES_IN = '24h';
abc1234 (Ahmet 2025-01-15 14:30:00 +0300 6)
ghi9012 (Ali 2025-02-20 16:45:00 +0300 7) async function login(email, password) {
abc1234 (Ahmet 2025-01-15 14:30:00 +0300 8) const user = await User.findOne({ email });
ghi9012 (Ali 2025-02-20 16:45:00 +0300 9) if (!user) throw new Error('User not found');
abc1234 (Ahmet 2025-01-15 14:30:00 +0300 10)
jkl3456 (Veli 2025-03-01 11:00:00 +0300 11) const valid = await bcrypt.compare(password, user.hash);
jkl3456 (Veli 2025-03-01 11:00:00 +0300 12) if (!valid) throw new Error('Invalid password');
abc1234 (Ahmet 2025-01-15 14:30:00 +0300 13)
abc1234 (Ahmet 2025-01-15 14:30:00 +0300 14) return jwt.sign({ id: user.id }, SECRET, { expiresIn: EXPIRES_IN });
abc1234 (Ahmet 2025-01-15 14:30:00 +0300 15) }Her satır için görebilirsin: Commit hash → Yazar → Tarih → Satır numarası → Kod
Blame Seçenekleri
# Belirli satır aralığı
git blame -L 7,15 src/auth.js
# E-posta göster
git blame -e src/auth.js
# Kısa hash
git blame --abbrev=7 src/auth.js
# Sadece belirli commit'ten sonraki değişiklikleri göster
git blame --since="2025-02-01" src/auth.js
# Whitespace değişikliklerini yoksay
git blame -w src/auth.js
# Satır taşımalarını tespit et (aynı dosya içinde)
git blame -M src/auth.js
# Dosyalar arası kopyalamayı tespit et
git blame -C src/auth.js
# Daha derine in (dosya taşıma ve kopyalama dahil)
git blame -C -C -C src/auth.js-w Bayrağı Neden Önemli?
# Birisi sadece indentation değiştirdi (tab → space veya reformatting)
# Normal blame bunu gösterir:
git blame src/app.js
# jkl3456 (Format Bot 2025-02-28) function login() {
# -w ile whitespace değişikliklerini yoksay:
git blame -w src/app.js
# abc1234 (Ahmet 2025-01-15) function login() {
# Gerçek yazarı görürsün!VS Code'da Blame
VS Code'da GitLens eklentisini yükle. Her satırın yanında kimin yazdığını, ne zaman yazdığını gösterir:
14 │ return jwt.sign({ id: user.id }, SECRET);
Ahmet, 6 weeks ago • feat: Add JWT authenticationBlame + Show = Tam Hikaye
Blame ile suçlu commit'i buldun. Şimdi o commit'in detayını görmek istiyorsun:
# Blame ile satırın commit'ini bul
git blame -L 11,12 src/auth.js
# jkl3456 (Veli 2025-03-01) const valid = await bcrypt.compare(...)
# O commit'in detayını gör
git show jkl3456commit jkl3456...
Author: Veli <veli@email.com>
Date: Sat Mar 1 11:00:00 2025 +0300
refactor: Use bcrypt instead of plain text comparison
Security improvement: password comparison now uses bcrypt
instead of plain text comparison.
diff --git a/src/auth.js b/src/auth.js
--- a/src/auth.js
+++ b/src/auth.js
@@ -8,7 +8,8 @@ async function login(email, password) {
const user = await User.findOne({ email });
if (!user) throw new Error('User not found');
- if (password !== user.password) throw new Error('Wrong password');
+ const valid = await bcrypt.compare(password, user.hash);
+ if (!valid) throw new Error('Invalid password');Artık tam hikayeyi biliyorsun: kim, ne zaman, neden, nasıl.
Git Log ile Forensik (Adli) Analiz
Bu üç aracı tamamlayan bazı git log teknikleri:
# Belirli bir dosyanın tüm değişiklik tarihçesi
git log --follow -- src/auth.js
# Belirli bir fonksiyonun tarihçesi (Git fonksiyon sınırlarını anlar!)
git log -L :login:src/auth.js
# Belirli bir metin parçasının tarihçesi
git log -S "bcrypt" -- src/auth.js
# "bcrypt" kelimesinin eklendiği veya silindiği commit'ler
# Belirli bir regex pattern'in tarihçesi
git log -G "TODO|FIXME|HACK"
# Bu pattern'leri içeren değişiklikler
# Belirli bir geliştiricinin yaptığı değişiklikler
git log --author="Ahmet" --since="2025-01-01" --until="2025-02-01"
# Commit mesajında belirli kelime arama
git log --grep="payment" --oneline
# Grafik görünüm ile
git log --all --oneline --graph --decorategit log -S vs git log -G
# -S (pickaxe): Belirli bir string'in EKLENME veya SİLİNME sayısını değiştiren commit'ler
git log -S "API_KEY" --oneline
# "API_KEY" ilk kez eklenen veya tamamen silinen commit'leri bulur
# -G (regex): Belirli bir pattern'i DEĞIŞTIREN commit'ler
git log -G "password.*plain" --oneline
# "password" ve "plain" kelimelerini içeren satırları değiştiren commit'lerÜç Aracı Birlikte Kullanma — Gerçek Dünya Senaryosu
Diyelim ki production'da bir bug keşfettin. Nasıl çözersin?
# ADIM 1: Bug'ın ne zaman girdiğini bul (bisect)
git bisect start
git bisect bad HEAD # Şu an bozuk
git bisect good v2.0 # v2.0'da çalışıyordu
# ... test et, good/bad de ...
# SONUÇ: abc1234 ilk hatalı commit
# ADIM 2: O commit'te ne değişti? (show)
git show abc1234
# Aha! src/payment.js dosyasında vergi hesabını değiştirmiş
# ADIM 3: O satırı kim yazdı? (blame)
git blame src/payment.js -L 42,50
# abc1234 (Intern Mehmet 2025-02-28) const tax = price * 0.08;
# Not: Vergi %18 olmalıydı, %8 yazılmış!
# ADIM 4: Düzeltme yap
git checkout main
# ... fix the bug ...
git commit -m "fix: Correct tax rate from 8% to 18%
The tax rate was incorrectly changed in abc1234.
Found via git bisect + git blame.
Fixes #142"
# ADIM 5: Hotfix'i production'a cherry-pick et
git checkout production
git cherry-pick HEAD # main'deki son commit'i al
git push origin productionZaman çizelgesi:
─────────────────────────────────────────────────────
bug girdi bug keşfedildi
↓ ↓
v2.0 ─── ... ─── abc1234 ─── ... ─── HEAD
↑ ↑
bisect buldu blame: Kim? Neden?
↓
cherry-pick → productionCherry-Pick vs Merge vs Rebase
Üç farklı "commit taşıma" yöntemi var. Ne zaman hangisini kullanacağını bilmek önemli:
Cherry-pick: TEK bir commit'i başka bir branch'e kopyala
→ Hotfix, acil düzeltme, spesifik değişiklik
Merge: İki branch'i birleştir (TÜM commit'ler)
→ Feature tamamlandığında, branch birleştirme
Rebase: Branch'in base'ini değiştir (TÜM commit'ler yeniden uygulanır)
→ Branch güncelleme, tarihçe temizleme┌──────────────┬────────────────┬──────────────┬──────────────┐
│ │ Cherry-pick │ Merge │ Rebase │
├──────────────┼────────────────┼──────────────┼──────────────┤
│ Kaç commit? │ 1 (veya az) │ Hepsi │ Hepsi │
│ Yeni hash? │ Evet │ Merge commit │ Evet │
│ Tarihçe │ Kopya oluşur │ Korunur │ Yeniden yazılır│
│ Kullanım │ Hotfix, patch │ Birleştirme │ Güncelleme │
│ Risk │ Düşük │ Düşük │ Orta-Yüksek │
└──────────────┴────────────────┴──────────────┴──────────────┘Yaygın Hatalar
1. Cherry-pick Bağımlılık Sorunu
# ❌ Commit C, Commit B'ye bağımlı. Sadece C'yi cherry-pick edersen
# C'deki kod B'deki tanımlamalara referans veriyor — hata!
# ✅ Bağımlı commit'leri birlikte al:
git cherry-pick B C
# veya
git cherry-pick B^..C2. Bisect'te Yanlış good/bad İşaretleme
# ❌ Yanlışlıkla "good" yerine "bad" dedin
# Bisect yanlış yöne gider!
# ✅ Yanlış işaretlediğini fark ettiysen:
git bisect log # Tarihçeyi gör
git bisect reset # Baştan başla3. Blame'de Format Commit'ini Suçlama
# ❌ blame gösteriyor: "Format Bot reformatted all files"
# Bu, gerçek yazarı gizliyor!
# ✅ -w ile whitespace'i yoksay:
git blame -w src/auth.js
# ✅ .git-blame-ignore-revs ile belirli commit'leri yoksay:
echo "abc1234 # Bulk formatting" >> .git-blame-ignore-revs
git config blame.ignoreRevsFile .git-blame-ignore-revs💡 İpucu:
.git-blame-ignore-revsdosyasını repo'ya commit'le. GitHub da bu dosyayı otomatik olarak tanır ve web arayüzündeki blame görünümünde bu commit'leri yoksayar.
Özet
Bu derste Git'in dedektif araçlarını öğrendik:
Cherry-pick — Belirli bir commit'i bir branch'ten alıp başka bir branch'e uygular. Hotfix'ler, acil düzeltmeler ve spesifik değişiklik taşıma için idealdir
Bisect — İkili arama algoritmasıyla 1000 commit arasından bile 10 adımda bug'ı tanıtan commit'i bulur.
git bisect runile tamamen otomatize edilebilirBlame — Her satırın kim tarafından, ne zaman, hangi commit'le yazıldığını gösterir.
-wile format değişikliklerini yoksayabilirsinForensik analiz —
git log -S,git log -G,git log -Lile kodun tarihçesini derinlemesine inceleyebilirsinBu üç araç birlikte kullanıldığında bug bulma → kaynak tespit → düzeltme → taşıma döngüsü çok verimli olur
Bir sonraki derste yarım kalan işlerini güvenle saklayabileceğin araçları göreceğiz: git stash ve git worktree.
AI Asistan
Sorularını yanıtlamaya hazır