Jak odzyskać upuszczoną skrytkę w Git?

1736

Często używam git stashoraz git stash popdo zapisywania i przywracania zmian w moim drzewie roboczym. Wczoraj miałem pewne zmiany w moim drzewie roboczym, które ukryłem i wyskoczyłem, a następnie wprowadziłem więcej zmian w moim drzewie roboczym. Chciałbym wrócić i przejrzeć wczorajsze ukryte zmiany, ale git stash popwydaje się, że usuwają wszystkie odniesienia do powiązanego zatwierdzenia.

Wiem, że jeśli użyję,git stash to .git / refs / stash zawiera odniesienie do zatwierdzenia użytego do utworzenia skrytki. A .git / logs / refs / stash zawiera całą skrytkę. Ale tych odniesień już nie ma git stash pop. Wiem, że zatwierdzenie wciąż jest gdzieś w moim repozytorium, ale nie wiem, co to było.

Czy istnieje prosty sposób na odzyskanie wczorajszego odniesienia do skrytki?

Pamiętaj, że nie jest to dla mnie dzisiaj krytyczne, ponieważ mam codzienne kopie zapasowe i mogę wrócić do wczorajszego drzewa roboczego, aby uzyskać zmiany. Pytam, bo musi być prostszy sposób!

Greg Hewgill
źródło
74
Uwaga na przyszłość: jeśli nie chcesz tracić swoich zapasów za każdym razem git stash pop, możesz to zrobić git stash apply. Robi to samo, ale nie usuwa odwołania do zastosowanej skrytki.
Kevin
3
Próbowałem wszystkiego tutaj, nie mogłem znaleźć skrytki, która została już pęknięta. Tak się cieszę za jetbrains.com/help/idea/local-history.html
Juan Mendes
Miałem ten problem. Aby zaktualizować repo, wpadłem git stash, git pull -r upstream, git push -f origin, git stash pop, i pop powiedział „fatal: dziennik odn / ukryta jest pusta”. Tried Próbowałem wielu z tych odpowiedzi, nic nie działało. Kiedy zajrzałem do .git / refs / stash , SHA tam był. Może problem z oznaczeniem dysku sieciowego Windows do synchronizacji offline? 🤷‍♂️
Brianary

Odpowiedzi:

2783

Po poznaniu skrótu, który upuściłeś zatwierdzenie skrytki, możesz zastosować go jako skrytkę:

git stash apply $stash_hash

Możesz też utworzyć dla niego osobną gałąź

git branch recovered $stash_hash

Następnie możesz robić, co chcesz, za pomocą wszystkich zwykłych narzędzi. Kiedy skończysz, po prostu zdmuchnij gałąź.

Znajdowanie skrótu

Jeśli właśnie go otworzyłeś, a terminal jest nadal otwarty, nadalgit stash pop będziesz mieć wydrukowaną wartość skrótu na ekranie (dzięki, Dolda).

W przeciwnym razie można go znaleźć, używając tego dla systemu Linux, Unix lub Git Bash dla systemu Windows:

git fsck --no-reflog | awk '/dangling commit/ {print $3}'

... lub używając Powershell dla Windows:

git fsck --no-reflog | select-string 'dangling commit' | foreach { $bits = $_ -split ' '; echo $bits[2];}

Spowoduje to wyświetlenie wszystkich zatwierdzeń na końcach wykresu zatwierdzeń, do których nie ma już odniesienia z żadnego odgałęzienia lub tagu - każde utracone zatwierdzenie, w tym każde utworzone przez Ciebie ukryte zatwierdzenie, będzie znajdować się gdzieś na tym wykresie.

Najłatwiejszym sposobem na znalezienie zatwierdzonego skrytki jest prawdopodobnie przekazanie tej listy do gitk:

gitk --all $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' )

... lub zobacz odpowiedź z emragins, jeśli używasz Powershell dla Windows.

Spowoduje to uruchomienie przeglądarki repozytorium pokazującej każde zatwierdzenie w repozytorium , niezależnie od tego, czy jest osiągalne, czy nie.

Możesz gitktam zamienić coś takiego, git log --graph --oneline --decoratejeśli wolisz ładny wykres na konsoli niż osobną aplikację GUI.

