Jak mogę cofnąć resetowanie gita - hard HEAD ~ 1?


1155

Czy można cofnąć zmiany spowodowane następującym poleceniem? Jeśli tak to jak?

git reset --hard HEAD~1

8
Napisałem kompletny przewodnik do odzyskiwania utraconego zatwierdzenia za pomocą git. Ma nawet ilustracje :-) [Sprawdź] [fixLink] [fixLink]: programblings.com/2008/06/07/…
webmat

12
--hardodrzuca niezatwierdzone zmiany. Ponieważ nie są one śledzone przez git, nie ma możliwości ich przywrócenia za pomocą git.
Zaz


1
To jest świetny artykuł, który pomoże ci w odzyskaniu plików.
Jan Swart

2
To świetny zasób prosto z Github: Jak cofnąć (prawie) cokolwiek z Gitem
jasonleonhard

Odpowiedzi:


1770

Pat Notz ma rację. Możesz odzyskać zatwierdzenie, o ile upłynęło kilka dni. git tylko śmieci są zbierane po około miesiącu, chyba że wyraźnie powiesz mu, aby usunąć nowsze obiekty BLOB.

$ git init
Initialized empty Git repository in .git/

$ echo "testing reset" > file1
$ git add file1
$ git commit -m 'added file1'
Created initial commit 1a75c1d: added file1
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 file1

$ echo "added new file" > file2
$ git add file2
$ git commit -m 'added file2'
Created commit f6e5064: added file2
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 file2

$ git reset --hard HEAD^
HEAD is now at 1a75c1d... added file1

$ cat file2
cat: file2: No such file or directory

$ git reflog
1a75c1d... HEAD@{0}: reset --hard HEAD^: updating HEAD
f6e5064... HEAD@{1}: commit: added file2

$ git reset --hard f6e5064
HEAD is now at f6e5064... added file2

$ cat file2
added new file

Możesz zobaczyć w przykładzie, że plik2 został usunięty w wyniku twardego resetu, ale został przywrócony na miejscu, gdy zresetowałem przez dziennik reflog.


222
Możesz użyć „git reset --hard HEAD @ {1}”, nie musisz używać SHA1. W większości przypadków powinno wystarczyć użycie „git reset --hard ORIG_HEAD”.
Jakub Narębski

39
git log -gmoże być nieco ładniejszym sposobem na przeglądanie dziennika niż git reflog.
Dan Molding

60
Jest z tym bardzo ważne zastrzeżenie… i jest to część „- twarda”. --hard zdmuchuje twoje lokalne niezatwierdzone zmiany. I nie możesz ich odzyskać w ten sposób (ponieważ nigdzie ich nie popełniono). Wierzę, że nic nie możesz na to poradzić :(
Michael Anderson,

3
^ Po prostu wiedz, że możesz ukryć swoje lokalne zmiany przed zresetowaniem - twarde, a następnie po prostu je pop i nic nie stracisz! Muszę kochać dupka.
Lethjakman

5
Wolę git reflogpo git log -gprostu dlatego, że masz wszystkie informacje w jednym wierszu z sha1, HEAD info i zatwierdzasz wiadomości w jednej linii. O wiele łatwiejszy do odczytania.
Snowcrash

370

To, co chcesz zrobić, to określić sha1 zatwierdzenia, które chcesz przywrócić. Możesz uzyskać sha1, sprawdzając reflog ( git reflog), a następnie wykonując polecenie

git reset --hard <sha1 of desired commit>

Ale nie czekaj zbyt długo ... po kilku tygodniach git w końcu zobaczy to zatwierdzenie jako niepowiązane i usunie wszystkie obiekty BLOB.


2
ostrzeżenie dla mnie kilka minut temu: spowoduje to zresetowanie wszystkich modyfikacji. nie dotknie jednak nieśledzonych plików.
aexl

174

Odpowiedź jest ukryta w szczegółowej odpowiedzi powyżej, możesz po prostu zrobić:

$> git reset --hard HEAD@{1}

(Zobacz wyniki git reflog show )


17
Pamiętaj, że nie jest to rozwiązanie, jeśli dokonałeś jakichkolwiek innych zmian repo od czasu zresetowania. Zdecydowanie spójrz na reflog przed uruchomieniem czegokolwiek.
forresthopkinsa

2
Przypadkowo zresetowałem moje repozytorium, myślałem, że moja praca została utracona na zawsze. Ta odpowiedź uratowała mi dzień.
Zaiyang Li,

oh! naprawdę oszczędzasz mi dzień =)
jfcogato

