Git Hata Durumlarında Kurtarma: reset, revert ve restore

Bir şeyleri mahvetmişsinizdir. Yanlış branch’e commit atmışsınızdır, staging area’yı berbat etmişsinizdir ya da “bir şey olur mu” diye düşünürken üç saatlik çalışmanızı uçurmuşsunuzdur. Git kullanıyorsanız bu anlar kaçınılmaz. İyi haber şu: Git, neredeyse her hatadan kurtulmanızı sağlayacak araçlarla dolu. Kötü haber ise bu araçları karıştırırsanız kötüyü daha da kötü yapabilirsiniz.

reset, revert ve restore — bu üç komut yüzeysel bakışta benzer görünüyor ama birbirinden çok farklı senaryolar için tasarlanmış. Hangisini ne zaman kullanacağınızı bilmek, bir sysadmin veya DevOps mühendisi olarak Git konusundaki olgunluk seviyenizi doğrudan ele veriyor.

Önce Zihinsel Model: Git’in Üç Katmanı

Bu komutları anlamak için Git’in üç katmanını net görmek gerekiyor:

  • Working directory: Disk üzerindeki gerçek dosyalarınız
  • Staging area (index): git add ile eklediğiniz, commit’e hazır değişiklikler
  • Commit history: .git dizininde saklanan, kalıcı kayıt

reset, revert ve restore arasındaki temel fark, bu katmanlardan hangisini, nasıl etkiledikleriyle ilgili. Bunu kafanıza oturtursanız gerisi mantıkla geliyor.

git restore: En Güvenli Başlangıç Noktası

2019’da Git 2.23 ile gelen restore, aslında checkout‘un işini daha net bir semantikle yapıyor. Commit history’e dokunmuyor. Sadece working directory ve staging area ile ilgileniyor.

Staging Area’dan Değişiklik Geri Alma

Diyelim ki yanlış dosyayı git add ile eklediniz. Panik yok:

# Staging area'ya eklediğiniz dosyayı unstage et
git restore --staged config/database.yml

# Ya da tüm staged dosyaları geri al
git restore --staged .

Bu komut dosyayı silmiyor, sadece staging area’dan çıkarıyor. Değişiklikleriniz working directory’de duruyor.

Working Directory’deki Değişiklikleri Atmak

Bir dosyada denemeler yaptınız, berbat ettiniz, son commit’e dönmek istiyorsunuz:

# Tek dosyayı son commit'e döndür
git restore src/main.py

# Tüm working directory'yi sıfırla
git restore .

Dikkat: Bu komut geri alınamaz. Commit edilmemiş değişiklikleriniz gidiyor, reflog’da da yok. Çalıştırmadan önce bir kez daha düşünün.

Belirli Bir Commit’ten Dosya Getirme

Gerçek dünyada sık karşılaşılan senaryo: Bir dosyayı iki hafta önceki haline döndürmek istiyorsunuz ama diğer dosyalara dokunmak istemiyorsunuz.

# Belirli bir commit'ten dosya getir
git restore --source=abc1234 src/api/auth.py

# Ya da branch ismini kullanabilirsiniz
git restore --source=main src/api/auth.py

Bu dosyayı working directory’e getirir ama commit etmez. İstediğiniz buysa commit’e hazırsınız, değilse atmak da kolay.

git reset: Güçlü Ama Riskli

reset commit history’i değiştiriyor. Bu, onu hem çok güçlü hem de dikkat gerektiren bir araç yapıyor. Üç modu var ve aralarındaki farkı bilmemek ciddi sorunlara yol açabiliyor.

–soft: Commit’i Geri Al, Değişiklikleri Koru

# Son commit'i geri al, değişiklikler staged olarak kalsın
git reset --soft HEAD~1

Ne oldu? Commit history’den son commit silindi, ama o commit’teki değişiklikler staging area’da duruyor. Sanki hiç commit etmemişsiniz gibi, ama git add yapmışsınız gibi.

Ne zaman kullanırsınız? Commit mesajını berbat ettiğinizde, ya da birkaç küçük commit’i tek bir güzel commit’e toplamak istediğinizde.

# Son 3 commit'i birleştir
git reset --soft HEAD~3
git commit -m "feat: kullanıcı kimlik doğrulama modülü tamamlandı"

–mixed: Varsayılan Davranış

# --mixed varsayılan, açıkça yazmak zorunda değilsiniz
git reset HEAD~1
git reset --mixed HEAD~1

Commit geri alındı, staging area temizlendi, ama değişiklikler working directory’de duruyor. git add yapmamış halindesiniz.

Bunu genellikle “dur bir dakika, bu commit’i farklı parçalara bölsem daha mantıklı olur” dediğinizde kullanırsınız.