Aby wykryć zatwierdzenia ukryć, poszukaj komunikatów zatwierdzania tego formularza:

        WIP w somebranch : commithash Jakaś stara wiadomość zatwierdzenia

Uwaga : Komunikat zatwierdzenia będzie w tej formie (zaczynając od „WIP włączony”) tylko wtedy, gdy nie dostarczysz wiadomości w tym momencie git stash.

Arystoteles Pagaltzis
źródło
49
Jaydel wyjął te słowa z moich ust. Ten post zapisał moją pracę :) Chciałbym tylko dodać - zapamiętanie daty, w której pracowałeś nad tym, co straciłeś, ułatwia przeglądanie gitk w poszukiwaniu tego, czego szukasz.
Sridhar Sarnobat,
4
@Codey: Ponieważ PowerShell. Nie wiem, czy MsysGit dostarcza plik binarny AWK. Googling mówi mi, że coś takiego %{ $_.Split(' ')[2]; }powinno odpowiadać poleceniu PowerShell {print $3}w tym awkpoleceniu, ale nie mam systemu Windows, który by to przetestował, a nadal potrzebujesz odpowiednika dla tej /dangling commit/części. W każdym razie po prostu uruchom git fsck --no-reflogi spójrz na wynik. Chcesz skróty z linii „wiszące zatwierdzenie <commitID>”.
Arystoteles Pagaltzis
7
Warto wspomnieć, że komunikat zatwierdzenia będzie miał ciąg „WIP” tylko wtedy, gdy nie dostarczysz własnego komunikatu podczas ukrywania (tj. Wykonywania git stash save "<message>").
Samir Aguiar
12
Jeśli wiesz, kiedy nastąpiło upuszczenie, możesz użyć tego jednowierszowego, aby uzyskać listę wiszących zatwierdzeń przez zwiększenie czasu: git fsck --no-reflog | awk '/dangling commit/ {print $3}' | xargs -L 1 git --no-pager show -s --format="%ci %H" | sortprawdopodobnie ostatni wpis jest tym, którego chcesz stash apply.
ris8_allo_zen0
3
git stash apply {ref}przywrócił upuszczoną skrytkę! gitjest tak świetne, że powinno być nielegalne!
Tom Russell
707

Jeśli nie zamknąłeś terminala, po prostu spójrz na dane wyjściowe z, git stash popa będziesz miał identyfikator obiektu upuszczonej skrytki. Zwykle wygląda to tak:

$ git stash pop
[...]
Dropped refs/stash@{0} (2ca03e22256be97f9e40f08e6d6773c7d41dbfd1)

(Zauważ, że git stash droptworzy również tę samą linię.)

Aby odzyskać ten zapas, po prostu biegnij git branch tmp 2cae03e, a dostaniesz go jako gałąź. Aby przekonwertować to na ukryty, uruchom:

git stash apply tmp
git stash

Posiadanie go jako gałęzi pozwala również swobodnie nim manipulować; na przykład, aby wybrać go lub połączyć.