Świetny. To naprawdę pomogło.
Prashant Biradar

116

Można go odzyskać, jeśli Git nie zebrał jeszcze śmieci.

Uzyskaj przegląd zwisających zatwierdzeń dzięki fsck:

$ git fsck --lost-found
dangling commit b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf

Odzyskaj zwisające zatwierdzenie z rebase:

$ git rebase b72e67a9bb3f1fc1b64528bcce031af4f0d6fcbf

1
To niesamowite rzeczy. Życie zapisane.
Mohhamad Hasham

Szczegółowe wyjaśnienie można znaleźć tutaj: medium.com/@CarrieGuss/… . Rzeczy ratujące życie.
tuan.dinh

49

Jeśli masz szczęście, tak jak ja, możesz wrócić do edytora tekstu i nacisnąć „cofnij”.

Wiem, że to nie jest właściwa odpowiedź, ale zaoszczędziło mi to pół dnia pracy, więc mam nadzieję, że zrobi to samo dla kogoś innego!


3
To właściwie bardzo dobra wskazówka, uratowała mnie wiele razy;) I jest o wiele prostsza niż robienie czegokolwiek w git ...
severin

3
i dziękuję wam za całą dobroć łaskawą na całym świecie. Dziękuję Ci. Dziękuję Ci. Dziękuję Ci.
Podróż

9
Jest to jedyny sposób na odzyskanie niestabilnych zmian w plikach po twardym resecie. Też mnie uratował;)
Czarek Tomczak

4
Jako dodatkową wskazówkę, niektóre IDE jako zaćmienie mają również zapisaną historię ostatnich plików. W ten sposób możesz nawet odzyskać starsze zmiany po zamknięciu edytora. To działało dla mnie cuda.
martin

1
Niech Bóg cię błogosławi Chris
Adam Waite

44

o ile wiem, --hardodrzuca niezaangażowane zmiany. Ponieważ nie są one śledzone przez git. ale możesz cofnąć discarded commit.

$ git reflog

wyświetli:

b0d059c HEAD@{0}: reset: moving to HEAD~1
4bac331 HEAD@{1}: commit: added level introduction....
....

gdzie 4bac331jest discarded commit.

Teraz wystarczy przesunąć głowę do tego zatwierdzenia:

$ git reset --hard 4bac331

34

Przykład przypadku IRL:

$ git fsck --lost-found

Checking object directories: 100% (256/256), done.
Checking objects: 100% (3/3), done.
dangling blob 025cab9725ccc00fbd7202da543f556c146cb119
dangling blob 84e9af799c2f5f08fb50874e5be7fb5cb7aa7c1b
dangling blob 85f4d1a289e094012819d9732f017c7805ee85b4
dangling blob 8f654d1cd425da7389d12c17dd2d88d318496d98
dangling blob 9183b84bbd292dcc238ca546dab896e073432933
dangling blob 1448ee51d0ea16f259371b32a557b60f908d15ee
dangling blob 95372cef6148d980ab1d7539ee6fbb44f5e87e22
dangling blob 9b3bf9fb1ee82c6d6d5ec9149e38fe53d4151fbd
dangling blob 2b21002ca449a9e30dbb87e535fbd4e65bac18f7
dangling blob 2fff2f8e4ea6408ac84a8560477aa00583002e66
dangling blob 333e76340b59a944456b4befd0e007c2e23ab37b
dangling blob b87163c8def315d40721e592f15c2192a33816bb
dangling blob c22aafb90358f6bf22577d1ae077ad89d9eea0a7
dangling blob c6ef78dd64c886e9c9895e2fc4556e69e4fbb133
dangling blob 4a71f9ff8262701171d42559a283c751fea6a201
dangling blob 6b762d368f44ddd441e5b8eae6a7b611335b49a2
dangling blob 724d23914b48443b19eada79c3eb1813c3c67fed
dangling blob 749ffc9a412e7584245af5106e78167b9480a27b
dangling commit f6ce1a403399772d4146d306d5763f3f5715cb5a    <- it's this one

