4 puan yazan GN⁺ 2025-01-18 | 1 yorum | WhatsApp'ta paylaş
  • Git’te bir hatanın adını bilmiyorsanız çözümü aramak zor olduğundan, yaygın kurtarma senaryolarına göre komutları hızlıca bulup kullanmaya yarayan bir kaynak
  • Depoyu ciddi biçimde bozduğunuzda ya da silme/birleştirme hatalarını geri almak istediğinizde git reflog ile işlem geçmişini bulup git reset HEAD@{index} ile önceki duruma dönebilirsiniz
  • Son commit’teki küçük düzeltmeleri veya mesaj değişikliğini git commit --amend ile hızlıca yapın; ancak bunu zaten açık/paylaşılan bir branch’e push edilmiş commit’lerde kullanmayın
  • Yanlış branch’e commit yaptıysanız değişikliği reset, stash, checkout, stash pop akışıyla veya cherry-pick ile taşıyabilirsiniz; ancak güvenli yöntem push edilip edilmediğine göre değişir
  • Uzak depo durumuna zorla dönmek için kullanılan reset --hard ve git clean -d --force, izlenmeyen dosyaları da silebileceğinden çalıştırmadan önce kontrol etmek gerekir

Git hatalarını kurtarmanın zor olmasının nedeni

  • Git’te hata yapmak kolaydır ve sorunu düzeltmek için çoğu zaman arama yapabilmek adına gerekli kavramın adını zaten bilmeniz gerekir
  • Bu kaynak, Git’te sık yaşanan kötü durumları sade İngilizce ve komut örnekleriyle derler
  • Eksiksiz bir referans değildir; aynı işi yapmanın başka yolları da olabilir

Depoyu geçmiş bir duruma döndürmek

  • Depoyu ciddi biçimde bozduysanız veya sildiğiniz içeriği geri getirmek istiyorsanız işlem geçmişini görmek için git reflog kullanın
git reflog


# Tüm branch’lerdeki Git işlemlerinin listesini görebilirsiniz


# Her öğe HEAD@{index} biçiminde bir indekse sahiptir
git reset HEAD@{index}
  • reflog şu durumlarda kullanılabilir
    • Yanlışlıkla silinen içeriği kurtarma
    • Depoyu bozan denemeleri kaldırma
    • Kötü bir merge sonrası kurtarma
    • Gerçekten çalıştığı ana geri dönme

Son commit’i düzeltme yolu

  • Son commit’in hemen ardından küçük bir düzeltme eklemeniz gerekiyorsa git commit --amend --no-edit kullanın
# Değişiklikten sonra
git add . # veya tek tek dosya ekleyin
git commit --amend --no-edit
  • Bu komut yeni değişikliği son commit’e dahil eder
  • Aynı işi yeni bir commit oluşturup rebase -i ile birleştirerek de yapabilirsiniz, ancak amend çok daha hızlıdır
  • Zaten açık/paylaşılan bir branch’e push edilmiş commit’lerde amend kullanmamalısınız
  • Yalnızca son commit mesajını değiştirmek için şu komutu kullanın
git commit --amend


# Yönergeleri izleyerek commit mesajını değiştirin

Yanlış branch’e commit yaptığınızda

  • Yanlışlıkla master’a commit yaptıysanız ama bunun yeni bir branch’te olması gerekiyorsa, mevcut durumdan yeni bir branch oluşturup master’ın son commit’ini kaldırın
git branch some-new-branch-name
git reset HEAD~ --hard
git checkout some-new-branch-name
  • Bu yöntem, zaten açık/paylaşılan bir branch’e push edilmiş commit’ler için uygun değildir
  • Önce başka denemeler yaptıysanız HEAD~ yerine HEAD@{number-of-commits-back} biçimini kullanmanız gerekebilir
  • Tamamen yanlış branch’e commit yaptıysanız reset --soft, stash, checkout, stash pop akışıyla taşıyabilirsiniz
git reset HEAD~ --soft
git stash
git checkout name-of-the-correct-branch
git stash pop
git add . # veya tek tek dosya ekleyin
git commit -m "your message here"
  • Aynı durumda cherry-pick kullanmanın bir yolu da vardır
git checkout name-of-the-correct-branch
git cherry-pick master
git checkout master
git reset HEAD~ --hard

Değişiklikleri ve eski commit’leri geri almak

  • Bir dosyayı değiştirdiyseniz ama git diff boş görünüyorsa değişikliği zaten staging’e almış olabilirsiniz; bu durumda --staged bayrağı gerekir
