Git sıfırlamayı geri al - hazırlama alanında onaylanmamış dosyalarla zor


110

İşimi kurtarmaya çalışıyorum. Aptalca yaptım git reset --hard, ama ondan önce sadece get add .yaptım ve yapmadım git commit. Lütfen yardım et! İşte günlüğüm:

MacBookPro:api user$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)

#   modified:   .gitignore
...


MacBookPro:api user$ git reset --hard
HEAD is now at ff546fa added new strucuture for api

git reset --hardBu durumda geri almak mümkün mü ?


@MarkLongair harika adam! Az önce işimi geri aldın! Tüm çıktıların dosyalarını oluşturmak için bir Python betiği yazdım! Senaryoyu cevap olarak ekleyeceğim
Boy

4
"Aptalca" değil .. ama "safça" ... çünkü ben AYNISI yaptım!
Rosdi Kasim

Yine de aptalca olabilir ;-)
Duncan McGregor

İşte bunların bir kısmını nasıl tersine çevireceğinizle ilgili harika bir makale . Biraz manuel çalışma gerektirecek.
Jan Swart

@MarkLongair `` bul .git / nesneler / -type f -printf '% TY-% Tm-% Td% TT% p \ n' | sıralama `` benim için çalıştı. tarihler de görünür, blobları sondan kontrol etmeye başlayın.
ZAky

Yanıtlar:


175

Dizine eklediğiniz tüm dosyaları (örneğin sizin durumunuzda olduğu gibi) kurtarabilmeniz gerekir, git add .ancak biraz iş olabilir. Dizine bir dosya eklemek için git, onu nesne veritabanına ekler; bu, çöp toplama henüz gerçekleşmediği sürece dosyanın kurtarılabileceği anlamına gelir. Jakub Narębski'nin cevabında bunun nasıl yapılacağına dair bir örnek var:

Ancak, bunu bir test deposunda denedim ve birkaç sorun vardı - --cachedolmalıydı --cacheve .git/lost-founddizini gerçekten oluşturmadığını buldum . Ancak, aşağıdaki adımlar benim için çalıştı:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)")

Bu, nesne veritabanındaki herhangi bir ref, indekste veya reflog aracılığıyla erişilemeyen tüm nesnelerin çıktısını almalıdır. Çıktı şunun gibi görünecek:

unreachable blob 907b308167f0880fb2a5c0e1614bb0c7620f9dc3
unreachable blob 72663d3adcf67548b9e0f0b2eeef62bce3d53e03

... ve bu lekelerin her biri için şunları yapabilirsiniz:

git show 907b308

Dosyanın içeriğini çıkarmak için.


Çok fazla çıktı mı?

Sehe'nin aşağıdaki yorumuna yanıt olarak güncelleme :

Bu komutun çıktısında listelenen çok sayıda işlem ve ağaç olduğunu fark ederseniz, başvurulmayan işlemlerden referans verilen nesneleri çıktıdan çıkarmak isteyebilirsiniz. (Genellikle bu işlemlere reflog aracılığıyla geri dönebilirsiniz - biz sadece dizine eklenen, ancak hiçbir zaman bir kaydetme yoluyla bulunamayan nesnelerle ilgileniyoruz.)

İlk olarak, komutun çıktısını şu şekilde kaydedin:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > all

Artık ulaşılamayan bu işlemlerin nesne adları şu şekilde bulunabilir:

egrep commit all | cut -d ' ' -f 3

Dolayısıyla, yalnızca dizine eklenmiş, ancak herhangi bir noktada taahhüt edilmemiş ağaçları ve nesneleri şu şekilde bulabilirsiniz:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") \
  $(egrep commit all | cut -d ' ' -f 3)

Bu, dikkate almanız gereken nesnelerin sayısını büyük ölçüde azaltır.


Güncelleme: Aşağıdaki Philip Oakley , dikkate alınacak nesnelerin sayısını azaltmanın başka bir yolunu öneriyor; bu, yalnızca altındaki en son değiştirilen dosyaları dikkate almaktır .git/objects. Bunları şununla bulabilirsiniz:

find .git/objects/ -type f -printf '%TY-%Tm-%Td %TT %p\n' | sort

(Bu findçağrıyı burada buldum .) Bu listenin sonu şöyle görünebilir:

2011-08-22 11:43:43.0234896770 .git/objects/b2/1700b09c0bc0fc848f67dd751a9e4ea5b4133b
2011-09-13 07:36:37.5868133260 .git/objects/de/629830603289ef159268f443da79968360913a

Bu durumda, bu nesneleri aşağıdakilerle görebilirsiniz:

git show b21700b09c0bc0fc848f67dd751a9e4ea5b4133b
git show de629830603289ef159268f443da79968360913a

( /Nesne adını almak için yolun sonundaki öğesini kaldırmanız gerektiğini unutmayın .)


Cevabımı göndermeden önce tam olarak bu adımları eklemeyi düşündüm. Ancak, bunu yerel depolarımdan biriyle denediğimde, yüzlerce ulaşılamaz kişi buldum ve bunları gönderilen tarihe göre sıralamak için yeterli bir yaklaşım bulamadım. Şimdi bu harika olurdu
sehe

3
Son kaydetmenizden sonra olan en son nesneler için nesne zaman damgalarına bir göz atmak mümkün değil mi? yoksa bir şey mi kaçırıyorum?
Philip Oakley

1
@Philip Oakley: Bu öneri için teşekkürler, nesne veritabanında en son değiştirilen nesneleri bulma önerisini ekledim.
Mark Longair

2
Ahbap, bu harika! Bu, az önce @ $$ kartımı kurtardı. Ve biraz git bilimi öğrendim.
Dizine

2
Burada kimsenin kireç ışığını çalmaması için. Ama aynı problemle karşılaştığım ve tüm işimi kaybettiğimi düşündüğüm için bir not olarak. IDE kullananlar için , IDE'lerin genellikle tek tıkla kurtarma çözümü vardır. PHPstorm durumunda, dizini sağ tıklayıp yerel geçmişi göster seçeneğini seçip ardından geçerli son duruma geri dönmem gerekiyordu.
Shalom Sam

58

Az önce bir işlem yaptım git reset --hardve bir taahhütümü kaybettim. Ama commit karmasını biliyordum, bu yüzden onu git cherry-pick COMMIT_HASHgeri yükleyebildim.

Bunu taahhüdü kaybettikten sonra birkaç dakika içinde yaptım, bu yüzden bazılarınız için işe yarayabilir.


5
Teşekkür ederim teşekkür ederim teşekkür ederim Bu cevap sayesinde bir günlük işi kurtarmayı
başardık

21
Muhtemelen bahsetmeye değer bu karmaları kullanarak git reflog, örneğin git reset --hard-> git reflog(HEAD @ {1} karmasına bakarak) ve son olarakgit cherry-pick COMMIT_HASH
Javier López

2
Bunu okuyan arkadaşlar, bunun yalnızca tek bir commit hash için işe yaradığına dikkat edin, eğer birden fazla commit varsa, problemi bir anda çözmez!
Ain Tohvri