$ git show f6ce1a403399772d4146d306d5763f3f5715cb5a

commit f6ce1a403399772d4146d306d5763f3f5715cb5a
Author: Stian Gudmundsen Høiland <stian@Stians-Mac-mini.local>
Date:   Wed Aug 15 08:41:30 2012 +0200

    *MY COMMIT MESSAGE IS DISPLAYED HERE*

diff --git a/Some.file b/Some.file
new file mode 100644
index 0000000..15baeba
--- /dev/null
+++ b/Some.file
*THE WHOLE COMMIT IS DISPLAYED HERE*

$ git rebase f6ce1a403399772d4146d306d5763f3f5715cb5a

First, rewinding head to replay your work on top of it...
Fast-forwarded master to f6ce1a403399772d4146d306d5763f3f5715cb5a.

2
Zwisające kropelki brzmią jak potwór AD&D!
sbichenko

Dzięki @Stian Dobrze wyjaśnione! Chciałbym dodać dla innych, którzy znaleźli tę odpowiedź, że jeśli masz więcej niż jeden „zwisający” zatwierdzenie, nie jest pewne, czy chcesz zrobić bazowanie w ostatnim wierszu :)
JimiSweden

git show zapisał niektóre moje pliki, dziękuję bardzo stary!
William

32

W większości przypadków tak.

W zależności od stanu, w jakim znajdowało się Twoje repozytorium, gdy uruchomiłeś polecenie, efekty git reset --hardmogą być różne: od trywialnego do cofania, w zasadzie niemożliwe.

Poniżej wymieniłem szereg różnych możliwych scenariuszy i sposoby ich odzyskania.

Wszystkie moje zmiany zostały zatwierdzone, ale teraz zobowiązania zniknęły!

Taka sytuacja zwykle występuje, gdy uruchamiasz git resetz argumentem, jak w git reset --hard HEAD~. Nie martw się, łatwo to odzyskać!

Jeśli po prostu pobiegłeś git reseti od tamtej pory nie zrobiłeś nic więcej, możesz wrócić do miejsca, w którym byłeś, dzięki tej linijce:

git reset --hard @{1}

Spowoduje to zresetowanie bieżącej gałęzi bez względu na jej stan przed ostatnią modyfikacją (w twoim przypadku najnowszą modyfikacją gałęzi będzie twardy reset, który próbujesz cofnąć).

Jeśli jednak użytkownik nie dokonano innych zmian w swojej branży od resetu, jedno-liner powyżej nie będą działać. Zamiast tego powinieneś uruchomić, aby zobaczyć listę wszystkich ostatnich zmian dokonanych w oddziale (w tym resetów). Ta lista będzie wyglądać mniej więcej tak:git reflog <branchname>

7c169bd master@{0}: reset: moving to HEAD~
3ae5027 master@{1}: commit: Changed file2
7c169bd master@{2}: commit: Some change
5eb37ca master@{3}: commit (initial): Initial commit

Znajdź operację na tej liście, którą chcesz „cofnąć”. W powyższym przykładzie byłby to pierwszy wiersz, który mówi „reset: przejście do HEAD ~”. Następnie skopiuj reprezentację zatwierdzenia przed (poniżej) tą operacją. W naszym przypadku będzie to master@{1}(lub 3ae5027oba reprezentują ten sam zatwierdzenie) i uruchomi się, git reset --hard <commit>aby przywrócić bieżącą gałąź do tego zatwierdzenia.

