Undo Git reset --hard with uncommitted files in the staging area

Próbuję odzyskać pracę. Głupio zrobiłem git reset --hard, ale wcześniej robiłem tylko get add ., a nie git commit. Proszę o pomoc! Oto mój log:

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

Czy w tej sytuacji można cofnąć git reset --hard?

Author: Ajedi32, 2011-09-10

7 answers

Powinieneś być w stanie odzyskać wszystkie pliki, które dodałeś do indeksu (np. jak w twojej sytuacji, z git add .), chociaż może to być trochę pracy. Aby dodać plik do indeksu, git dodaje go do object database, co oznacza, że może być odzyskany tak długo, jak pobieranie śmieci jeszcze się nie zdarzyło. Oto przykład jak to zrobić podany w odpowiedzi Jakuba Narębskiego tutaj:

Jednak wypróbowałem to na testowym repozytorium i było kilka problemów - --cached powinno być --cache i okazało się, że to nie tworzy katalogu .git/lost-found. Jednak następujące kroki zadziałały dla mnie:

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

To powinno wypisywać wszystkie obiekty w bazie danych obiektów, które nie są osiągalne przez żadne ref, w indeksie lub przez reflog. Wynik będzie wyglądał mniej więcej tak:

unreachable blob 907b308167f0880fb2a5c0e1614bb0c7620f9dc3
unreachable blob 72663d3adcf67548b9e0f0b2eeef62bce3d53e03

... i dla każdej z tych plam, można do:

git show 907b308

Aby wyświetlić zawartość pliku.


Zbyt duża wydajność?

Update in response to sehe ' s comment below:

Jeśli zauważysz, że masz wiele commitów i drzew wymienionych na wyjściu z tego polecenia, możesz chcieć usunąć z tego wyjścia wszystkie obiekty, do których odwołują się niezrealizowane commity. (Zazwyczaj i tak możesz wrócić do tych commitów poprzez reflog - interesują nas tylko obiekty, które zostały dodane do indeksu, ale nigdy nie mogą zostać znalezione przez commit.)

Najpierw zapisz wyjście polecenia, używając:

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

Teraz nazwy obiektów tych nieosiągalnych commitów można znaleźć za pomocą:

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

Więc możesz znaleźć tylko drzewa i obiekty, które zostały dodane do indeksu, ale nie zostały w żadnym momencie popełnione, z:

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

To ogromnie zmniejsza liczbę przedmiotów, które musisz wziąć pod uwagę.


Aktualizacja: Philip Oakley poniżej proponujemy inny sposób na zmniejszenie liczby obiektów do rozważenia, czyli po prostu rozważenie ostatnio zmodyfikowanych plików pod .git/objects. Można je znaleźć za pomocą:

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

(znalazłem to find inwokacja tutaj.) Koniec tej listy może wyglądać następująco:

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

W takim przypadku możesz zobaczyć te obiekty z:

git show b21700b09c0bc0fc848f67dd751a9e4ea5b4133b
git show de629830603289ef159268f443da79968360913a

(zauważ, że musisz usunąć / na końcu ścieżki, aby uzyskać nazwę obiektu.)

 157
Author: Mark Longair,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:34

Właśnie zrobiłem Git reset -- hard i straciłem jeden commit. Ale znałem hash commit, więc byłem w stanie wykonać git cherry-pick COMMIT_HASH aby go przywrócić.

Zrobiłem to w ciągu kilku minut od utraty commit, więc może to zadziałać dla niektórych z was.

 58
Author: Richard Saunders,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-27 17:42:44

Dzięki Markowi Longairowi odzyskałem swoje rzeczy!

Najpierw zapisałem wszystkie hasze do pliku:

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

Następnie umieszczam je wszystkie (usuwając rzecz' unreachable blob') na liście i umieszczam wszystkie dane w nowych plikach...musisz wybrać swoje pliki i zmienić je ponownie, czego potrzebujesz...ale potrzebowałem tylko kilku plików..mam nadzieję, że to komuś pomoże...

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
 13
Author: Boy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-08 13:52:01

@ Ajedi32 rozwiązanie w komentarzach zadziałało dla mnie dokładnie w tej sytuacji.

git reset --hard @{1}