4
Aslında "ileri" yi sıfırlayabilirsiniz. Bu nedenle, birden fazla kaydetmeyi sıfırladıysanız, başka bir 'git reset --hard <commit>' yapmak, o noktaya kadar tüm commit zincirini geri yüklemelidir. (henüz GC'lenmiş olmadıklarına göre)
akimsko

6
Kaybedilen taahhütleri kurtarmak için tek git reset --hardgit reset --hard @{1}
satırlık (

13

Mark Longair sayesinde eşyalarımı geri aldım!

İlk önce tüm karmaları bir dosyaya kaydettim:

git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") > allhashes

daha sonra hepsini (ulaşılamaz blob'u kaldırarak) bir listeye koydum ve tüm verileri yeni dosyalara koydum ... dosyalarınızı seçmeniz ve ihtiyacınız olan yeniden adlandırmanız gerekiyor ... ama sadece birkaçına ihtiyacım vardı dosyalar..hope bu birine yardımcı olur ...

commits = ["c2520e04839c05505ef17f985a49ffd42809f",
    "41901be74651829d97f29934f190055ae4e93",
    "50f078c937f07b508a1a73d3566a822927a57",
    "51077d43a3ed6333c8a3616412c9b3b0fb6d4",
    "56e290dc0aaa20e64702357b340d397213cb",
    "5b731d988cfb24500842ec5df84d3e1950c87",
    "9c438e09cf759bf84e109a2f0c18520",
    ...
    ]

from subprocess import call
filename = "file"
i = 1
for c in commits:
    f = open(filename + str(i),"wb")
    call(["git", "show", c],stdout=f)
    i+=1

1
Evet! Bu tam olarak ihtiyacım olan şey. Tüm dosyaları yeniden oluşturmak için Python betiğini de seviyorum. Diğer cevaplar, çöp toplama ile verilerimi kaybetme konusunda beni endişelendirdi, bu yüzden dosyaları
Eric Olson

1
Bu senaryoyu bu cevaba göre yazdım. Kutudan çıktığı gibi çalışır: github.com/pendashteh/git-recover-index
Alexar

1
Daha kolay bu şekilde otomatik hale getirmek bulmak: mkdir lost; git fsck --cache --unreachable $(git for-each-ref --format="%(objectname)") | grep -Po '\s\S{40}$' | xargs -i echo "git show {} > lost/{}.blob" | sh. Dosyalar sona ereceklost/*.blob
Matija Nalis

6

@ Ajedi32'nin yorumlardaki çözümü tam da bu durumda benim için çalıştı.

git reset --hard @{1}

Tüm bu çözümlerin git gc olmamasına dayandığını ve bazılarının buna neden olabileceğini unutmayın, bu nedenle herhangi bir şey denemeden önce .git dizininizin içeriğini sıkıştırırım, böylece bir anlık görüntünüz yoksa geri dönebilirsiniz. Senin için çalışmıyorum.


Bir sürü yüklenmemiş dosyam vardı. Sonra 1 dosya teslim ettim ve depomu git reset --hardbilinmeyen herhangi bir şekilde bozan a yaptım . @Duncan, @ {1} ne yapıyor? ve hangi yorumu kastediyorsunuz? Bir sıfırlanıyor git resetmu?
alpha_989

Sanırım @ {1}, son fakat bir commit için göreceli bir referans, ama ben uzman değilim, sadece benim için neyin işe yaradığını bildiriyorum
Duncan McGregor

3

Aynı sorunla karşılaştı, ancak değişiklikleri dizine eklemedi. Yani yukarıdaki tüm komutlar bana istenen değişiklikleri geri getirmedi.

Yukarıdaki ayrıntılı cevapların hepsinden sonra, bu saf bir ipucu, ama belki de benim yaptığım gibi ilk önce bunu düşünmeyen birini kurtaracak.

Umutsuzluk içinde, editörümde (LightTable) CTRL-Z tuşlarına her açık sekmede bir kez basmaya çalıştım - bu şans eseri o sekmedeki dosyayı git reset --hard. HTH.


1
Tam olarak aynı durum, dizine eklemeyi kaçırdı ve donanımdan sıfırlama yaptı ve çözümlerin hiçbiri yukarıda çalışmadı. Bu bir SMART hareketiydi, teşekkürler Ctrl + Z yaptım ve günümü kurtardım. Editörüm: SublimeText. Benim tarafımdan bir yukarı!
Vivek

1

Bu muhtemelen oradaki git uzmanları için açıktır, ancak bunu koymak istedim çünkü çılgınca araştırmamda bunun ortaya çıktığını görmedim.

Bazı dosyaları git reset --hardhazırladım ve bir yaptım , biraz çıldırdım ve sonra durumumun tüm dosyalarımın hala aşamalı olduğunu ve tüm silme işlemlerinin aşamasız olduğunu gösterdiğini fark ettim.

Bu noktada, silme işlemlerini aşamalandırmadığınız sürece, bu aşamalı değişiklikleri gerçekleştirebilirsiniz. Bundan sonra, sadece git reset --hardbir kez daha yapmak için cesaretinizi toplamalısınız , bu da sizi sahnelediğiniz ve şimdi taahhüt ettiğiniz o değişikliklere geri götürecektir.

Yine, bu muhtemelen çoğu için yeni bir şey değil, ama umuyorum ki bana yardımcı olduğu ve bunu öneren hiçbir şey bulamadığım için başka birine yardımcı olabilir.


1

Tanrım, bu soru ve cevaplarıyla karşılaşana kadar saçımı çektim. Sorulan soruya verilen doğru ve kısa cevabın yalnızca yukarıdaki yorumlardan ikisini bir araya getirirseniz verilebileceğine inanıyorum, bu yüzden burada hepsi tek bir yerde:

  1. Chilicuil tarafından belirtildiği gibi, git refloggeri dönmek istediğiniz commit karmasını orada tanımlamak için çalıştırın .

  2. Akimsko tarafından belirtildiği gibi, yalnızca bir commit kaybetmedikçe büyük olasılıkla kiraz seçmek istemezsiniz, bu yüzden daha sonra koşmalısınız git reset --hard <hash-commit-you-want>

Egit Eclipse kullanıcıları için not: Bu adımları egit ile Eclipse'de gerçekleştirmenin bir yolunu bulamadım. Eclipse'i kapatmak, yukarıdaki komutları bir terminal penceresinden çalıştırmak ve ardından Eclipse'i yeniden açmak benim için gayet iyi çalıştı.


0

Yukarıdaki çözümler işe yarayabilir, ancak git-s karmaşık geri alma işlemlerinden geçmek yerine bundan kurtulmanın daha basit yolları vardır . Çoğu git sıfırlamasının az sayıda dosyada gerçekleştiğini tahmin ediyorum ve zaten VIM kullanıyorsanız, bu zaman açısından en verimli çözüm olabilir. Uyarı, ViM'sher iki şekilde de kullanmanız gereken kalıcı-geri almayı zaten kullanıyor olmanız gerektiğidir, çünkü size sınırsız sayıda değişikliği geri alma yeteneği sağlar.

İşte adımlar:

  1. Vim tuşuna basın :ve komutu yazın set undodir. Eğer varsa persistent undoGözlerinde farklı açık .vimrc, bu benzer bir sonuç gösterecektir undodir=~/.vim_runtime/temp_dirs/undodir.

  2. Deponuzda git logson taahhüdü yaptığınız son tarihi / saati öğrenmek için kullanın

  3. Senin için kabuk gezindiğini olarak undodirkullanarak cd ~/.vim_runtime/temp_dirs/undodir.

  4. Bu dizinde, son işlemden bu yana değiştirdiğiniz tüm dosyaları bulmak için bu komutu kullanın.

    find . -newermt "2018-03-20 11:24:44" \! -newermt "2018-03-23" \( -type f -regextype posix-extended -regex '.*' \) \-not -path "*/env/*" -not -path "*/.git/*"

    Burada "2018-03-20 11:24:44" son işlemenin tarih ve saatidir. Yaptığınız tarih git reset --hard"2018-03-22" ise , "2018-03-22" ve ardından "2018-03-23" kullanın. Bunun nedeni, alt sınırın kapsayıcı olduğu ve üst sınırın dışlayıcı olduğu bir bulmanın tuhaflığıdır. https://unix.stackexchange.com/a/70404/242983

  5. Sonra, dosyaların her birine gidin, onları vim'de açın ve "20 milyondan önce" yapın. "H daha önce" kullanarak "daha önce" hakkında daha fazla ayrıntı bulabilirsiniz. Burada earlier 20m20 dakika önce, 20 dakika önce yaptığınızı varsayarak dosyanın durumuna geri dönün demek git hard --reset. Bunu findkomuttan çıkan tüm dosyalar için tekrarlayın . Eminim ki birileri bunları birleştirecek bir senaryo yazabilir.


0

IntelliJ kullanıyorum ve her dosyaya bakıp şunları yapabildim:

Edit -> reload from disk

Neyse ki, git statusçalışma değişikliklerimi silmeden önce bir doğru yapmıştım, bu yüzden tam olarak neyi yeniden yüklemem gerektiğini biliyordum.


0

Dosya hiyerarşinizi hatırlamak ve Mark Longair tekniğini Phil Oakley modifikasyonu ile kullanmak çarpıcı sonuçlar verir.

Esasen, en azından dosyaları depoya eklediyseniz ancak kaydetmediyseniz, etkileşimli olarak kullanarak geri yükleyebilir git show, günlüğü inceleyebilir ve her bir dosyayı oluşturmak için kabuk yeniden yönlendirmeyi kullanabilirsiniz (ilgilendiğiniz yolu hatırlayarak).

HTH!


0

Yakın zamanda bir gözden git diffgeçirdiyseniz, değişiklikleri henüz hazırlamamış olsanız bile, bunun gibi olaylardan kurtarmanın başka bir yolu vardır: çıktısı git diffhala konsol arabelleğinizdeyse, yukarı kaydırıp kopyalayıp yapıştırabilirsiniz. bir dosyaya fark ve kullanmak patchsizin ağacına diff uygulamak için aracı: patch -p0 < file. Bu yaklaşım beni birkaç kez kurtardı.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.