Zainscenizowałem swoje zmiany git add, ale nigdy się nie popełniłem. Teraz moje zmiany zniknęły!

Jest to nieco trudniejsze do odzyskania. git nie mają kopie plików dodanych, ale ponieważ kopie te nie były związane z żadnym konkretnym popełnić nie można przywrócić zmian naraz. Zamiast tego musisz zlokalizować poszczególne pliki w bazie danych git i przywrócić je ręcznie. Możesz to zrobić za pomocą git fsck.

Aby uzyskać szczegółowe informacje na ten temat, zobacz Cofnij resetowanie git - twarde z nieproszonymi plikami w obszarze przemieszczania .

Miałem zmiany w plikach w moim katalogu roboczym, z którymi nigdy nie wystawiałem git addi nigdy nie popełniłem. Teraz moje zmiany zniknęły!

O o. Nie chcę ci tego mówić, ale prawdopodobnie nie masz szczęścia. git nie przechowuje zmian, których nie dodajesz ani nie zatwierdzasz, i zgodnie z dokumentacją dlagit reset :

--ciężko

Resetuje indeks i działające drzewo. Wszelkie zmiany w śledzonych plikach w działającym drzewie, ponieważ <commit>zostały odrzucone.

Jest możliwe, że może być w stanie odzyskać swoje zmiany z pewnego rodzaju narzędzia do odzyskiwania dysku lub profesjonalnych usług odzyskiwania danych, ale w tym momencie to chyba więcej kłopotów niż to warte.


1
One-liner działał na mnie, dziękuję, ale zastanawiam się, co dokładnie robi „@ {1}” ..
Stan Bashtavenko

1
@StanB Dokumentacja znajduje się tutaj: git-scm.com/docs/git-rev-parse w zasadzie odnosi się do pierwszego wpisu dziennika w bieżącym oddziale.
Ajedi32

Dzięki za omówienie wszystkich spraw. Nie popełniłem ani nie dodałem mojego.
xdhmoore

21

Jeśli jeszcze nie wyrzuciłeś śmiecia ze swojego repozytorium (np. Używając git repack -dlub git gc, ale zauważ, że zbieranie śmieci może również nastąpić automatycznie), to twoje zatwierdzenie wciąż tam jest - po prostu nie jest już dostępne przez HEAD.

Możesz spróbować znaleźć swoje zatwierdzenie, przeglądając dane wyjściowe git fsck --lost-found.

Nowsze wersje Gita mają coś, co nazywa się „reflog”, który jest dziennikiem wszystkich zmian, które są dokonywane w refach (w przeciwieństwie do zmian, które są dokonywane w zawartości repozytorium). Na przykład za każdym razem, gdy zmieniasz HEAD (tj. Za każdym razem, gdy zmieniasz git checkoutgałęzie), które będą rejestrowane. I, oczywiście, git resetmanipulowałeś również HEAD, więc też został zalogowany. Możesz uzyskać dostęp do starszych stanów swoich referencji w podobny sposób, jak możesz uzyskać dostęp do starszych stanów swojego repozytorium, używając @znaku zamiast znaku ~, jak git reset HEAD@{1}.

Trochę czasu zajęło mi zrozumienie, jaka jest różnica między HEAD @ {1} a HEAD ~ 1, więc oto małe wyjaśnienie:

git init
git commit --allow-empty -mOne
git commit --allow-empty -mTwo
git checkout -b anotherbranch
git commit --allow-empty -mThree
git checkout master # This changes the HEAD, but not the repository contents
git show HEAD~1 # => One
git show HEAD@{1} # => Three
git reflog

HEAD~1Oznacza to , że „przejdź do zatwierdzenia przed zatwierdzeniem, na które HEAD obecnie wskazuje”, natomiast HEAD@{1}oznacza „przejdź do zatwierdzenia, na które HEAD wskazał, zanim wskazał, gdzie aktualnie wskazuje”.

To z łatwością pozwoli ci znaleźć utracone zatwierdzenie i je odzyskać.


