Git Stash ile Yarım Kalan Değişiklikleri Saklama
Sabah 9’da acil bir production bug’ı geliyor, üzerinde çalıştığın feature branch yarı yolda, değişiklikler commit edilmemiş. Ne yapacaksın? git stash tam bu an için var. Yıllarca bu komutu sadece “bir yere kaldır” diye kullandım, ta ki gerçekten derinlemesine incelemeye karar verene dek. Şimdi stash olmadan çalışmayı hayal bile edemiyorum.
Git Stash Nedir ve Neden Önemlidir?
git stash, commit etmeden çalışma dizinindeki ve index’teki değişiklikleri geçici olarak saklayan bir mekanizma. Teknik olarak konuşursak, stash aslında özel bir referans olan refs/stash üzerinde saklanan bir commit zinciri. Yani “sihirli bir çekmece” değil, Git’in kendi veri yapısını kullanan bir özellik.
Peki neden sadece “geçici bir branch aç, commit et” demiyoruz? Çünkü stash bundan daha hızlı, daha temiz ve iş akışını daha az kesiyor. Özellikle birden fazla şeyi stash’leyip farklı branşlar arasında geçiş yapmanız gerektiğinde farkı anlıyorsunuz.
Temel kullanımdan başlayalım ama hızlıca ileri seviyeye geçeceğiz.
Temel Kullanım: İlk Adımlar
# Tüm değişiklikleri stash'le
git stash
# Açıklama ekleyerek stash'le (bunu her zaman yapın, ciddi söylüyorum)
git stash push -m "kullanici-auth middleware yarida kaldi"
# Stash listesini gör
git stash list
# En son stash'i geri yükle
git stash pop
# Belirli bir stash'i geri yükle
git stash pop stash@{2}
git stash ile git stash push arasındaki fark: push daha yeni ve daha fazla seçenek sunuyor. Eski git stash save artık deprecated, bunu kullananlar varsa push‘a geçin.
git stash list çıktısı şöyle görünür:
stash@{0}: On feature/user-auth: kullanici-auth middleware yarida kaldi
stash@{1}: WIP on main: hızlı fix denemesi
stash@{2}: On feature/payment: payment gateway refactor
Açıklamasız stash’lerin “WIP on branch-name” şeklinde göründüğüne dikkat edin. İki gün sonra hangisinin ne olduğunu anlamak için ekstra efor harcamak zorunda kalırsınız.
Untracked ve Ignored Dosyaları Stash’lemek
Burada çoğu kişinin düştüğü bir tuzak var. Varsayılan git stash sadece takip edilen (tracked) dosyaları saklar. Yeni oluşturduğunuz ama henüz git add yapmadığınız dosyalar stash’e girmez.
# Untracked dosyaları da dahil et
git stash push -u -m "yeni servis dosyalariyla birlikte"
# Hem untracked hem de .gitignore'daki dosyaları dahil et
git stash push -a -m "her seyi sakla"
-u veya –include-untracked: Git tarafından takip edilmeyen yeni dosyaları da stash’e ekler.
-a veya –all: Hem untracked hem de ignored dosyaları dahil eder. Dikkatli kullanın, node_modules gibi şeyler de dahil olur ve stash işlemi çok yavaşlar.
Gerçek dünyada -u bayrağını neredeyse her zaman kullanıyorum. Yeni bir dosya oluşturduğunuzda ve stash yaptığınızda o dosyanın kaybolduğunu sandığınız anlar can sıkıcıdır.
Kısmi Stash: Sadece Belirli Dosyaları Saklamak
Bu özelliği az kişi biliyor ama çok işe yarıyor. Diyelim ki aynı branch’te hem bir bug fix hem de yeni bir feature üzerinde çalışıyorsunuz. Sadece feature kısmını stash’leyip bug fix’i commit etmek istiyorsunuz.
# Belirli dosyaları stash'le
git stash push -m "sadece payment modulu" src/payment/gateway.js src/payment/validator.js
# Interaktif mod: hangi chunk'ların stash'leneceğini seç
git stash push -p -m "secici stash"
-p veya --patch bayrağı interaktif bir oturum açar. Her değişiklik bloğu için y (evet), n (hayır), s (daha küçük parçalara böl) gibi seçenekler sunar. Bu özelliği öğrendikten sonra commit disiplinim ciddi şekilde arttı.
Gerçek bir senaryo: Bir mikroservis projesinde çalışırken logging altyapısını yeniden yazıyordum, aynı zamanda küçük bir timeout bug’ını da fark ettim ve düzelttim. git stash push -p ile sadece logging değişikliklerini stash’leyip timeout fix’ini ayrı bir commit olarak gönderdim. Temiz bir git history için bu yaklaşım mükemmel.
Stash Üzerine Branch Açmak
Stash’e attığınız değişiklikler büyüyorsa veya uzun süre bekleyecekse, stash’ten direkt branch açmak daha sağlıklı.
# Stash'ten yeni branch oluştur ve değişiklikleri uygula
git stash branch yeni-feature-branch stash@{0}
# En son stash'ten branch aç
git stash branch acil-duzeltme-branch
Bu komut şunları yapar: stash’in oluşturulduğu commit’e gider, yeni branch açar, stash’i uygular ve eğer başarılıysa stash’i listeden siler. Tek hamlede temiz bir başlangıç.
# Mevcut stash durumunu incele
git stash show stash@{1}
# Diff olarak göster
git stash show -p stash@{1}
# Stat formatında göster
git stash show --stat stash@{0}
git stash show olmadan stash uygulamak biraz kör uçuş yapmak gibi. Özellikle birkaç günlük stash’lerde neyin ne olduğunu hatırlamak için önce show, sonra pop alışkanlığı edinin.
Stash Uygulama Stratejileri: Pop vs Apply
# Pop: uygula ve listeden sil
git stash pop stash@{0}
# Apply: uygula ama listede bırak
git stash apply stash@{0}
# Apply sonrası manuel silme
git stash drop stash@{0}
# Tüm stash listesini temizle (geri dönüşü yok!)
git stash clear
pop ile apply arasındaki tercih önemli. Ben şahsen risk aldığım durumlarda apply kullanıyorum. Eğer stash uygulandıktan sonra conflict çıkarsa ve ben de işleri karıştırırsam, stash hala listede duruyor ve tekrar başlayabilirim. pop ise başarılı uygulamanın ardından stash’i siliyor.
Bir conflict senaryosu:
# Stash'i apply et
git stash apply stash@{0}
# Conflict varsa çöz
# vim src/api/routes.js (conflict markers'ları düzelt)
# Çözdükten sonra
git add src/api/routes.js
git stash drop stash@{0}
pop sırasında conflict çıkarsa stash otomatik olarak silinmez, endişelenmeyin. Ama bunu bilmeyenler panikleyebilir.
İleri Seviye: Stash ile Index Yönetimi
Staging area’yı (index) ve çalışma dizinini ayrı ayrı stash’lemek mümkün. Bu çok özel ama bazen hayat kurtarıcı.
# Sadece staged değişiklikleri stash'le, working directory'yi bırak
git stash push --staged -m "sadece staged kisim"
# Staging area'yı koru, sadece working directory değişikliklerini stash'le
git stash push --keep-index -m "staged degisiklikler yerinde kalsin"
–staged: Sadece index’e (staging area) eklenmiş değişiklikleri stash’ler.
–keep-index: Çalışma dizinini ve staging area’yı stash’ler ama staging area’yı working directory’de bırakır. Test senaryoları için harika.
Pratik kullanım örneği: Bir değişikliği commit etmeden önce “sadece staged kısmıyla testler geçiyor mu?” diye kontrol etmek istiyorsunuz.
# Değişikliklerin bir kısmını stage et
git add src/utils/helpers.js
# Staged olmayan kısmı geçici olarak sakla
git stash push --keep-index -m "test icin gecici"
# Testleri çalıştır
npm test
# Her şey yolundaysa stash'i geri al
git stash pop
Stash’i Başka Branch’e Taşımak
Yanlış branch’te çalıştığınızı fark ettiğinizde, en yaygın senaryolardan biri:
# main branch'tesiniz, yanlışlıkla burada değişiklik yaptınız
git stash push -m "yanlis branchte yapilan degisiklik"
# Doğru branch'e geç
git checkout feature/correct-branch
# Stash'i burada uygula
git stash pop
# Doğruladıktan sonra commit et
git add .
git commit -m "feat: dogru branchte yapilan degisiklik"
Bu senaryoyu ayda en az birkaç kez yaşıyorum. git stash olmadan ya değişiklikleri kaybedecektim ya da main’e yanlış commit atacaktım.
Stash’i Diğer Araçlarla Birleştirmek
# Stash listesini daha okunaklı göster
git stash list --format="%gd: %s (%cr)"
# Son N stash'i göster
git stash list | head -5
# Belirli bir stash'teki dosya listesi
git stash show --name-only stash@{0}
# Stash'teki belirli bir dosyayı geri yükle (diğerlerini değil)
git checkout stash@{0} -- src/config/database.js
Son komut özellikle güçlü. Bir stash’in tamamını değil, sadece içindeki belirli bir dosyayı almak istediğinizde kullanılır. Mesela stash’te 10 dosya var ama siz sadece config dosyasına ihtiyaç duyuyorsunuz.
# Stash'teki belirli dizini geri yükle
git checkout stash@{0} -- src/services/
# Stash'i silmeden önce patch dosyası olarak export et
git stash show -p stash@{0} > backup.patch
# Patch'i sonradan uygula
git apply backup.patch
Patch olarak export etmek, stash’leri takım arkadaşlarıyla paylaşmanın pratik bir yolu. “Şu değişikliklere bak” diyip patch dosyası gönderebilirsiniz.
Kayıp Stash’leri Kurtarmak
“Yanlışlıkla stash clear yaptım” veya “pop sonrası conflict çözerken silindi” durumları için:
# Erişilemeyen (dangling) commit'leri listele
git fsck --no-reflog | grep commit
# veya daha spesifik
git fsck --unreachable | grep commit
# Bulunan commit hash'lerini incele
git show <commit-hash>
# Kurtarılan stash'i branch olarak kaydet
git branch recovered-stash <commit-hash>
Bu yöntem garantili değil, özellikle git gc çalıştıysa dangling commit’ler temizlenmiş olabilir. Ama birkaç kez çalışan değişiklikleri bu şekilde kurtardım. git fsck burada gerçek bir kurtarıcı.
Daha sistematik bir arama için:
# Son 30 gün içindeki tüm stash benzeri commit'leri bul
git log --all --oneline --graph | grep -i "WIP|stash"
# Reflog ile stash geçmişine bak
git reflog stash
git reflog stash bazen standart git stash list‘te gözükmeyen eski stash’leri gösterir. Kayıp stash arıyorsanız ilk bakacağınız yer burası olmalı.
Ekip Çalışmasında Stash Kültürü
Bireysel olarak stash kullanmak kolay, ama ekip olarak bazı kurallar belirlemek iyi oluyor. Kendi ekibimde uyguladığımız birkaç pratik:
İsimlendirme standardı: Stash mesajlarında Jira ticket numarası veya kısa açıklama zorunlu tutmak. git stash push -m "PROJ-123: payment refactor WIP" gibi.
Düzenli temizlik: Haftada bir git stash list bakıp eskimiş stash’leri temizlemek. 2 haftadan eski stash genellikle ya uygulanmış ya da artık gerekli değildir.
Stash yerine WIP commit: Uzun süreli saklama için git commit -m "WIP: kaydet, push etme" ve dalı push etmek bazen stash’ten daha güvenli. Çünkü stash sadece lokal.
Bu son noktayı vurgulamak gerek: stash tamamen lokaldir. Push etmezsiniz, paylaşmazsınız. Makineniz çökerse, stash’leriniz gider. Kritik yarım kalan işler için WIP commit veya patch export daha güvenli.
# WIP commit pattern
git add -A
git commit -m "WIP: [PROJ-456] cache layer refactor - SQUASH BEFORE MERGE"
# Sonradan squash ile temizle
git rebase -i HEAD~3
Stash ve CI/CD Pipeline’larında Dikkat Edilecekler
Otomatik sistemlerde stash kullanımı bazen sorun çıkarır. CI ortamında git stash çalıştırıp sonra pop etmeyi unutursanız, pipeline’daki sonraki adımlar beklenmedik değişikliklerle çalışabilir.
# CI script'lerinde güvenli kullanım
git stash push -m "ci-pre-deploy-$(date +%Y%m%d%H%M%S)"
# ... işlemler ...
git stash pop
# veya
git stash drop stash@{0}
Pre-commit hook’larında stash sıkça kullanılır. Örneğin sadece staged değişiklikleri lint’lemek için:
#!/bin/bash
# .git/hooks/pre-commit
# Staged olmayan değişiklikleri geçici olarak sakla
git stash push --keep-index --include-untracked -m "pre-commit hook stash"
# Lint çalıştır
npm run lint
# Lint sonucunu kaydet
LINT_RESULT=$?
# Stash'i geri al
git stash pop
# Lint başarısızsa commit'i engelle
exit $LINT_RESULT
Bu hook pattern’i oldukça yaygın ve etkili. Sadece commit etmek istediğiniz kodun lint’ten geçmesini sağlar.
Sonuç
git stash ilk bakışta basit bir “bir yere koy” komutu gibi görünse de üzerine kurulu iş akışları oldukça zengin. Temel push/pop kullanımından başlayıp --patch, --staged, --keep-index gibi bayraklarla çok daha ince kontrol sağlayabilirsiniz.
Benim için en değerli stash özellikleri şunlar oldu: -p ile interaktif kısmi stash, git checkout stash@{0} -- ile tek dosya kurtarma ve kayıp stash’ler için git fsck kombinasyonu. Bunları gerçek projede kullanmadan önce test ortamında denemenizi öneririm.
Son olarak şunu söyleyeyim: stash bir kurtarıcı ama aynı zamanda bir erteleme mekanizması. Stash listeniz 10’u geçmeye başladıysa, çalışma disiplininizi gözden geçirme vakti gelmiş demektir. Her stash, er ya da geç deal edilmesi gereken bir borçtur. Ama o borcun varlığını bilmek, borcu yönetmenin ilk adımı.