git diff --staged
  • Birkaç commit önceki değişikliği geri almak için git log ile hash’i bulun ve git revert çalıştırın
git log
git revert [saved hash]
  • git revert, ilgili commit’i geri alan yeni bir commit oluşturur
  • Tüm commit’i değil yalnızca belirli bir dosyayı geri almak için dosyanın eski sürümünü indekse alıp commit etme akışını kullanın
git log
git checkout [saved hash] -- path/to/file
git commit -m "Wow, you don't have to copy-paste to undo"

Uzak depo durumuna zorla dönmek

  • Depoyu silip yeniden clone etmek yerine, uzak depo durumuyla eşitleyen Git komutlarını kullanabilirsiniz
git fetch origin
git checkout master
git reset --hard origin/master
git clean -d --force
  • git reset --hard origin/master, branch’i uzak master durumuna getirir
  • git clean -d --force, izlenmeyen dosya ve dizinleri siler
  • Bozulmuş her branch için checkout, reset, clean işlemlerini tekrarlamanız gerekir
  • Bu işlemler yıkıcıdır ve geri alınamaz; bu yüzden çalıştırmadan önce dikkatli olun

Kapsam ve katkı

  • Bu kaynak eksiksiz bir Git referansı değil, deneme yanılmayla bulunan pratik kurtarma prosedürleri derlemesidir
  • Çeviri eklemeye yardımcı olmak istiyorsanız GitHub’a PR gönderebilirsiniz