2
Kolejne wyjaśnienie, które moim zdaniem byłoby jaśniejsze: HEAD ~ 1 oznacza przejście do „rodzica HEAD”, podczas gdy HEAD @ {1} przejdź do „cofnij się o krok w historii HEAD”
kizzx2

1
Problem polega na tym, że termin „historia” jest naprawdę przeciążony w VCS. Jeszcze innym sposobem wyrażenia jest to, że ~ cofa się w historii zmian , podczas gdy @ cofa się w historii chronologicznej lub czasowej . Ale żadna z trzech wersji nie jest szczególnie dobra.
Jörg W Mittag

@ kizzx2 (i Jorg) faktycznie te 3 wyjaśnienia, razem wzięte, bardzo pomagają - dzięki
Richard Le Mesurier

14

Przed odpowiedzią dodajmy tło, wyjaśniając, co to jest HEAD.

First of all what is HEAD?

HEADjest po prostu odniesieniem do bieżącego zatwierdzenia (najnowszego) w bieżącej gałęzi.
W HEADdanym momencie może być tylko jeden . (z wyłączeniem git worktree)

Zawartość HEADjest przechowywana w środku .git/HEADi zawiera 40 bajtów SHA-1 bieżącego zatwierdzenia.


detached HEAD

Jeśli nie korzystasz z ostatniego zatwierdzenia, co oznacza, że HEADnazywa się to wcześniejszym zatwierdzeniem w historii detached HEAD.

wprowadź opis zdjęcia tutaj

W wierszu polecenia będzie to wyglądać tak: SHA-1 zamiast nazwy gałęzi, ponieważ HEADnie wskazuje ona końca wierzchołka bieżącej gałęzi

wprowadź opis zdjęcia tutaj


Kilka opcji odzyskiwania po odłączeniu HEAD:


git checkout

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back

Spowoduje to pobranie nowej gałęzi wskazującej żądany zatwierdzenie.
To polecenie pobierze do danego zatwierdzenia.
W tym momencie możesz utworzyć gałąź i od tego momentu zacząć pracować.

# Checkout a given commit. 
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>

# create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

Zawsze możesz również użyć reflog.
git reflogwyświetli każdą zmianę, która zaktualizowała HEADi sprawdzenie żądanego wpisu ponownego logowania spowoduje HEADpowrót do tego zatwierdzenia.

Za każdym razem, gdy HEAD zostanie zmodyfikowany, pojawi się nowy wpis w reflog

git reflog
git checkout HEAD@{...}

Spowoduje to powrót do żądanego zatwierdzenia

wprowadź opis zdjęcia tutaj


git reset HEAD --hard <commit_id>

„Przesuń” głowę z powrotem do żądanego zatwierdzenia.

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts, if you've modified things which were
# changed since the commit you reset to.
  • Uwaga: ( od wersji Git 2.7 )
    możesz również korzystać z nich git rebase --no-autostash.


git revert <sha-1>

„Cofnij” podany zakres zatwierdzania lub zatwierdzania.
Polecenie reset spowoduje „cofnięcie” wszelkich zmian dokonanych w danym zatwierdzeniu.
Nowe zatwierdzenie z łatką cofania zostanie zatwierdzone, podczas gdy oryginalne zatwierdzenie pozostanie również w historii.

# add new commit with the undo of the original one.
# the <sha-1> can be any commit(s) or commit range
git revert <sha-1>

Ten schemat ilustruje, które polecenie robi co.
Jak widać, reset && checkoutzmodyfikuj HEAD.

wprowadź opis zdjęcia tutaj


Wygląda na to, że twój git reset HEAD --hard <commit_id>przykład pochodzi z stackoverflow.com/questions/4114095/... - Jeśli tak jest, czy możesz edytować atrybucję?
Rob

12

git reflog

  • Znajdź swój plik sha na liście, a następnie skopiuj go i wklej do tego polecenia:

git cherry-pick <the sha>


