git reflog: kaybolan commit'leri ve branch'leri nasıl geri getirirsiniz
git reflog, Git'in HEAD ve branch hareketlerini (commit, reset, checkout, rebase, merge) kaydettiği yerel bir günlüktür. Hatadan önceki kaydı bulun, örneğin HEAD@{2}, ve git reset --hard HEAD@{2} ile durumu geri getirin ya da git branch kurtarma HEAD@{2} ile bir branch oluşturun.
git reflog, Git'in referans günlüğüdür: HEAD'in ve branch uçlarının her hareketini kaydeder. Bir commit yaptınız, branch değiştirdiniz, reset, rebase ya da merge çalıştırdınız — her biri için reflog'a, işlemden sonra referansın gösterdiği hash yazılır. Bu yüzden artık hiçbir branch'in göstermediği bir commit bile bulunup geri getirilebilir.
Bilmeniz gereken temel nokta: reflog yereldir. Yalnızca sizin klonunuzda bulunur, sunucuya gönderilmez ve git clone ile gelmez. Ama kendi deponuzda geçmişle ilgili neredeyse her hatadan kurtarır.
git reflog çıktısını okuma
git reflog
3c4d5e6 (HEAD -> main) HEAD@{0}: reset: moving to HEAD~2
9a8b7c6 HEAD@{1}: commit: E-posta doğrulaması ekle
1f2e3d4 HEAD@{2}: commit: Yapılandırmayı ayrı dosyaya taşı
3c4d5e6 (HEAD -> main) HEAD@{3}: checkout: moving from feature to main
7b6a5c4 (feature) HEAD@{4}: commit: Giriş formu taslağı
Her satır HEAD'in bir durumudur; en üstte en yeni olan:
- hash — işlemden sonra HEAD'in gösterdiği commit;
- HEAD@{n} — «HEAD n hareket önce neredeydi»; bu tam bir referanstır, her komuta verebilirsiniz;
- açıklama — işlemin türü (commit, reset, checkout, rebase, merge, pull, cherry-pick, commit (amend)) ve ayrıntılar.
Örnekte iki commit — «Yapılandırmayı ayrı dosyaya taşı» ve «E-posta doğrulaması ekle» — yapılmış, sonra reset HEAD~2 branch'i geri almıştır. git log'da artık görünmezler, ama reflog'da yerlerinde dururlar.
Belirli bir branch'in reflog'u
Her branch'in kendi günlüğü vardır. Branch'ler arasında çok geçiş yaptıysanız bu daha kısa ve daha kullanışlıdır:
git reflog show feature
git reflog show origin/main # fetch sırasında uzak branch nasıl değişti
Zamana göre referanslar
Numara yerine bir zaman da verebilirsiniz; Git o zaman referansın nerede olduğunu bulur:
git show main@{yesterday}
git diff main@{"2 hours ago"} main
git log -g --since="1 day ago" --oneline
git log'daki -g (--walk-reflogs) bayrağı, ebeveyn bağlantıları yerine reflog üzerinden gezer ve alışık olduğunuz biçim ve filtreleri kullanmanıza izin verir. Tarihleri görmek için git reflog --date=iso yararlıdır.
git reset --hard sonrası commit'leri geri getirme
En sık karşılaşılan durum: git reset --hard'ı yanlış commit'e uyguladınız ve işiniz «kayboldu». Commit'ler aslında hiçbir yere gitmedi; sadece branch'lerden görünmez oldular.
- Günlüğü açın:
git reflog. reset: moving to ...satırından hemen önceki satırı bulun — hatadan önceki durum. Yukarıdaki örnekte buHEAD@{1}, hash'i9a8b7c6.- Doğru olduğunu kontrol edin:
git log --oneline -5 HEAD@{1}. - Branch'i geri alın:
git reset --hard HEAD@{1}.
git reset --hard HEAD@{1}
HEAD is now at 9a8b7c6 E-posta doğrulaması ekle
Dikkat: her işlemden sonra numaralar kayar. Reset'ten sonra az önceki HEAD@{1}, HEAD@{2} olur. Bu yüzden numara yerine hash'i kopyalamak daha güvenlidir.
Önemli bir sınır: reflog yalnızca commit edilmiş şeyleri tutar. reset --hard ya da git checkout -- dosya ile silinen commit edilmemiş değişiklikler reflog'la geri gelmez. Dosyalar index'e eklenmişse küçük bir umut kalır; bu aşağıda anlatılıyor.
Silinmiş branch'i geri getirme
Bir branch'i git branch -D feature ile sildiniz ve içinde gönderilmemiş commit'ler vardı. Branch'in kendi günlüğü onunla birlikte silinir, ama HEAD günlüğü kalır — HEAD, siz o branch üzerindeyken commit'lerinin üzerinden geçmiştir.
git reflog | grep feature
7b6a5c4 HEAD@{4}: commit: Giriş formu taslağı
3c4d5e6 HEAD@{3}: checkout: moving from feature to main
# Branch'in son commit'i — oradan ayrıldığınız commit
git branch feature 7b6a5c4
checkout: moving from feature to ... satırını arayın: bu satırın altındaki (daha eski) kaydın hash'i branch'in son durumudur. Ayrıca silme sırasında Git hash'i yazdırır: Deleted branch feature (was 7b6a5c4). Terminal hâlâ açıksa bu bilgi yeterlidir.
Başarısız rebase'i geri alma
Rebase commit'leri yeniden yazar; sürecte çakışmaları yanlış çözdüyseniz sonuç başlangıçtakinden kötü olabilir. Reflog, rebase başlamadan önceki durumu saklar.
git reflog
e1f2a3b HEAD@{0}: rebase (finish): returning to refs/heads/feature
e1f2a3b HEAD@{1}: rebase (pick): Giriş formu taslağı
c4d5e6f HEAD@{2}: rebase (start): checkout main
7b6a5c4 HEAD@{3}: commit: Giriş formu taslağı
git reset --hard HEAD@{3}
Hedef, rebase (start) satırından önceki kayıttır. Bir de kısa yol var: birleştirme, rebase, reset gibi tehlikeli işlemlerden önce Git eski konumu ORIG_HEAD referansına kaydeder, dolayısıyla rebase'den hemen sonra git reset --hard ORIG_HEAD işe yarar. Ama ORIG_HEAD bir sonraki benzer işlemde üzerine yazılır; reflog ise tüm geçmişi tutar.
git commit --amend öncesi commit'i geri getirme
--amend, commit'i değiştirmez; yerine yeni bir commit oluşturur. Eski commit reflog'da kalır:
git reflog
5a6b7c8 HEAD@{0}: commit (amend): E-posta doğrulaması ekle
9a8b7c6 HEAD@{1}: commit: E-posta doğrulaması ekle
git diff HEAD@{1} HEAD # amend neyi değiştirdi
git reset --soft HEAD@{1} # eski commit'e dön; amend'in değişiklikleri index'te kalır
Güvenli geri alma: reset yerine branch
Hangi commit'in gerektiğinden emin değilseniz, körlemesine reset --hard yapmayın. Bir kurtarma branch'i oluşturun: hiçbir şeyi bozmaz ve bulduğunuz commit'i silinmeye karşı korur:
git branch rescue HEAD@{5}
git log --oneline rescue
git switch rescue
Ardından rescue'deki commit'leri cherry-pick ile taşıyabilir ya da birleştirebilirsiniz; her şey yerine döndüğünde rescue branch'ini silin.
Reflog ne kadar süre saklanır
Kayıtlar sonsuza kadar tutulmaz. Git'in periyodik olarak kendi çalıştırdığı git gc onları temizler. Süreleri ayarlar belirler:
| Ayar | Varsayılan | Anlamı |
|---|---|---|
gc.reflogExpire | 90 gün | Mevcut branch'lerden erişilebilen commit'li kayıtların süresi |
gc.reflogExpireUnreachable | 30 gün | Hiçbir branch'ten erişilemeyen commit'li kayıtların süresi — tam olarak «kaybolanlar» |
gc.pruneExpire | 2 hafta | Artık hiçbir şey göstermeyen erişilemez nesnelerin ne zaman silineceği |
Pratik sonuç: kaybolan bir commit, yaklaşık bir ay boyunca reflog üzerinden kesin olarak erişilebilir. Geri getirmeyi ertelemeyin ve geri getirmeden git gc --prune=now ya da git reflog expire --expire=now --all komutlarını çalıştırmayın.
reflog işe yaramazsa: git fsck
Bazen commit reflog'da yoktur: örneğin git stash onu oluşturmuş, sonra git stash drop ile stash silinmiştir ya da değişiklikler yalnızca index'te olmuştur. O zaman «sahipsiz» nesnelere bakın:
git fsck --lost-found
dangling commit 2b3c4d5e6f...
dangling blob 8e9f0a1b2c...
git show 2b3c4d5e6f # içeriğe bak
git branch recovered 2b3c4d5e6f
Silinmiş stash'ten kalan sahipsiz commit'lerin mesajı «WIP on ...» ile başlar. Sahipsiz blob'lar, bir zamanlar git add ile eklenmiş ama commit edilmemiş dosyaların içeriğidir. Dosya adları yoktur, içeriğe bakarak tanımanız gerekir; ama bu yolla reset --hard sonrası işi kurtarmak bazen mümkün olur. --lost-found bayrağıyla Git bunları ayrıca .git/lost-found/ klasörüne de kopyalar.
reflog'un yapamadıkları
- Makineler arasında çalışmaz. Bir meslektaşınız force push yapıp sunucudaki commit'lerinizi sildiyse, onları bu commit'lere yerel olarak sahip olan birinin reflog'unda arayın; örneğin silinmeden önce fetch yapmış herhangi birinde
git reflog show origin/main. - Commit edilmemiş değişiklikleri görmez. Bu yüzden riskli bir işlemden önce en iyi sigorta commit ya da
git stash'tir. - Geçmişi sonsuza dek tutmaz. Süreler yukarıdaki gibidir; ayrıca taze bir klon boş bir günlükle başlar.
Uygulama: riskli bir işlem öncesi sigorta
Reflog kurtarır, ama bununla geri dönmek dikkat ister: onlarca benzer kayıt arasından doğrusunu bulmanız gerekir. Çok daha kolayı, önceden bir işaret koymaktır. Rebase, büyük bir reset ya da geçmişle deneme yapmadan önce geçici bir branch ya da etiket oluşturun:
git branch backup/before-rebase
git rebase -i main
# bir şeyler ters gitti
git reset --hard backup/before-rebase
# her şey yolunda
git branch -D backup/before-rebase
Bu tek komutluk bir iş ve hash aramaktan sizi kurtarır. Reflog, sigortayı hatırlamadığınız durumlar için yedek planınızdır.
Doğru kaydı hızlıca bulma
Uzun günlükte filtreleme yardımcı olur. İşlem türüne ya da commit mesajındaki metne göre arayın; şüpheli durumlarda içeriğe bakın:
git reflog | grep -E "reset|rebase \(start\)"
git log -g --grep="doğrulama" --oneline
git show --stat HEAD@{7}
git log -g --grep, reflog'daki commit mesajlarında arar; git show --stat ise hangi dosyaların etkilendiğini gösterir. Mesaj «wip» gibi bilgisiz olsa bile dosyalara bakarak doğru commit'i tanıyabilirsiniz.
Kısaca
git reflog, Git'in son haftalarda commit edilmiş her şeyi bulup geri getirebildiğiniz güvenlik ağıdır. Adımlar hep aynıdır: günlüğü açın, hatadan önceki kaydı bulun, git log ya da git show ile doğruluğunu kontrol edin, sonra bir branch oluşturun ya da reset --hard yapın. Restore, reset, revert ve hatalardan kurtarma Git kursunda uygulamalı olarak anlatılmaktadır.
Sık sorulan sorular
git reflog ile git log arasındaki fark nedir?
git log, commit'lerin ebeveyn bağlantılarını izleyerek branch'in geçmişini gösterir. git reflog ise deponuzda HEAD ve branch'lerin nereye hareket ettiğini gösterir; artık branch geçmişinde olmayan commit'leri de içerir.
reflog GitHub veya GitLab'a gider mi?
Hayır. Reflog yalnızca yerel .git/logs klasöründe tutulur ve push ile clone sırasında aktarılmaz.
Reflog'u temizleyebilir miyim?
Evet: git reflog expire --expire=now --all ve ardından git gc --prune=now. Ama bundan sonra kaybolan commit'leri geri getirmek mümkün olmaz.
reflog dosyası nerede duruyor?
Düz metin dosyaları olarak .git/logs/HEAD ve .git/logs/refs/heads/branch-adi içinde; her hareket için bir satır.
Sunucudaki bare depoda reflog çalışır mı?
Varsayılan olarak bare depolarda reflog kapalıdır (core.logAllRefUpdates), bu yüzden sunucuda buna güvenmemelisiniz.
Bu konu “Sıfırdan Güvenilir Geliştiriciye Git” kursunda uygulamalı olarak ayrıntılı işleniyor.
Kursa git