Tüm yazılar

#git#yazılım geliştirme#versiyon kontrol

Git'te Sildiğiniz Her Şeyi Geri Getirin: git reflog ile Kurtarma Rehberi

Yanlışlıkla git reset --hard yaptınız, bir dalı sildiniz ya da rebase her şeyi karıştırdı mı? Panik yapmayın: Git neredeyse hiçbir şeyi hemen silmez. git reflog ile kaybolan commit'leri, dalları ve stash'leri nasıl kurtaracağınızı anlatıyoruz.

Git'te Sildiğiniz Her Şeyi Geri Getirin: git reflog ile Kurtarma Rehberi
İçindekiler 11

Saat gece yarısı. Üç günlük çalışmanızı bitirdiniz, "biraz temizlik yapayım" diyerek terminale bir komut yazdınız ve Enter'a bastınız:

git reset --hard HEAD~3

Bir saniye sonra fark ediyorsunuz: o üç commit, üç günlük işti. git log artık onları göstermiyor. Mide kasılması başlıyor.

İyi haber: işiniz büyük ihtimalle hâlâ orada. Git, commit'lenmiş hiçbir şeyi hemen silmez. Sadece ona giden yolu kaybettiniz. Bu yolu bulmanın adı git reflog.

Kısaca:

  • Git, HEAD'in ve dalların geçtiği her noktayı yerel bir günlükte (reflog) tutar. Varsayılan olarak bu kayıtlar 90 gün saklanır.
  • git reflog ile kaybolan commit'in kimliğini bulur, git reset ya da git branch ile geri getirirsiniz.
  • Silinen dallar, bozulan rebase'ler ve hatalı merge'ler aynı yöntemle kurtarılır.
  • Kurtarılamayan tek şey: hiç commit'lenmemiş değişiklikler. Sık commit atın.

Git neden hiçbir şeyi hemen silmez?

Git'te her commit, içeriğinden üretilen benzersiz bir kimlikle (hash) veritabanında saklanır. Dallar ve HEAD ise sadece bu commit'leri gösteren etiketlerdir. git reset --hard HEAD~3 yaptığınızda Git o üç commit'i silmez; dal etiketini üç adım geri taşır. Commit'ler yerinde durur, sadece hiçbir dal onları göstermediği için git log'da görünmezler.

Bu "sahipsiz" commit'ler, Git'in çöp toplayıcısı (git gc) onları temizleyene kadar yaşamaya devam eder. Varsayılan ayarlarda bu süre, reflog'da hâlâ bir kaydı olan commit'ler için 90 gün, olmayanlar için 30 gündür.

git reflog: Git'in kara kutusu

Uçakların kara kutusu gibi, reflog da deponuzda HEAD'in gittiği her yeri kaydeder: her commit, checkout, reset, rebase, merge ve pull.

git reflog

Çıktı şuna benzer:

a1b2c3d HEAD@{0}: reset: moving to HEAD~3
f9e8d7c HEAD@{1}: commit: Ödeme ekranı testleri eklendi
b4c5d6e HEAD@{2}: commit: Ödeme API entegrasyonu
e7f8a9b HEAD@{3}: commit: Sepet hesaplama düzeltmesi
a1b2c3d HEAD@{4}: checkout: moving from main to odeme-ozelligi

En üstteki satır, az önce yaptığınız felaket reset. Hemen altındaki HEAD@{1} ise reset'ten hemen önceki durum: üç günlük işinizin son commit'i.

Önemli bir not: reflog yereldir. Sadece sizin bilgisayarınızdaki depoda bulunur, uzak sunucuya gönderilmez. Başka birinin bilgisayarındaki ya da GitHub'daki bir durumu kendi reflog'unuzda göremezsiniz.

Senaryo 1: git reset --hard sonrası kaybolan commit'ler

Reset'ten önceki duruma dönmek için:

git reset --hard HEAD@{1}

Ya da doğrudan commit kimliğiyle:

git reset --hard f9e8d7c

Hepsi bu. git log üç commit'inizi tekrar gösterecek.

Daha temkinli olmak isterseniz, mevcut dalınıza dokunmadan önce kurtarılan noktadan yeni bir dal oluşturun ve orada inceleyin:

git branch kurtarma f9e8d7c

Pratik bir kısayol da var: reset, merge ve rebase gibi HEAD'i büyük ölçüde taşıyan komutlar, önceki konumu ORIG_HEAD adında özel bir referansa kaydeder. Hemen fark ettiyseniz git reset --hard ORIG_HEAD da aynı işi görür.

Senaryo 2: Yanlışlıkla silinen dal

git branch -D odeme-ozelligi
# Deleted branch odeme-ozelligi (was f9e8d7c).

Git, silerken dalın son commit'inin kimliğini zaten söylüyor. Terminal geçmişinde bu satırı görüyorsanız:

git branch odeme-ozelligi f9e8d7c

Terminal kapandıysa reflog'a dönün. O dala en son geçtiğiniz ya da orada commit attığınız satırları bulun:

git reflog | grep odeme-ozelligi

Çıkan commit kimliğiyle dalı yeniden oluşturun.

Senaryo 3: Karışan rebase

Bir rebase sırasında çakışmaları yanlış çözdünüz ve sonuç berbat oldu. Rebase henüz bitmediyse en kolay yol:

git rebase --abort