1
git-cherry-pick - Zastosuj zmiany wprowadzone przez niektóre istniejące zatwierdzenia. Myślę, że w tym kontekście jest to proste i bardzo pomocne
Amitesh

1
wszyscy tego szukają, gdy szukają sposobu na cofnięcie twardego resetowania zmian. ta odpowiedź powinna zyskać więcej głosów
ErenL

@ErenL Chyba właśnie pomyślałem, że ludzie lubią wykonywać dodatkową pracę. haha
ScottyBlades

1
Oto, właśnie uratowałeś mi dzień 🙂
Sylvernus Akubo

11

Wiem, że to stary wątek ... ale ponieważ wiele osób szuka sposobów na cofnięcie rzeczy w Git, nadal uważam, że dobrym pomysłem może być dalsze udzielanie wskazówek tutaj.

Gdy wykonujesz „dodawanie git” lub przenosisz cokolwiek z lewego górnego do lewego dolnego w git gui, zawartość pliku jest przechowywana w obiekcie blob, a zawartość pliku można odzyskać z tego obiektu blob.

Można więc odzyskać plik, nawet jeśli nie został on zatwierdzony, ale należy go dodać.

git init  
echo hello >> test.txt  
git add test.txt  

Teraz obiekt blob został utworzony, ale jest do niego odwoływany przez indeks, więc nie będzie wymieniony w git fsck, dopóki nie zresetujemy. Więc resetujemy ...

git reset --hard  
git fsck  

dostaniesz wiszącą kroplę ce013625030ba8dba906f756967f9e9ca394464a

git show ce01362  

przywróci zawartość pliku „cześć”

Aby znaleźć niereferencyjne zatwierdzenia, znalazłem wskazówkę sugerującą to.

gitk --all $(git log -g --pretty=format:%h)  

Mam to jako narzędzie w git gui i jest to bardzo przydatne.


+1. Jak wspomniano w stackoverflow.com/a/21350689/6309 , git fsck --lost-foundmoże pomóc.
VonC

7

Jeśli korzystasz z JetBrains IDE (cokolwiek opartego na IntelliJ), możesz odzyskać nawet niezaangażowane zmiany za pomocą funkcji „Historii lokalnej”.

Kliknij prawym przyciskiem myszy katalog najwyższego poziomu w drzewie plików, znajdź „Historię lokalną” w menu kontekstowym i wybierz „Pokaż historię”. Otworzy się widok, w którym można znaleźć ostatnie zmiany, a gdy znajdziesz wersję, do której chcesz wrócić, kliknij ją prawym przyciskiem myszy i kliknij „Przywróć”.



3

Stworzono mały skrypt, aby nieco łatwiej było znaleźć zatwierdzenie, którego szuka:

git fsck --lost-found | grep commit | cut -d ' ' -f 3 | xargs -i git show \{\} | egrep '^commit |Date:'

Tak, można go znacznie upiększyć za pomocą awk lub czegoś podobnego, ale jest to proste i właśnie tego potrzebowałem. Może uratować kogoś innego 30 sekund.


0

Mój problem jest prawie podobny. Mam nieprzypisane pliki przed wejściem git reset --hard.

Na szczęście Udało mi się pominąć wszystkie te zasoby. Po tym, jak zauważyłem, że mogę po prostu cofnąć ( ctrl-z). Just Chcę tylko dodać to do wszystkich powyższych odpowiedzi.

Uwaga. Nie można ctrl-zotworzyć nieotwartych plików.


0

To uratowało mi życie: https://medium.com/@CarrieGuss/how-to-recover-from-a-git-hard-reset-b830b5e3f60c

Zasadniczo musisz uruchomić:

for blob in $(git fsck --lost-found | awk ‘$2 == “blob” { print $3 }’); do git cat-file -p $blob > $blob.txt; done

Następnie ręcznie przechodząc przez ból, aby ponownie uporządkować pliki do właściwej struktury.

Na wynos: nigdy nie używaj, git reset --hardjeśli nie do końca w 100% rozumiesz, jak to działa, najlepiej go nie używać.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.