Dolda2000
źródło
54
Można również zrobić git stash apply commitidwtedy git stash, aby otrzymać nowy zapas.
Matthew Flaschen
32
Zauważ, że jeśli git automatycznie scala skrytkę i ma konflikty, to nie pokaże hasza.
James
31
@James: Z drugiej strony, jeśli te konflikty są wynikiem działania git stash pop, to również nie zrzuci skrytki, więc zwykle nie stanowi to problemu.
Dolda2000,
2
Nie było SHA w moim wyjściu git stash pop. :(
Wyrzuć konto
2
@Honey: Właśnie o to chodzi git stash pop. Jeśli chcesz zastosować skrytkę bez upuszczania, użyj git stash applyzamiast tego. Dodatkowo, jeśli chcesz zastosować zmianę do kilku gałęzi, możesz również zamiast tego wybrać zatwierdzenie.
Dolda2000
271

Chciałem tylko wspomnieć o tym dodatku do zaakceptowanego rozwiązania. Nie było to dla mnie od razu oczywiste za pierwszym razem, gdy wypróbowałem tę metodę (być może powinno być), ale aby zastosować ukrytą wartość z wartości skrótu, po prostu użyj polecenia „zastosuj ukrytą git”:

$ git stash apply ad38abbf76e26c803b27a6079348192d32f52219

Kiedy byłem nowy w Git, nie było to dla mnie jasne i próbowałem różnych kombinacji „Git Show”, „Git Apply”, „Patch” itp.

Przebrnąć
źródło
3
Pamiętaj, że dotyczy to (duh!) Skrytki do bieżącego drzewa roboczego. Jeśli drzewo jest brudne, możesz najpierw użyć tymczasowej gałęzi lub skrytki, zastosować skrytkę z SHA-1, skasować ponownie, a następnie pop od drugiej do ostatniej skrytki (zwanej skrytką @ {1}).
musiKk
111

Aby uzyskać listę skrytek, które nadal znajdują się w twoim repozytorium, ale nie są już dostępne:

git fsck --unreachable | grep commit | cut -d" " -f3 | xargs git log --merges --no-walk --grep=WIP

Jeśli nadałeś tytuł skrytce, zamień „WIP” -grep=WIPna końcu polecenia na część wiadomości, np -grep=Tesselation.

Polecenie wyświetla grep dla „WIP”, ponieważ domyślna wiadomość zatwierdzenia dla skrytki ma postać WIP on mybranch: [previous-commit-hash] Message of the previous commit.

Senthil A Kumar
źródło
1
echo 'git fsck --unreachable | grep commit | cut -d "" -f3 | xargs git log --merges --no-walk --grep = WIP '> / usr / local / bin / git-stashlog; chmod a + rx / usr / local / bin / git-stashlog # git stashlog
Erik Martino
Możesz też dodać to do swojego .gitconfig jako alias (poprzedź polecenie a !).
asmeurer
Uratowałem mój boczek - no nie bardzo, ale zaoszczędziłem na ponownym kodowaniu dni pracy - doceniam - biorąc pod uwagę, że dopiero co spadłem, właśnie wybrałem górną SHA z wyników twojego polecenia - wtedy ... git skrytka zastosuj SHA ... jak wspomniano w innych odpowiedziach - wiele thx
danday74
75

Właśnie skonstruowałem polecenie, które pomogło mi znaleźć moje zgubione zatwierdzenie skrytki:

for ref in `find .git/objects | sed -e 's#.git/objects/##' | grep / | tr -d /`; do if [ `git cat-file -t $ref` = "commit" ]; then git show --summary $ref; fi; done | less

Spowoduje to wyświetlenie listy wszystkich obiektów w drzewie .git / objects, zlokalizowanie obiektów typu commit, a następnie przedstawienie podsumowania każdego z nich. Od tego momentu wystarczyło przejrzeć commits, aby znaleźć odpowiedni „WIP w pracy: 6a9bb2” („work” to moja gałąź, 619bb2 to ostatnie zatwierdzenie).

Zauważam, że jeśli użyję „git stash Apply” zamiast „git stash pop”, nie miałbym tego problemu, a jeśli użyję „git stash save message ”, łatwiej byłoby znaleźć zatwierdzenie.

Aktualizacja: Z pomysłem Nathana staje się on krótszy:

for ref in `git fsck --unreachable | grep commit | cut -d' ' -f3`; do git show --summary $ref; done | less
Greg Hewgill
źródło
41

git fsck --unreachable | grep commitpowinien pokazywać sha1, chociaż lista, którą zwraca, może być dość duża. git show <sha1>pokaże, czy chcesz zatwierdzić.

git cherry-pick -m 1 <sha1> połączy zatwierdzenie z bieżącą gałęzią.

Nathan Jones
źródło
37

Odpowiednik Windows PowerShell przy użyciu gitk:

gitk --all $(git fsck --no-reflog | Select-String "(dangling commit )(.*)" | %{ $_.Line.Split(' ')[2] })

Prawdopodobnie istnieje bardziej skuteczny sposób, aby to zrobić w jednej rurze, ale to działa.

emragins
źródło
1
Jestem bardzo wdzięczny za odpowiedź
Виталий Шебаниц
32

Jeśli chcesz przywrócić utraconą skrytkę, najpierw musisz znaleźć skrót utraconej skrytki.

Jak zasugerował Arystoteles Pagaltzis, git fsckpowinien ci pomóc.

Osobiście używam swojego log-allaliasu, który pokazuje mi każde zatwierdzenie (odzyskiwalne zatwierdzenia), aby mieć lepszy wgląd w sytuację:

git log --graph --decorate --pretty=oneline --abbrev-commit --all $(git fsck --no-reflogs | grep commit | cut -d' ' -f3)

Możesz przeprowadzić jeszcze szybsze wyszukiwanie, jeśli szukasz tylko wiadomości „WIP on”.

Gdy poznasz swój sha1, po prostu zmienisz swój dziennik skrytki, aby dodać starą skrytkę:

git update-ref refs/stash ed6721d

Prawdopodobnie wolisz mieć powiązaną wiadomość, więc -m

git update-ref -m "$(git log -1 --pretty=format:'%s' ed6721d)" refs/stash ed6721d

Będziesz nawet chciał użyć tego jako aliasu:

restash = !git update-ref -m $(git log -1 --pretty=format:'%s' $1) refs/stash $1
Colin Hebert
źródło
2
Jednak -d\\ powinno być -d\ (lub nawet jaśniej -d' ')
joeytwiddle
Wystąpił błąd: „krytyczny: dwuznaczny argument„ wiszący ”: nieznana wersja lub ścieżka nie działa w drzewie roboczym.”
Daniel Ryan,
musisz także git update-ref -m "$(git log -1 --pretty=format:'%s' ed6721d)" refs/stash ed6721
zawrzeć podkomendę
18

Podobało mi się podejście Arystotelesa, ale nie podobało mi się używanie GITK ... ponieważ przyzwyczaiłem się do używania GIT z wiersza poleceń.

Zamiast tego wziąłem wiszące zatwierdzenia i wyprowadziłem kod do pliku DIFF w celu przejrzenia w moim edytorze kodu.

git show $( git fsck --no-reflog | awk '/dangling commit/ {print $3}' ) > ~/stash_recovery.diff

Teraz możesz załadować wynikowy plik diff / txt (znajdujący się w folderze domowym) do edytora txt i zobaczyć rzeczywisty kod i wynikowy SHA.

Więc po prostu użyj

git stash apply ad38abbf76e26c803b27a6079348192d32f52219
Shaheen Ghiassy
źródło
17

Możesz wyświetlić listę wszystkich nieosiągalnych zatwierdzeń, pisząc to polecenie w terminalu -

git fsck --unreachable

Sprawdź nieosiągalny skrót zatwierdzenia -

git show hash

Wreszcie zastosuj, jeśli znajdziesz schowany przedmiot -

git stash apply hash
Vivek Kumar
źródło
15

Dlaczego ludzie zadają to pytanie? Ponieważ jeszcze nie wiedzą o logu ani go nie rozumieją.

Większość odpowiedzi na to pytanie daje długie polecenia z opcjami, których prawie nikt nie pamięta. Tak więc ludzie wchodzą w to pytanie i kopiują, wklejając wszystko, co ich zdaniem potrzebują, i zapominają o tym niemal natychmiast.

Radziłbym każdemu, kto ma to pytanie, aby po prostu sprawdził rejestr (git reflog), nie więcej. Gdy zobaczysz tę listę wszystkich zatwierdzeń, istnieje sto sposobów, aby dowiedzieć się, czego szukasz, i wybrać ją z wiśni lub utworzyć z niej gałąź. W trakcie tego procesu dowiesz się o ponownym logowaniu i przydatnych opcjach różnych podstawowych poleceń git.

RobbyD
źródło
1
Cześć Robby. Jest to istotne, jeśli pracujesz, śledzisz się na boku i musisz wrócić tam, gdzie przerwałeś kilka tygodni temu, aby dowiedzieć się, że nie możesz znaleźć swojej ukrytej pracy - prawdopodobnie zgubiła się gdzieś w innych rzeczach, które robiliśmy. reflog jest świetny, jeśli jest to najnowsza historia, ale nie na długo.
emragins
1
Hej emragins, zgadzam się, ale to był dokładnie przypadek użycia OP. Nie wiem na pewno, jak zachowałyby się inne polecenia tutaj, ale moim głównym celem byłoby to, że przestałyby działać, gdy wyczyszczone zostanie odwołanie do jego ukrytego zatwierdzenia.
RobbyD,
1
Hmm ... powyższy scenariusz doprowadził mnie do tego pytania, i wiem, że minęło co najmniej kilka tygodni, jeśli nawet nie bliżej niż miesiąc między tym, kiedy (nieświadomie) straciłem zapas i kiedy udało mi się go odzyskać.
emragins 21.07.17
15

W OSX z git v2.6.4 po prostu przypadkowo uruchamiam upuszczanie git, a następnie znalazłem go, przechodząc przez poniższe kroki

Jeśli znasz nazwę skrytki, użyj:

$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show | grep -B 6 -A 2 <name of the stash>

w przeciwnym razie znajdziesz identyfikator wyniku, ręcznie:

$ git fsck --unreachable | grep commit | cut -c 20- | xargs git show

Następnie, gdy znajdziesz identyfikator zatwierdzenia, po prostu naciśnij przycisk git stash Apply {commit-id}

Mam nadzieję, że to pomoże komuś szybko

Czy Tecim
źródło
12

Chcę dodać do zaakceptowanego rozwiązania kolejną dobrą drogę do przejścia przez wszystkie zmiany, gdy albo nie masz dostępnego gitk, albo nie ma X dla danych wyjściowych.

git fsck --no-reflog | awk '/dangling commit/ {print $3}' > tmp_commits

for h in `cat tmp_commits`; do git show $h | less; done

Następnie otrzymujesz wszystkie różnice dla tych skrótów wyświetlanych jeden po drugim. Naciśnij „q”, aby przejść do następnego porównania.

Phil
źródło
12

Nie mogłem uzyskać żadnej odpowiedzi do pracy w systemie Windows w prostym oknie poleceń (w moim przypadku Windows 7). awk, grepI Select-stringnie zostały uznane za poleceń. Więc spróbowałem innego podejścia:

  • pierwszy bieg: git fsck --unreachable | findstr "commit"
  • skopiuj dane wyjściowe do notatnika
  • znajdź zamień „nieosiągalny zatwierdzenie” na start cmd /k git show

będzie wyglądać mniej więcej tak:

start cmd /k git show 8506d235f935b92df65d58e7d75e9441220537a4 start cmd /k git show 44078733e1b36962571019126243782421fcd8ae start cmd /k git show ec09069ec893db4ec1901f94eefc8dc606b1dbf1 start cmd /k git show d00aab9198e8b81d052d90720165e48b287c302e

  • zapisz jako plik .bat i uruchom go
  • skrypt otworzy kilka okien poleceń, pokazując każde zatwierdzenie
  • jeśli znalazłeś ten, którego szukasz, uruchom: git stash apply (your hash)

może nie jest najlepszym rozwiązaniem, ale działało dla mnie

Koen
źródło
Możesz używać git bash nawet w systemie Windows. W git bash masz wszystkie narzędzia wiersza poleceń (unixoid), których potrzebujesz.
Adrian W
10

Akceptowana odpowiedź Arystotelesa pokaże wszystkie dostępne zatwierdzenia, w tym zatwierdzenia inne niż ukryte. Aby odfiltrować hałas:

git fsck --no-reflog | \
awk '/dangling commit/ {print $3}' | \
xargs git log --no-walk --format="%H" \
  --grep="WIP on" --min-parents=3 --max-parents=3

Dotyczy to tylko zatwierdzeń, które mają dokładnie 3 zatwierdzenia nadrzędne (które będą miały ukrytą zawartość) i których komunikat zawiera „WIP włączone”.

Pamiętaj, że jeśli zapisałeś swoją skrytkę z wiadomością (np git stash save "My newly created stash" ), Zastąpi to domyślną wiadomość „WIP na ...”.

Możesz wyświetlić więcej informacji o każdym zatwierdzeniu, np. Wyświetlić komunikat zatwierdzenia lub przekazać go do git stash show:

git fsck --no-reflog | \
awk '/dangling commit/ {print $3}' | \
xargs git log --no-walk --format="%H" \
  --grep="WIP on" --min-parents=3 --max-parents=3 | \
xargs -n1 -I '{}' bash -c "\
  git log -1 --format=medium --color=always '{}'; echo; \
  git stash show --color=always '{}'; echo; echo" | \
less -R
Brad Feehan
źródło
6

Moim ulubionym jest ten jednowarstwowy:

git log --oneline  $( git fsck --no-reflogs | awk '/dangling commit/ {print $3}' )

Jest to zasadniczo ten sam pomysł, co ta odpowiedź, ale znacznie krótszy. Oczywiście nadal możesz dodać--graph aby uzyskać wyświetlanie podobne do drzewa.

Po znalezieniu zatwierdzenia na liście zastosuj za pomocą

git stash apply THE_COMMIT_HASH_FOUND

Dla mnie użycie --no-reflogsujawniło zgubiony wpis skrytki, ale--unreachable (jak stwierdzono w wielu innych odpowiedziach) nie.

Uruchom go na git bash, gdy jesteś w systemie Windows.

Kredyty: Szczegóły powyższych poleceń pochodzą z https://gist.github.com/joseluisq/7f0f1402f05c45bac10814a9e38f81bf

Adrian W.
źródło
5

Odzyskano go, wykonując następujące czynności:

  1. Zidentyfikuj usunięty kod skrótu skrytki:

    gitk --all $ (git fsck --no-reflog | awk '/ dangling commit / {print $ 3}')

  2. Cherry Pick the Stash:

    git cherry-pick -m 1 $ stash_hash_code

  3. Rozwiąż konflikty, jeśli występują, używając:

    git scaletool

Dodatkowo możesz mieć problemy z komunikatem zatwierdzenia, jeśli używasz gerrit. Zapisz swoje zmiany przed następnymi alternatywami:

  1. Użyj twardego resetu do poprzedniego zatwierdzenia, a następnie ponownie wprowadź tę zmianę.
  2. Możesz także ukryć zmianę, uzupełnić bazę i ponownie uruchomić.
Abhijeet
źródło
@ miva2 Twoja edycja usunęła link do najbardziej poprawnej odpowiedzi w tym pytaniu. Dodanie linku z powrotem w komentarzu stackoverflow.com/questions/89332/…
Abhijeet
4

Poszedłem tu poszukać sposobu odzyskania skrytki, niezależnie od tego, co sprawdziłem. W szczególności schowałam coś, a potem sprawdziłam starszą wersję, a potem wyskoczyłam, ale skrytka była w tym momencie niedostępna, więc skrytka zniknęła; Nie mogłem po prostu git stashodłożyć go z powrotem na stos. To działało dla mnie:

$ git checkout somethingOld
$ git stash pop
...
nothing added to commit but untracked files present (use "git add" to track)
Dropped refs/stash@{0} (27f6bd8ba3c4a34f134e12fe69bf69c192f71179)
$ git checkout 27f6bd8ba3c
$ git reset HEAD^    # Make the working tree differ from the parent.
$ git stash # Put the stash back in the stack.
Saved working directory and index state WIP on (no branch): c2be516 Some message.
HEAD is now at c2be516 Some message.
$ git checkout somethingOld # Now we are back where we were.

Z perspektywy czasu powinienem był git stash applynie używać git stash pop. Robiłem bisecti miałem małą łatkę, którą chciałem zastosować na każdym bisectkroku. Teraz robię to:

$ git reset --hard; git bisect good; git stash apply
$ # Run tests
$ git reset --hard; git bisect bad; git stash apply
etc.
Ben
źródło
czy to odpowiedź, czy kontynuacja pytania?
Alex Brown
Trochę obu. Znalazłem tę stronę, ponieważ zgubiłem skrytkę i próbowałem ją odzyskać. Dla mnie przypadkiem użycia jest bisectum, w którym chcę zastosować zmianę przed testowaniem na każdym etapie. Nauczyłem się na własnej skórze, że nie można po prostu strzelać, testować, ukrywać, dzielić, ponieważ może to pozostawić inne zatwierdzenie na skrytce stash apply.
Ben