1 yorum

 
GN⁺ 2025-01-18
Hacker News görüşleri
  • Değiştirmek istediğim birkaç nokta var: her zaman git checkout yerine git switch kullanır ve reset --hard kullanımından mümkün olduğunca kaçınırdım
    Örneğin “yeni branch'te olması gereken commit'i yanlışlıkla master'a attıysanız”, bunu git branch some-new-branch-name, git switch -d HEAD~, git switch -C master, git switch some-new-branch-name sırasıyla halledebilirsiniz
    “Yanlış branch'e commit attıysanız”, önce doğru branch'e git switch yapıp ardından git cherry-pick master, git switch -d master~, git switch -C master ile çözerdim
    “Her şeyi bırakıp uzak depo durumuna geri dönmek” için de git fetch origin, git restore -WS ., git clean -d --force, git switch -d origin/master, git switch -C master gibi bir akış kurardım

    • Git'in iç modeli blob'lar, commit ağacı ve commit işaretçileri açısından çok güzel, ama komut satırı arayüzüyle arasında fazla büyük bir kopukluk var
      Git modelini iyi anlasanız bile bu tarifler sezgisel değil ve her komutun kendine özgü tuhaf davranışlarını da bilmeniz gerekiyor
      Örneğin git switch -c some-new-branch-name zaten var, ama sadece branch'i başka bir commit'e taşıyan git move-branch master HEAD~ gibi bir komut daha sezgisel olurdu
    • git checkout yerine git switch ve git restore kullanılmasına karşı çıkmaya çalışmıyorum, ancak bu komutlar Git v2.23 ile geldi ve o sürüm yaklaşık 5 yıl önce çıktı
      Yardım sayfasında hâlâ THIS COMMAND IS EXPERIMENTAL. THE BEHAVIOR MAY CHANGE. uyarısı yer alıyor
      Elbette bu uyarı komutların çıktığı neredeyse ilk günden beri vardı ve 5 yıl sonra artık buna deneysel dememek gerekir gibi görünüyor
      Yine de git checkout geriye dönük uyumluluk açısından daha iyi ve bu web sitesinin ilk yapıldığı dönemin Git kullanımını yansıttığı da söylenebilir
      https://github.com/git/git/blob/757161efcca150a9a96b312d9e78...
      https://github.com/git/git/releases/tag/v2.23.0
      https://github.com/git/git/commit/4e43b7ff1ea4b6f16b93a432b6...
    • Değişiklikleri staging alanına alırken varsayılan olarak git add . öğretmek pek iyi değil
      Belirli dosyaları açıkça eklemeyi öğretmek, sonradan “eyvah” deme ihtimalini azaltır
    • Bunun neden önerildiğinin gerekçesini merak ediyorum
      switch'in checkout'tan neden daha iyi olduğunu ve neden reset --hard kullanmamak gerektiğini bilmek isterim
    • reset --hardtan kaçının” ifadesinin ne anlama geldiğini anlayamıyorum
      Pratikte neden yeterli olmadığını gerçekten merak ediyorum; ben bunu alias git-restore-file='git restore --source=HEAD --' ile birlikte epey sık kullanıyorum ve iyi çalışıyor gibi görünüyor
  • Git öğrenirken varsayılan yol olarak GUI istemcileri önermeye başlamalıyız
    Böylece bu sorunların üçte biri çözülür, bir başka üçte biri de en baştan hiç ortaya çıkmaz
    Daha sonra komut satırının daha hızlı olduğuna karar verirlerse o zaman kullanırlar; ama önce insanların ağaçla nasıl etkileşime girdiğini görsel olarak görmesi gerekiyor
    Ben şahsen fork.dev'i seviyorum, ama bugünlerde istemcilerin çoğu aşağı yukarı benzer

    • GUI'nin Git için çoğu zaman daha iyi bir kullanıcı deneyimi sunduğuna katılıyorum
      Magit kullanınca “cherry-pick'i iptal et” gibi işlemler arayüz içinde keşfedilebilir oluyor ve diğer “X'i iptal et” işlemleriyle aynı kısayolu kullanıyor
      Yalnızca Git komut satırını kullanmak zorunda olsaydım nereden başlayacağım hakkında hiçbir fikrim olmazdı
      Etkileşimli rebase sırasında silmemem gereken commit'leri sildiğim de oldu, ama her rebase başlangıç noktasında reflog üzerinden erişilebilen bir anlık görüntü oluştuğu için hatalı bir rebase'i geri almak güvenli oluyor
      Magit'in reflog arayüzü log arayüzüyle aynı olduğu için ilk kez gördüğümde bile kaybolmadım; ama Git CLI olsaydı ne olduğunu anlamamam çok olasıydı
    • Git uyumlu bir alternatif olarak jj önermeye başladım
      jj komutunu parametresiz çalıştırınca commit ağacının ilgili bölümünü ve şu an nerede olduğunuzu göstermesi hoşuma gidiyor
      O an üzerinde çalıştığınız commit'i, bağlı branch'leri ve geçmişi, depodaki diğer aktif branch'leri, her commit'in mesajını ve değişiklik durumunu tek bakışta görebiliyorsunuz; bu da yönünüzü yeniden bulmak için harika
    • Git acemilerine her zaman kendilerine uygun bir UI bulup komut satırı yerine onu kullanmalarını öneriyorum
      Ama neredeyse kimse dinlemiyor; sonra CLI'da sürünüyor, branch'leri bozuyor, main'in gerisinde kalıyor ve rebase'den korkar hale geliyorlar
      UI'de sürükle-bırak ya da benzeri yollarla çözülebilecek pek çok şeyi, adeta bir tür kendine eziyet gibi, komut satırında yapmaya çalışıyorlar
    • Ağaçla etkileşimin nasıl olduğunu görsel olarak görmek gerekiyorsa, iş arkadaşlarıma faydalı olan bir etkileşimli eğitim var: https://learngitbranching.js.org/
    • Geçmişte sürüm kontrolü için GUI frontend'ler kullanmış biri olarak, her şey yolunda giderken bunlar iyiydi ama işler bir kez karışınca toparlamakta kimsenin yardımı pek dokunmuyordu
      Arayüz ne kadar kötü olursa olsun, ekipteki diğer insanların kullandığı arayüzün aynısını kullanmak iletişim açısından daha kolaydı; ayrıca internetteki sorun giderme kaynakları da çoğunlukla “standart yöntem”i varsaydığından daha işe yarıyordu
  • Nisan 2025'te Git'in 20. yılı olacak olması kulağa oldukça tuhaf geliyor
    O dönemde işin içindeydim ve SVN ya da CVS'nin yerine git, darcs, bazaar arasından hangisini öğrenmem gerektiğini düşündüğümü hatırlıyorum
    Mercurial'i de denedim mi bilmiyorum
    “GitHub etkisi”nin sürüm kontrol sistemi alanında yeni oyunculara duyulan ihtiyacı fiilen öldürüp öldürmediğini merak ediyorum
    Belki de bir noktada yeterince yak tıraşı yapılmıştır

    • GitHub etkisi, tüm ağ etkileri gibi gerçekten var, ama bu iyileştirmenin imkansız olduğu anlamına gelmiyor
      Tamamen jj'ye geçtim ve Git uyumluluğu olduğu için başkalarının da birlikte geçmesini beklemem gerekmiyor
      GitHub'ın çeşitli yönleri beni giderek daha fazla hayal kırıklığına uğratıyor; bir gün herkesin onu da aşmasını umuyorum, ama o tarafa giden yolun o kadar basit olduğu pek söylenemez
    • SVN her zaman iyi çalıştı
      SVN sezgisel ve vakaların %99'unda yeterince iyi çalışıyor, bu yüzden insanlara ayrıca “öğretmek” gerekmiyor
      Hepimiz 1337 hacker taklidi yapmayı bırakıp Git'in çoğu insan için gereğinden fazla bir araç olduğunu kabul etsek keşke
    • Her türlü iyileştirme en azından Git uyumlu olmalı, örneğin jj gibi
  • Git'in ne yaptığına dair zihinsel modelimin oldukça iyi olduğunu düşünüyorum, ama komut biraz karmaşıklaşınca argümanları hatırlayamıyorum
    Komutlar ne keşfedilebilir ne de ezberlemesi kolay
    Sorun metin tabanlı arayüzün kötü olması mı, yoksa ağaçları manipüle etmeyi metinle anlatmanın kendisinin zor olması mı bilmiyorum

  • Bu başlıkta hg ve Mercurial arama sonuçlarını görmek hoşuma gitti
    Mercurial'in dağıtık sürüm kontrol savaşını kaybetmiş olmasına hâlâ inanamıyorum; bence daha iyi araçtı

    • Bugünün ölçütlerine göre bile Mercurial tartışmasız daha iyi bir araç
      Kullanılabilir, kararlı, sahada kendini kanıtlamış ve aktif olarak destekleniyor
      “Görmek istediğin değişim ol” anlayışına yakın, o yüzden sadece kullanmak yeterli
      Kaybetti gibi ifadeler ters etki yaratıyor ve bu sözlerin taşıdığı örtük caydırıcılık Mercurial benimsenmesini düşük tutuyor
      Barındırma en azından Sourcehut ve heptapod.host üzerinde mümkün
      Ben de kişisel bir Heptapod instance'ı işletiyorum; Gitlab fork'una Mercurial için doğrudan destek eklenmiş hali ve gayet iyi çalışıyor
    • İlginç, ama GitHub bağlantısı var mı diye merak ediyorum
    • Mercurial daha yavaş bir araç
    • Mercurial barındırmaya ihtiyacınız varsa şu barındırmayı kullanabilirsiniz: https://hg.reactionary.software/
  • Git, birçok açıdan sezgiyi takip etmediği için kafama tam oturmayan teknolojilerden biri oldu
    Uzun süredir kullanmıyorsanız, neredeyse her işlem için Google'a bakmanız ya da man sayfasını açmanız gerekebilir

    • SVN ile başlayıp sonra Git'e geçtiğimde, farkları öğrenmem gerekti ama birçok açıdan daha kolay hale geldiğini hissettim
      Özellikle büyük bir kod tabanını SVN'de çok sayıda branch ile yönetip sonra yeniden birleştirme kabusunu yaşadıysanız, Git'in bu konuda inanılmaz derecede daha iyi olduğunu görüyorsunuz
      Git'e en baştan başlayan biri için öğrenme eğrisi çok daha dik olabilir, ama insanların tam olarak nerede takıldığını anlamak yine de bazen zor geliyor
    • Dün geçmişten bir dosyayı silmeye çalıştım
      Yerleşik yöntem olan filter-branch kullanılırsa uzun bir gecikmenin ardından Control+C'ye basıp filter-repo adlı üçüncü taraf bir Python betiğini indirmenizi söyleyen bir uyarı çıkıyor
  • Gurur duymuyorum ama en sık başvurduğum “mahvettim” Git çözümü, yerel depoyu silip yeniden clone etmek ve sonra değişiklikleri tekrar uygulamak
    Vakaların %95'inde harika çalışıyor, geri kalanında ise DevOps sorumlusundan yardım istiyorum

    • Git'i neredeyse 15 yıldır kullanıyorum ve Git'i dahili olarak kullanarak belirli sonuçlar üreten programlar/ürünler de iki kez yaptım, ama yine de Git'te biraz riskli bir iş yapmadan önce bazen cp -R .git ../git-backup gibi bir şey yapıyorum
      Çünkü çok kötü bozarsam tüm .git dizinini eski kopyayla değiştirebiliyorum
      Bu, belirli bir işlemi ya da işlem grubunu doğru şekilde geri almanın yolunu bulmaya çalışmaktan çok daha hızlı
    • Yalnız değilsin: https://xkcd.com/1597/
  • İnsanların Git hakkında gerçekten içselleştirmesini istediğim şey, Git'in yalnızca eklemeli bir veri deposu olduğudur
    Çalışmanızı depoya koymak için commit etmeniz yeterlidir ve bir kez içeri girdikten sonra bu çalışmayı Git komutlarıyla kaldıramazsınız
    reflog da bu ilkeye göre çalışır
    Ne yaparsanız yapın, önceki branch durumuna dönüp veri deposundaki çalışmayı geri getirebilirsiniz
    Ama commit etmezseniz Git yardımcı olamaz, o yüzden sadece commit etmek gerekir
    Her zaman commit edin; bu size fazla yük geliyorsa ya da sürekli unutuyorsanız, aracı düzeltmeniz gerekir
    Bütün gün kod yazıyorsanız genel olarak en az saatte bir kez commit etmelisiniz

  • Küçük bir şey ama git reflog gördüğüm her seferinde bana re-flog gibi görünüyor
    Belki de bu yüzden bu komutu doğru düzgün ezberleyemiyorum; bilinçaltımda bunu “reference log” olarak düşünmüyor gibiyim
    Onun yerine, sanki Git'e bir şeyi yeniden ucuza satmasını söylüyormuşum gibi geliyor

  • Git kullanıcısı değilim ama böyle şeyleri görünce git commit işleminin kendi başına pek bir anlamı olmadığını, asıl önemli olanın commit’in push edildiği veya merge edildiği an olduğunu daha güçlü düşünüyorum
    Bu, metin dosyası kaydetmeye benziyor
    Bir dosyayı her kaydettiğimizde kısa bir mesaj yazmadığımız için, bunu da öyle geçebiliriz

    • Zorlu bir debug yolculuğunun sonunda ortaya çıkan bir değişiklikse ve başkalarının buna daha sonra tekrar bakma ihtimali varsa mesaj yazarım
      Kısa değil; değişiklik ne kadar küçükse açıklama bazen o kadar uzun olur
      Etkisi büyük olan 2-3 satırlık bir değişiklik için 2-3 paragraf yazdığım da oldu
    • Yerel commit ile uzak commit’in amacının farklı olduğu çok açık görünüyor
      Yerel commit, her şeyin derlendiği ve çalıştığı noktada bir kontrol noktası oluşturma yöntemidir
      Böylece değişiklikleri stash edebilir veya bir şeyi bozduğunda çalışan duruma geri dönebilirsin
      Push etmeden önce reset ile bunları uygun commit’lere bölersen uzak geçmiş temiz kalır ve bir wip yığınına dönüşmez
      Ya da PR’ı squash merge ederek iki tarafın avantajını da elde edebilirsin
    • Commit, Git’in içerde dosyanın bir kopyasını oluşturmasını sağlayan bir işlem olduğu için çok önemlidir
      Yine de more fixes gibi anlamsız mesajlar girmeye gerek yok
      Genelde (WIP) özellik adı gibi bir ilk commit oluşturup memnun kalana kadar commit’i sürekli amend eder, sonra da en sonda mesajdan (WIP) ifadesini kaldırırım
    • Bunu metin dosyası kaydetmeyle karşılaştırıyorum ama her gece yatarken gün hakkında kısa bir yazı yazan birçok insan da var
      Git revision log’unu da buna benzer bir günlük olarak görüyorum
      Ayrı bir TODO belgesinden daha düzenlidir ve zaten yapılmış işi okunması kolay bir biçimde anlatmaya teşvik eder
      Ne kadar geri alınacağına karar verirken o zamanki commit’te ne düşündüğünü bilmek faydalı olabilir; yaklaşım yanlışsa sonradan yapılan analiz için de yardımcı olur
      Merge veya push edilmemiş işler hedefi olan ayrı değişiklikler halinde düzenlenmişse, bunların bir kısmı iyi bir kısmı kötüyken iyi olan bölümleri korumak çok daha kolay olur
    • Push edilene kadar her şey yereldir, yani istediğin gibi davranabilirsin
      Yine de teknik olarak commit’ler anlamsız değildir
      Commit yoksa push veya merge edilecek bir şey de yoktur
      Teknik olmayan açıdan da neden bir şeyi değiştirdiğini düz metinle kaydetmek çok faydalıdır
      Böyle açıklamaları fazla sık yazmak zorunda kalıyorsan, “değişiklik” birimini daha küçük tutmalı ya da aynı anda dokunduğun işleri daha seçici belirlemelisin
      Git kullanmıyorsan sürüm kontrolünü ve değişiklik yönetimini neyle yaptığını merak ediyorum