Rebase bittiyse reflog'da rebase başlamadan önceki satırı arayın. Genellikle "rebase (start)" satırının hemen altındaki kayıttır:

c3d4e5f HEAD@{5}: rebase (finish): returning to refs/heads/odeme-ozelligi
...
9a8b7c6 HEAD@{9}: rebase (start): checkout main
f9e8d7c HEAD@{10}: commit: Ödeme ekranı testleri eklendi
git reset --hard HEAD@{10}

Dal, rebase öncesindeki hâline döner.

Senaryo 4: Düşürülen stash

git stash drop ya da yanlışlıkla git stash clear yaptınız. Stash'ler de aslında commit'tir, ama normal reflog'da görünmezler. Onları bulmak için Git'in sahipsiz nesneleri tarayan aracını kullanın:

git fsck --unreachable | grep commit

Çıkan her commit kimliğinin içeriğine git show <kimlik> ile bakıp doğru stash'i bulduğunuzda:

git stash apply <kimlik>

Kurtarılamayanlar

Reflog güçlü ama sihirli değil. Şu durumlarda kurtarma zordur ya da imkânsızdır:

  • Hiç commit'lenmemiş değişiklikler. Çalışma dizinindeki kaydedilmemiş değişiklikleri git reset --hard ya da git checkout -- . ile silerseniz Git onları hiç kaydetmediği için geri getiremez. Tek istisna: git add ile hazırlama alanına eklediğiniz dosyalar, Git veritabanına yazılmıştır ve git fsck --lost-found ile bulunabilir. Ama dosya adları kaybolur, içerikleri tek tek incelemeniz gerekir.
  • Çöp toplayıcının temizlediği commit'ler. Süre dolduktan ve git gc çalıştıktan sonra geri dönüş yoktur.
  • Başka bir bilgisayardaki durum. Deponun yeni bir kopyasında (git clone) reflog boştur.

Zorla push'un ezdiği uzak dal

Biri git push --force ile uzak daldaki commit'leri ezdiyse, çözüm genellikle o commit'lerin hâlâ bulunduğu bir yerel kopyadır: ezilen commit'leri daha önce çekmiş herhangi bir ekip arkadaşının deposu. O kişi kendi reflog'undan ya da dalından eski durumu bulup yeniden gönderebilir.

Bu kazayı önlemek için --force yerine şunu kullanın:

git push --force-with-lease

Bu seçenek, uzak dal sizin son bildiğiniz durumdan farklıysa, yani biri siz görmeden yeni bir commit gönderdiyse, push'u reddeder. Başkasının işini ezmenizi engeller.

İyi alışkanlıklar

  • Sık ve küçük commit atın. Commit'lenmiş her şey kurtarılabilir. Bir işi bitirmeden de "WIP" commit'i atıp sonra düzenleyebilirsiniz.
  • Tehlikeli işlemden önce yedek dal açın. Büyük bir rebase'den önce git branch yedek demek bir saniye sürer.
  • --force-with-lease kullanın, çıplak --force değil.
  • Paniklemeden önce git reflog yazın. Kaybettiğinizi sandığınız şey büyük ihtimalle oradadır.

Sıkça sorulan sorular

git reflog ile git log arasındaki fark ne?

git log, bir daldan geriye doğru commit geçmişini gösterir; yalnızca o daldan ulaşılabilen commit'leri görür. git reflog ise HEAD'in yerel olarak geçtiği tüm noktaları, zaman sırasıyla gösterir; hiçbir dalın artık göstermediği commit'ler de dahil.

Reflog kayıtları ne kadar süre saklanır?

Varsayılan olarak, reflog kayıtları 90 gün, artık hiçbir daldan ulaşılamayan commit'lere ait kayıtlar ise 30 gün saklanır. Bu süreler gc.reflogExpire ve gc.reflogExpireUnreachable ayarlarıyla değiştirilebilir.

GitHub'da yaptığım bir hatayı reflog ile düzeltebilir miyim?

Kendi yerel deponuzda o commit'ler bir zamanlar varsa, evet: yerel reflog'dan bulup yeniden gönderebilirsiniz. Ama reflog uzak sunucudaki geçmişi tutmaz.

IDE'mdeki Git araçları reflog'u gösterir mi?

Birçok IDE ve Git arayüzü (ör. JetBrains IDE'lerindeki Git penceresi) reflog benzeri bir geçmiş ya da "yerel geçmiş" özelliği sunar. Ama en güvenilir ve evrensel yol, terminalde git reflog'dur.

Versiyon kontrolünü doğru kullanmak, bir yazılım ekibinin hem hızını hem de güvenliğini belirler. Git iş akışları, CI/CD süreçleri ve sağlam bir geliştirme altyapısıyla projelerinizi büyütmek isterseniz web uygulama geliştirme sayfamızdan bize ulaşabilirsiniz.

Kaynaklar

PaylaşLinkedInXWhatsApp
Bu konuda yardıma mı ihtiyacınız var?

Yazıda anlattıklarımızı kendi projenize uygulamak isterseniz birlikte bakalım.

Bize yazın
YE

EngerekTech’in kurucusu. İşletmeler için web, mobil ve kurumsal yazılım geliştiriyor; Angular, Spring Boot ve Flutter ile çalışıyor, KPSS Düello ve Kelime Kavanozu uygulamalarını geliştirdi. Blogda yapay zekâ araçlarını ve yazılım geliştirmeyi kendi projelerinde kullandığı haliyle anlatıyor.