Git Cherry-Pick ile Seçili Commitleri Aktarma
Üretim ortamında bir hotfix dağıtmanız gerekiyor ama main branch’iniz henüz test edilmemiş yarım kalmış özelliklerle dolu. Tam bu anda git cherry-pick aklınıza gelmiyor mu? Çünkü bu komutu iyi bilmiyorsanız ya tüm branch’i merge etmek zorunda kalırsınız ya da değişiklikleri elle kopyalarsınız ki ikisi de can sıkıcı hatalar doğurur. Bu yazıda cherry-pick’in nasıl çalıştığını, ne zaman kullanmanız gerektiğini ve gerçek dünya senaryolarında nasıl uyguladığımı aktaracağım.
Cherry-Pick Nedir, Ne İşe Yarar
git cherry-pick, bir branch’teki belirli commit’leri alıp başka bir branch’e uygulamanızı sağlar. Merge veya rebase’den farkı şu: tüm branch geçmişini değil, yalnızca seçtiğiniz commit’leri taşır. Her commit, kendi değişiklik setini (diff) içerir ve cherry-pick bu diff’i hedef branch’e yeniden uygular.
Şöyle düşünün: feature/payment branch’inizde 15 commit var. Bu commit’lerin 13’ü hâlâ geliştirme aşamasında, ama 2 tanesi kritik bir güvenlik düzeltmesi. Sadece o 2 commit’i main‘e almak istiyorsunuz. İşte tam burada cherry-pick devreye giriyor.
Teknik olarak cherry-pick, kaynak commit’in parent’ıyla kendisi arasındaki farkı alır ve bunu hedef branch’in üstüne uygular. Bu yüzden yeni bir commit hash’i oluşturur; aynı değişiklikler farklı bir ebeveyne uygulandığı için hash değişmek zorunda.
Temel Kullanım
En basit hali şu:
git checkout main
git cherry-pick abc1234
Bu komut abc1234 hash’li commit’i alır ve şu an bulunduğunuz branch’e uygular. Commit hash’in tamamını yazmanıza gerek yok, ilk 7 karakter genellikle yeterli.
Birden fazla commit için:
git cherry-pick abc1234 def5678 ghi9012
Bu şekilde sırayla üç commit’i uygularsınız. Sıralama önemli: commit’leri kronolojik sıraya göre vermenizi tavsiye ederim, aksi hâlde çakışma riskini artırırsınız.
Peki commit hash’leri nasıl bulursunuz? git log burada en iyi arkadaşınız:
git log --oneline feature/payment
# Çıktı şöyle görünür:
# a3f8c21 Fix SQL injection vulnerability in payment module
# 7b2d459 Add input validation for card number
# 3e1a890 Refactor payment service (WIP)
# ...
--oneline parametresi her commit’i tek satırda gösterir, hash ve commit mesajıyla. Buradan ihtiyacınız olan commit’leri seçip cherry-pick’e veriyorsunuz.
Aralık Belirterek Kullanım
Ardışık birkaç commit’i tek tek yazmak yerine aralık sözdizimini kullanabilirsiniz:
git cherry-pick abc1234..ghi9012
Dikkat: bu sözdizimi abc1234‘ü dahil etmez, abc1234‘ten sonraki commit’ten başlayarak ghi9012‘ye kadar (dahil) uygular. Başlangıç commit’ini de dahil etmek istiyorsanız:
git cherry-pick abc1234^..ghi9012
^ karakteri “bu commit’in parent’ı” anlamına gelir, dolayısıyla aralık abc1234‘ten itibaren başlar.
Bunu pratikte görelim. Diyelim ki feature/api branch’inizde şu geçmiş var:
git log --oneline feature/api
# Output:
# f9a2b3c Add rate limiting to API endpoints
# e8b1a2d Fix authentication token expiry bug
# d7c0912 Improve error response format
# c6b8801 Add new user endpoint (incomplete)
# b5a7690 Initial API refactor
Sadece d7c0912 ile e8b1a2d ve f9a2b3c commit’lerini almak istiyorsunuz:
git checkout main
git cherry-pick d7c0912^..f9a2b3c
Sık Karşılaşılan Parametreler
Cherry-pick’in birkaç önemli seçeneği var, bunları bilmeden işlerinizi zorlaştırabilirsiniz:
–no-commit (-n): Değişiklikleri uygular ama commit oluşturmaz. Birden fazla cherry-pick’i tek commit’e toplamak istediğinizde kullanışlı.
–edit (-e): Commit mesajını uygulamadan önce düzenlemenizi sağlar. Hedef branch’e özgü bir not eklemek istediğinizde işe yarar.
–signoff (-s): Commit mesajına “Signed-off-by” satırı ekler. Bazı projelerde zorunlu.
–mainline (-m): Merge commit’leri cherry-pick ederken hangi parent’ın “ana hat” olduğunu belirtir. Merge commit’lerle çalışırken bunu açıklayacağım.
–abort: Cherry-pick sırasında çakışma olduğunda işlemi tamamen iptal eder ve başlangıç durumuna döner.
–continue: Çakışmaları çözdükten sonra işleme devam eder.
–skip: Mevcut commit’i atlayarak bir sonrakine geçer. Zaten uygulanmış değişikliklerde kullanılır.
Gerçek Dünya Senaryosu 1: Hotfix Yayınlama
Bu senaryoyu onlarca kez yaşadım. develop branch’i üzerinde yoğun çalışma var, main production’da. Bir müşteri kritik bir bug bildiriyor.
# Şu anki durumu görelim
git log --oneline develop | head -10
# 7f3a891 Add new dashboard widgets (WIP)
# 6e2b780 Integrate third-party analytics
# 5d1c679 Fix null pointer exception in user service <-- bunu istiyoruz
# 4c0a568 Refactor authentication module (breaking changes)
# 3b9f457 Update dependencies
# Main branch'e geçip cherry-pick yapalım
git checkout main
git cherry-pick 5d1c679
# Başarılı olursa şunu görürsünüz:
# [main 8a4d231] Fix null pointer exception in user service
# 1 file changed, 3 insertions(+), 1 deletion(-)
# Production'a deploy
git push origin main
Bu kadar. develop‘taki karmaşıklığa dokunmadan sadece ilgili düzeltmeyi production’a taşıdınız.
Gerçek Dünya Senaryosu 2: Yanlış Branch’e Yapılan Commit
Bu da çok sık yaşanan bir durum: aceleyle yanlış branch’tesiniz ve birkaç commit yaptınız. feature/new-ui yerine feature/old-ui branch’indesiniz.
# Yanlış branch'teki son 3 commit'i görelim
git log --oneline feature/old-ui | head -5
# c3e1a20 Add responsive layout for mobile
# b2d0f19 Update color scheme
# a1c9e08 Redesign header component
# 9b8d7f6 (bu commit'ten öncesi doğru çalışma)
# Doğru branch'e geçip commit'leri taşıyalım
git checkout feature/new-ui
git cherry-pick a1c9e08^..c3e1a20
# Yanlış branch'teki commit'leri geri alalım
git checkout feature/old-ui
git reset --hard 9b8d7f6
Dikkat: git reset --hard geri dönüşü olmayan bir işlem. Push edilmemiş commit’lerde kullanın. Eğer commit’leri zaten push ettiyseniz ekibinizle koordineli hareket etmeniz gerekiyor.
Çakışmalarla Başa Çıkmak
Cherry-pick en sık çakışmalar (conflict) yüzünden karmaşık hâle gelir. Hedef branch’te cherry-pick etmeye çalıştığınız dosyalar değiştirilmişse Git otomatik çözemez ve sizin müdahalenizi ister.
git cherry-pick e8b1a2d
# Auto-merging src/auth/token.js
# CONFLICT (content): Merge conflict in src/auth/token.js
# error: could not apply e8b1a2d... Fix authentication token expiry bug
# hint: After resolving the conflicts, mark them with
# hint: "git add <paths>" or "git rm <paths>"
# hint: and run "git cherry-pick --continue".
Bu durumda yapmanız gerekenler adım adım:
# Çakışan dosyaları görmek için
git status
# Her iki tarafın değişikliklerini görmek için
git diff
# Çakışan dosyayı düzenleyin (editörünüzde açın)
# <<<<<<< HEAD
# (mevcut branch'teki kod)
# =======
# (cherry-pick etmeye çalıştığınız kod)
# >>>>>>> e8b1a2d
# Dosyayı düzenledikten sonra
git add src/auth/token.js
# Cherry-pick'e devam edin
git cherry-pick --continue
Eğer vazgeçmek isterseniz:
git cherry-pick --abort
Bu komut sizi cherry-pick öncesi temiz duruma geri götürür.
–no-commit ile Değişiklikleri Birleştirme
Bazen birden fazla commit’i tek bir mantıklı commit olarak toplamak istersiniz. Örneğin küçük typo düzeltmelerini ana commit’le birleştirmek gibi.
# İki commit'i staged hâlde uygula ama commit etme
git cherry-pick --no-commit abc1234 def5678
# Şimdi her şey staging area'da, tek commit oluşturun
git status
# Changes to be committed:
# modified: src/utils/helper.js
# modified: src/utils/validator.js
git commit -m "Apply combined fixes from feature branch"
Bu yaklaşım özellikle “bu 5 commit aslında tek bir özelliğin parçasıydı, neden ayrı commit yapıldı ki” diye düşündüğünüz durumlarda çok işe yarıyor.
Merge Commit’leri Cherry-Pick Etmek
Merge commit’lerini cherry-pick etmek biraz farklı çalışır. Merge commit’inin iki parent’ı vardır ve Git hangisinin “ana hat” olduğunu bilmek ister.
# Merge commit'ini bulmak
git log --oneline --merges feature/large-feature
# m8n9o10 Merge branch 'feature/payment-gateway' into feature/large-feature
# -m 1 ile birinci parent'ı (genellikle hedef branch) ana hat kabul et
git cherry-pick -m 1 m8n9o10
-m 1 çoğu durumda doğru seçimdir çünkü merge commit’inde 1. parent genellikle merge yapılan branch (hedef), 2. parent ise merge edilen branch (kaynak) olur. Ama bunu git show m8n9o10 ile doğrulayabilirsiniz.
Şunu da belirteyim: merge commit’leri cherry-pick etmekten genellikle kaçınırım. Çoğu durumda merge commit’indeki bireysel commit’leri ayrı ayrı cherry-pick etmek daha güvenli ve öngörülebilir sonuçlar verir.
Cherry-Pick ile Birden Fazla Branch’e Aynı Anda Yayınlama
Bazı projelerde v1.x, v2.x gibi paralel sürüm branch’leriniz olabilir ve bir güvenlik yamasını her birine uygulamanız gerekir:
# Yamayı önce develop'ta hazırla ve commit et
git checkout develop
# ... değişiklikler yapıldı ...
git commit -m "Security: Fix XSS vulnerability in comment parser"
# Commit hash'i not et: p4q5r6s
# v2.x branch'ine uygula
git checkout v2.x
git cherry-pick p4q5r6s
# v1.x branch'ine uygula
git checkout v1.x
git cherry-pick p4q5r6s
# Eğer v1.x'te API farklıysa çakışma olabilir,
# yukarıdaki çakışma çözme adımlarını uygulayın
# Hepsini push et
git push origin develop v2.x v1.x
Bu pattern özellikle açık kaynak projelerde ve enterprise ortamlarda LTS (Long Term Support) sürümleri yönetirken çok sık karşılaşılan bir durum.
Cherry-Pick’i Otomatikleştirmek
Ekibinizde belirli commit’lerin bir branch’ten diğerine düzenli olarak aktarılması gerekiyorsa bu işlemi script ile otomatize edebilirsiniz:
#!/bin/bash
# cherry-pick-hotfix.sh
# Kullanım: ./cherry-pick-hotfix.sh <commit-hash> [<commit-hash2> ...]
set -e
SOURCE_BRANCH="develop"
TARGET_BRANCH="main"
if [ "$#" -eq 0 ]; then
echo "Hata: En az bir commit hash belirtmelisiniz."
echo "Kullanim: $0 <commit-hash> [<commit-hash2> ...]"
exit 1
fi
echo "==> $TARGET_BRANCH branch'ine geciliyor..."
git checkout "$TARGET_BRANCH"
git pull origin "$TARGET_BRANCH"
echo "==> Cherry-pick basliyor..."
for commit in "$@"; do
echo " -> $commit uygulanıyor..."
if git cherry-pick "$commit"; then
echo " -> $commit basariyla uygulandı."
else
echo " HATA: $commit icin cakisma var. Lutfen elle cozun."
echo " Cozumden sonra 'git cherry-pick --continue' calistirin."
exit 1
fi
done
echo "==> Degisiklikler push ediliyor..."
git push origin "$TARGET_BRANCH"
echo "==> Tamamlandi! Asagidaki commit'ler $TARGET_BRANCH branch'ine aktarildi:"
for commit in "$@"; do
git log --oneline -1 "$commit" 2>/dev/null || echo " $commit (hash artik farkli olabilir)"
done
Bu scripti şöyle kullanırsınız:
chmod +x cherry-pick-hotfix.sh
./cherry-pick-hotfix.sh 5d1c679 e8b1a2d
Ne Zaman Cherry-Pick Kullanmamalısınız
Cherry-pick güçlü bir araç ama her duruma uygun değil. Birkaç önemli uyarı:
Büyük branch merge’lerinin yerine kullanmayın. Eğer 50 commit’in tamamını bir branch’ten diğerine taşımanız gerekiyorsa bu işin doğru çözümü git merge veya git rebase. Cherry-pick ile 50 commit aktarmak hem zahmetli hem de geçmiş yönetimini zorlaştırır.
Bağımlı commit’leri dikkatli seçin. Eğer commit B, commit A‘nın oluşturduğu bir fonksiyona bağımlıysa ve siz sadece commit B‘yi cherry-pick ederseniz çakışma veya derleme hatası alırsınız. Bağımlılık zincirini anlamadan cherry-pick yapmak sorun çıkarır.
Push edilmiş commit’leri cherry-pick sonrası silmeyin. Başka geliştiricilerin pull ettiği commit’leri geri almak zorunda kalırsanız ciddi sorunlar yaşanır. Bu durumda git revert daha uygun bir yaklaşımdır.
Sık sık cherry-pick yapmak gereken bir workflow varsa branch stratejinizi gözden geçirin. Sürekli cherry-pick ihtiyacı genellikle branch yapısının yanlış kurgulandığının işareti.
Duplicate Commit Problemini Anlamak
Cherry-pick’in en sık yanlış anlaşılan yönü şu: aynı değişikliği iki farklı hash ile oluşturur. main‘de abc1234 olan commit, cherry-pick sonrası xyz7890 olarak görünür. İçerik aynı, hash farklı.
Bu ne anlama gelir? Eğer daha sonra feature branch’ini main‘e merge etmeye çalışırsanız Git bu iki commit’i ayrı değişiklikler olarak görebilir. Modern Git bunu çoğu durumda akıllıca çözse de karmaşık durumlarda çakışma çıkabilir.
Bunu önlemek için bir yaklaşım: cherry-pick ettiğiniz değişiklikler zaten main‘e girdikten sonra feature branch’ini main üzerine rebase etmek. Rebase işlemi, aynı değişiklikleri tanıyarak ilgili commit’leri atlar.
git checkout feature/payment
git rebase main
# Rebase sırasında Git, cherry-pick edilmiş commit'leri tanıyıp atlayabilir
İpuçları ve Kısayollar
Günlük kullanımda işinizi kolaylaştıracak birkaç pratik bilgi:
Commit’in hangi branch’lerde olduğunu kontrol etmek için:
git branch --contains abc1234
Cherry-pick yapmadan önce ne tür değişiklikler olduğunu görmek için:
git show abc1234 --stat
Son N commit’i cherry-pick etmek için (başka branch’teyken):
# feature branch'teki son 3 commit'i al
git cherry-pick feature~3..feature
Cherry-pick geçmişinizi görmek için (ORIG_HEAD’i kullanarak):
git log ORIG_HEAD..HEAD --oneline
Sonuç
git cherry-pick iyi kullanıldığında ekibin verimliliğini gerçekten artıran bir araç. Hotfix yayınlamak, yanlış branch’e giden commit’leri kurtarmak, paralel sürümlere yama uygulamak gibi senaryolarda vazgeçilmez oluyor.
Ama şunu unutmayın: cherry-pick bir acil çözüm aracı, birincil iş akışınız değil. Eğer kendinizi sürekli cherry-pick yaparken buluyorsanız, bu büyük ihtimalle branch stratejinizde iyileştirme yapmanız gerektiğinin sinyali. Sağlıklı bir Git workflow’u, bu tür müdahale ihtiyacını minimuma indirir.
Çakışmalardan korkmayın. İlk başta gözünüzü korkutabilir ama git cherry-pick --abort her zaman yanınızda, işlemi geri almak her zaman mümkün. Denemekten çekinmeyin, test ortamınızda pratik yapın ve bu komutla aranızı iyice geliştirin.