–hard: Nükleer Seçenek

# Son commit'i tamamen sil, working directory'i de temizle
git reset --hard HEAD~1

# Belirli bir commit'e git
git reset --hard abc1234

Bu komut gerçekten her şeyi siliyor. Commit history, staging area ve working directory hepsini o commit’in haline getiriyor. Commit edilmemiş değişiklikler tamamen gidiyor.

Gerçek senaryo: Production branch’e yanlışlıkla push etmeden önce bunu çalıştırırsanız kurtulursunuz. Ama push ettikten sonra --hard reset yapıp tekrar push etmeye çalışırsanız ekip arkadaşlarınızla ciddi sorununuz olur.

# BU YAPMAYIN - paylaşılan branch'lerde
git reset --hard HEAD~2
git push --force  # Diğer herkesin çalışmasını mahveder

Reflog ile Kurtarma

--hard kullandınız ve bir şeylerin yanlış gittiğini fark ettiniz. Pes etmeyin:

# Son yapılan işlemleri gör
git reflog

# Çıktı böyle görünür:
# abc1234 HEAD@{0}: reset: moving to HEAD~2
# def5678 HEAD@{1}: commit: önemli özellik eklendi
# ghi9012 HEAD@{2}: commit: bug fix

# Silinen commit'e dön
git reset --hard def5678

Reflog Git’in kara kutusu. Neredeyse her şeyi oradan kurtarabilirsiniz. Ama bu da sonsuz değil, varsayılan olarak 90 gün tutuluyor.

git revert: Takım Oyuncusu

revert temelden farklı düşünülmüş. History’i değiştirmiyor, aksine “bu commit’in yaptığı değişiklikleri geri alan yeni bir commit ekle” diyor. Bu, shared branch’lerde çalışırken altın kural haline geliyor.

# Belirli bir commit'i geri al
git revert abc1234

# Commit mesajı editörünü açmadan direkt commit et
git revert abc1234 --no-edit

Neden revert Tercih Etmeli?

Şöyle düşünün: main veya develop branch’inde beş kişi çalışıyor. Siz iki gün önce push ettiğiniz bir commit’in bug yarattığını fark ettiniz. reset --hard yapıp force push ederseniz diğer dört kişinin yerel repolarını mahvetmiş olursunuz. revert ise sadece yeni bir commit ekler, kimsenin history’si bozulmaz.

# Problematik commit'i bul
git log --oneline

# abc1234  feat: ödeme sistemi entegrasyonu
# def5678  refactor: veritabanı bağlantı havuzu
# ghi9012  fix: login sayfası yönlendirme hatası

# Ödeme sistemi commit'ini geri al
git revert abc1234
git push origin main

Herkes git pull yaptığında revert commit’ini görür, history net kalır ve kimsenin çalışması bozulmaz.

Birden Fazla Commit’i Revert Etmek

# Son 3 commit'i geri al (her biri için ayrı commit)
git revert HEAD~3..HEAD

# Tek bir revert commit'i olarak yap
git revert --no-commit HEAD~3..HEAD
git commit -m "revert: son 3 commit geri alındı, kritik bug"

Merge Commit’leri Revert Etmek

Bu biraz daha karmaşık. Merge commit’inin iki parent’ı var ve hangisine döneceğinizi belirtmeniz gerekiyor:

# Merge commit'i bul
git log --merges --oneline

# abc1234  Merge branch 'feature/payment' into main

# -m 1 main branch'i (ilk parent) baz alır
git revert -m 1 abc1234

-m 1 genellikle doğru seçim, ama branch yapınıza göre değişebilir. git show abc1234 ile hangi parent’ın ne olduğunu görebilirsiniz.

Karmaşık Senaryolar ve Pratik Çözümler

Senaryo 1: Yanlış Branch’e Commit

Klasik. main üzerinde çalışıyorsunuz ama feature branch’inde olmanız gerekiyordu:

# Hangi branch'te olduğunuzu anlayın
git log --oneline -5

# Commit hash'ini not edin (örn: abc1234)
# Yeni branch oluşturun ve o commit'e taşıyın
git branch feature/yeni-ozellik

# main'i geri alın
git reset --hard HEAD~1

# Feature branch'e geçin
git checkout feature/yeni-ozellik

# Commit doğru branch'te
git log --oneline -3

Senaryo 2: Hassas Bilgi Commit Edildi

.env dosyası, API key, şifre… Commit ettiniz ve fark ettiniz. Push etmediyseniz:

# Henüz push etmediyseniz
git reset --soft HEAD~1
# .env'yi .gitignore'a ekle
echo ".env" >> .gitignore
git rm --cached .env
git add .gitignore
git commit -m "chore: .env dosyası git takibinden çıkarıldı"