Zauważ, że wszystkie te rozwiązania polegają na tym, że nie ma git gc, a niektóre z nich mogą je wywołać, więc zapinam zawartość Twojego .git directory przed próbowaniem czegokolwiek, abyś miał migawkę, do której możesz wrócić, jeśli nie działa dla Ciebie.

 5
Author: Duncan McGregor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-11 19:14:36

Napotkał ten sam problem, ale nie dodał zmian do indeksu. Więc wszystkie powyższe polecenia nie przyniosły mi pożądanych zmian.

Po wszystkich powyższych rozbudowanych odpowiedziach, jest to naiwna podpowiedź, ale może to uratować kogoś, kto nie pomyślał o tym pierwszy, tak jak ja.

W rozpaczy próbowałem wcisnąć CTRL-Z w edytorze (LightTable), raz w każdej otwartej karcie - to na szczęście odzyskało plik w tej karcie, do ostatniego stanu przed git reset --hard. HTH.

 3
Author: olange,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-30 10:59:49

Boże, ciągnąłem za włosy, dopóki nie natknąłem się na to pytanie i na jego odpowiedzi. Uważam, że poprawna i zwięzła odpowiedź na zadane pytanie jest dostępna tylko wtedy, gdy wyciągniesz dwa z powyższych komentarzy razem, więc tutaj wszystko jest w jednym miejscu:

  1. Jak wspomniał chilicuil, Uruchom 'git reflog', aby zidentyfikować tam commit hash, do którego chcesz wrócić

  2. Jak wspomniał akimsko, prawdopodobnie nie będziesz chciał wybierać wiśni chyba, że straciłeś tylko jeden commit, więc następnie należy uruchomić ' Git reset -- hard

Uwaga dla użytkowników egit Eclipse: nie mogłem znaleźć sposobu na wykonanie tych kroków w Eclipse z egit. Zamykanie Eclipse, uruchamianie powyższych poleceń z okna terminala, a następnie ponowne otwarcie Eclipse działało dla mnie dobrze.

 1
Author: Lolo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-03 05:50:46

Powyższe rozwiązania mogą działać, jednak istnieją prostsze sposoby na odzyskanie z to zamiast przechodzić przez git-s complex undo. Domyślam się, że najbardziej Git-resetuje się na małej liczbie plików, a jeśli już używasz Vima, to to może być najbardziej oszczędne rozwiązanie. Zastrzeżenie jest takie, że już musisz używaj ViM's persistent-undo, którego powinieneś używać tak czy inaczej, ponieważ zapewnia możliwość cofnięcia nieograniczonej liczby zmian.

Oto kroki:

  1. W vim naciśnij : i wpisz polecenie set undodir. Jeśli masz włączone persistent undo w swoim .vimrc, wyświetli się wynik podobny do undodir=~/.vim_runtime/temp_dirs/undodir.

  2. W repo użyj git log, aby dowiedzieć się ostatniej daty / godziny wykonania ostatniego commit

  3. W powłoce przejdź do undodir używając cd ~/.vim_runtime/temp_dirs/undodir.

  4. W tym katalogu użyj tego polecenia, aby znaleźć wszystkie pliki, które masz zmieniony od ostatniego commit

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

    Tutaj "2018-03-20 11:24:44" jest datą i godziną ostatniego commita. Jeśli data, w której zrobiłeś git reset --hard to "2018-03-22", a następnie użyj "2018-03-22", następnie użyj "2018-03-23". Jest to spowodowane dziwactwem znalezienia, gdzie dolna granica jest całkowita, a górna granica jest wyłączna. https://unix.stackexchange.com/a/70404/242983

  5. Następnie przejdź do każdego z plików otwórz je w Vimie i wykonaj "wcześniejsze 20m". Możesz znaleźć więcej szczegóły dotyczące "wcześniej" za pomocą "H wcześniej". Proszę. earlier 20m oznacza powrót do stanu pliku 20 minut wstecz, zakładając, że zrobiłeś git hard --reset, 20 minut wstecz. Powtórz to wszystkie pliki, które zostały wyplute z polecenia find. Jestem pewien. ktoś może napisać scenariusz, który połączy te rzeczy.

 0
Author: alpha_989,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-21 16:56:22