Push ettiyseniz iş daha ciddi. Komut satırı araçları (git-filter-repo gibi) ile history’i temizlemeniz gerekir ama en güvenli yol API key’i hemen geçersiz kılmak ve yeni key oluşturmaktır. History temizliği önemli ama önce güvenlik açığını kapatın.

Senaryo 3: Staging Area Karmaşası

Birden fazla dosyada değişiklik yaptınız, hepsini git add . ile eklediniz ama aslında ayrı commit’ler yapmanız gerekiyordu:

# Tümünü unstage et
git restore --staged .

# Şimdi seçerek ekle
git add src/auth/login.py
git commit -m "fix: login sayfası null pointer hatası"

git add src/payment/checkout.py
git commit -m "feat: ödeme sayfasına loading state eklendi"

Ya da interaktif staging ile daha granüler kontrol:

# Aynı dosyanın farklı parçalarını farklı commit'lere ayır
git add -p src/utils.py

Bu komut dosyadaki her değişikliği ayrı ayrı gösterir ve hangisini ekleyip hangisini eklemeyeceğinizi sorular. Gerçekten çok kullanışlı.

Senaryo 4: Detached HEAD Durumu

git checkout abc1234 yaptınız ve şimdi “HEAD detached at abc1234” görüyorsunuz. Panik değil:

# Nerede olduğunuzu anlayın
git status
git log --oneline -3

# Değişiklik yaptıysanız ve saklamak istiyorsanız
git branch geçici-dal
git checkout geçici-dal

# Ya da hiçbir şey yapmadıysanız normal branch'e dönün
git checkout main

Detached HEAD durumunda yaptığınız commit’ler bir branch’e bağlı olmadığı için garbage collection ile silinebilir. Ama reflog’da görünürler, panik yapmaya gerek yok.

Komutları Karşılaştırmak

Hangi durumda ne kullanacağınıza dair pratik rehber:

  • Staged dosyayı geri almak: git restore --staged
  • Working directory değişikliğini atmak: git restore
  • Commit mesajını düzeltmek (push edilmemiş): git reset --soft HEAD~1 ile geri al, tekrar commit et
  • Son commit’i tamamen silmek (push edilmemiş): git reset --hard HEAD~1
  • Paylaşılan branch’te commit’i geri almak: git revert
  • Eski bir dosyayı getirmek: git restore --source=
  • Yanlışlıkla silinen branch’i kurtarmak: git reflog ile hash bul, git branch

Bir Kaç Güvenlik Önlemi

Yıllar içinde öğrendiğim bazı alışkanlıklar:

Reset yapmadan önce bir branch oluşturun. Bedeli sıfır, kurtardığı anlar paha biçilemez:

git branch backup/before-reset-$(date +%Y%m%d)
git reset --hard HEAD~3

Stash’i küçümsemeyin. restore veya reset öncesi:

git stash push -m "reset öncesi yedek - $(date)"
git stash list  # Listede görünüyor mu kontrol et

Force push için –force-with-lease kullanın. Eğer gerçekten force push yapmanız gerekiyorsa:

# Bunu kullanın
git push --force-with-lease origin feature/branch

# Bunu değil
git push --force origin feature/branch

--force-with-lease sizden sonra kimse push etmediyse işlemi gerçekleştirir. Birinin çalışmasını ezmekten korur.

Sonuç

restore, revert ve reset arasındaki fark özünde şu: Ne kadar geri gitmek istiyorsunuz ve bu geri gidiş sadece sizi mi yoksa başkalarını da mı etkiliyor?

Sadece working directory veya staging area ile ilgileniyorsanız restore yeterli ve güvenli. Commit history’i düzenlemek istiyorsunuz ama değişikliklerinizi korumak istiyorsanız reset --soft veya --mixed işinizi görür. Her şeyi silip sıfırlamak istiyorsanız reset --hard var ama geri dönüşü zor. Ekibinizle paylaştığınız branch’lerde bir şeyleri geri almak gerekiyorsa revert tek güvenli yol.

Bu komutları ezberlemek değil, mantığını anlamak önemli. Git’in üç katmanını (working directory, staging area, commit history) kafanızda netleştirdiğinizde, hangi komutun hangi katmana dokunduğu mantıksal olarak ortaya çıkıyor. Gerisi dokümantasyon meselesi.

Son bir not: Reflog’u tanıyın. Kaç kez “her şeyi mahvettim” dediğinizde git reflog sizi kurtaracak. Git neredeyse hiçbir şeyi gerçekten silmiyor, sadece nerede olduğunu bilmeniz gerekiyor.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir