Pytanie Znajdź i przywróć usunięty plik w repozytorium Git


Powiedz, że jestem w repozytorium Git. Usuwam plik i zatwierdzam tę zmianę. Kontynuuję pracę i podejmuję kolejne zobowiązania. Następnie stwierdzam, że muszę przywrócić ten plik.

Wiem, że mogę pobrać plik za pomocą git checkout HEAD^ foo.bar, ale tak naprawdę nie wiem, kiedy plik został usunięty.

  1. Jaki byłby najszybszy sposób, aby znaleźć commit, który usunął daną nazwę pliku?
  2. Jaki byłby najłatwiejszy sposób przywrócenia tego pliku do kopii roboczej?

Mam nadzieję, że nie będę musiał ręcznie przeglądać moich dzienników, kasować całego projektu dla danego SHA, a następnie ręcznie skopiować ten plik do mojego oryginalnego zamówienia na projekt.


2386
2018-06-04 22:40


pochodzenie


$ git checkout deletedFile, nikt tego wyraźnie nie powiedział ?! Odpowiedz na tytuł przyszłym pracownikom Google ... - hhh
zauważ, że poprzedni komentarz odpowiada na pytanie w tytule, a nie na ciele - w tym na dowiadywanie się gdy plik został usunięty. - avdgaag
Aby znaleźć zatwierdzenie, plik został usunięty w: git log --diff-filter=D -- path/to/file - titaniumdecoy
Związane z: Jak odrzucić niezaprogramowane zmiany w git?.
@hhh git checkout deletedFile zostanie cofnięty deletedFile jeśli został usunięty, ale to usunięcie nie został jeszcze wystawiony lub popełniony. Nie o to tutaj pyta; to pytanie dotyczy przywracania pliku, którego usunięcie zostało popełnione wiele razy. - Mark Amery


Odpowiedzi:


Znajdź ostatnie zatwierdzenie, które wpłynęło na daną ścieżkę. Ponieważ plik nie znajduje się w zatwierdzeniu HEAD, to zatwierdzenie musiało go usunąć.

git rev-list -n 1 HEAD -- <file_path>

Następnie wypróbuj wersję wcześniej zatwierdzoną, używając karetki (^) symbol:

git checkout <deleting_commit>^ -- <file_path>

Lub w jednym poleceniu, jeśli $file jest plik, o którym mowa.

git checkout $(git rev-list -n 1 HEAD -- "$file")^ -- "$file"

Jeśli używasz Zsh i masz włączoną opcję EXTENDED_GLOB, symbol karetki nie zadziała. Możesz użyć ~1 zamiast.

git checkout $(git rev-list -n 1 HEAD -- "$file")~1 -- "$file"

2725
2017-07-11 07:12



Najtrudniejsze jest sprawdzenie zatwierdzenia PRZED użyciem sufiksu ^. Dzięki. - Christian Oudard
Na co jest ^ na końcu? - ranman
@Ranman: To znaczy "pierwszy rodzic". - CB Bailey
Z wiersza poleceń systemu Windows wystąpił błąd. error: pathspec <filename> did not match any file(s) known to git.. Rozwiązaniem było użycie git bash. - donturner
@zoras zsh ma własne rozszerzenie na "^", ale możesz użyć alternatywnej składni '~ 1': git checkout <deleting-commit>~1 -- <file-path>  ~ X pozwala na określenie X commitów przed określonym commitem, więc ~ 1 to commit before, ~ 2 to dwa commit before, etc - Nils Luxton


  1. Posługiwać się git log --diff-filter=D --summary aby uzyskać wszystkie zatwierdzenia, które usunęły pliki, a pliki zostały usunięte;
  2. Posługiwać się git checkout $commit~1 filename przywrócić usunięty plik.

Gdzie $commit jest wartością zatwierdzenia znalezionego w kroku 1, np. e4cf499627


724
2018-06-04 23:10



ciekawy, do czego odnosi się ~ 1? - tommy chheng
@Tommy - specyfikacja tyldy da ci n-tego wnuka o nazwie commit. Widzieć book.git-scm.com/4_git_treeishes.html po więcej szczegółów . - Robert Munteanu
jest to zdecydowanie najłatwiejsze i intuicyjne podejście. git log -- *PartOfMyFileName*. Dziękuję za $commit~1 - bgs
git checkout $commit~1 filename Składnia działa idealnie dla pojedynczych plików, a także działa dla całych katalogów. tzn .: aby przywrócić wszystkie usunięte obrazy w ./images z Sha 12345: git checkout 12345~1 images. dzięki za tę odpowiedź! - noinput
@Alexar $commit~1 oznacza, że ​​powinieneś dodać nazwę commit. Coś jak 1d0c9ef6eb4e39488490543570c31c2ff594426c gdzie $commit jest. - Eugene


Aby przywrócić wszystkie usunięte pliki w folderze, wprowadź następujące polecenie.

git ls-files -d | xargs git checkout --

302
2017-12-02 06:11



Gdzie są przesyłane pliki? Nie widzę żadnej zmiany. - William Grand
Jest to prawdopodobnie najłatwiejsza metoda. To wypaczone, jak trudne git wykonał nawet najprostsze zadanie. - jww
co znaczy -- ? - Tebe
git checkout - [plik] przywróci zmiany w pliku [plik]. Rura zastąpi [plik] nazwą usuniętych plików. - Manu
The ls-files pod-komenda jest przydatna, ale nie działa dla plików, które zostały usunięte git rm tj. wystawiony, nie mówiąc już o zaangażowaniu, o co poprosił PO. - MarkHu


Przyszedłem do tego pytania, szukając przywrócenia pliku, który właśnie usunąłem, ale jeszcze nie zatwierdziłem zmiany. Na wypadek, gdybyś znalazł się w tej sytuacji, wystarczy wykonać następujące czynności:

git checkout HEAD -- path/to/file.ext


100
2018-04-10 00:03



Dzięki. Mi to pasuje. Przywróć plik, który nie został jeszcze zatwierdzony. - Hua Zhang
To najprostsze i najlepsze. - SmallChess


Jeśli jesteś szalony, użyj git-bisect. Oto co należy zrobić:

git bisect start
git bisect bad
git bisect good <some commit where you know the file existed>

Teraz czas na automatyczny test. Polecenie powłoki '[ -e foo.bar ]' zwróci 0, jeśli foo.bar istnieje i 1 w przeciwnym razie. Polecenie "uruchom" z git-bisect użyje wyszukiwania binarnego, aby automatycznie znaleźć pierwsze zatwierdzenie, gdy test się nie powiedzie. Rozpoczyna się w połowie podanego zakresu (od dobrego do złego) i dzieli go na połowę w oparciu o wynik określonego testu.

git bisect run '[ -e foo.bar ]'

Teraz jesteś na commit, który usunął to. Stąd możesz wrócić do przyszłości i użyć git-revert cofnąć zmianę,

git bisect reset
git revert <the offending commit>

lub możesz cofnąć jeden commit i ręcznie sprawdzić uszkodzenia:

git checkout HEAD^
cp foo.bar /tmp
git bisect reset
cp /tmp/foo.bar .

82
2018-06-04 22:46



Czy możesz rozwinąć dalej git bisect run '[ -e foo.bar ]'? - avdgaag
Możesz również użyć dobra i zła ręcznie, jeśli jest to coś, czego nie można sprawdzić automatycznie. Zobacz stronę man bisect. - Josh Lee
To nie jest najłatwiejsze rozwiązanie, ale robi wrażenie. Dzięki za napisanie. - avdgaag
@avdgaag the git bisect run mówi Git, aby zautomatyzował bisection, uruchamiając polecenie po słowie "run", w którym polecenie musi powrócić 0 dla good wersja (patrz git help bisect dla szczegółów). The '[ -e foo.bar ]' jest standardowym wyrażeniem do testowania, jeśli plik foo.bar nie istnieje (implementacja jest zwykle w pliku /usr/bin/[ który zwykle jest twardy link do /usr/bin/test) i pojedyncze znaki quation są używane do umieszczenia tego wszystkiego jako pojedynczego argumentu wiersza poleceń. - Mikko Rantalainen


Mój nowy ulubiony pseudonim na podstawie bonyiii„s odpowiedź (upvoted) i moja własna odpowiedź na temat "Przekaż argument do polecenia alias Git":

git config alias.restore '!f() { git checkout $(git rev-list -n 1 HEAD -- $1)~1 -- $(git diff --name-status $(git rev-list -n 1 HEAD -- $1)~1 | grep '^D' | cut -f 2); }; f'

Zgubiłem plik, usunięty przez pomyłkę kilka razy wcześniej?
Szybki:

git restore my_deleted_file

Kryzys zażegnany.


Robert Dailey proponuje w komentarzach następujący alias:

restore-file = !git checkout $(git rev-list -n 1 HEAD -- "$1")^ -- "$1"

I jegan dodaje w komentarzach:

Aby ustawić alias z wiersza poleceń, użyłem tego polecenia:

git config --global alias.restore "\!git checkout \$(git rev-list -n 1 HEAD -- \"\$1\")^ -- \"\$1\"" 

61
2018-02-17 15:33



Przywraca to całe zatwierdzenie, a nie tylko żądany plik. - Daniel Bang
Oto mój pseudonim, działa cudownie: restore-file = !git checkout $(git rev-list -n 1 HEAD -- "$1")^ -- "$1" - void.pointer
@RobertDailey To wygląda świetnie! Uwzględniłem twój alias w odpowiedzi, aby uzyskać lepszą widoczność. - VonC
git: 'restore' nie jest poleceniem git. - resultsway
Aby ustawić alias z wiersza poleceń, użyłem tego polecenia: git config --global alias.restore "\!git checkout \$(git rev-list -n 1 HEAD -- \"\$1\")^ -- \"\$1\"" - jegan


Jeśli znasz nazwę pliku, jest to prosty sposób na podstawowe polecenia:

Wyświetl wszystkie zatwierdzenia dla tego pliku.

git log -- path/to/file

Ostatnie zatwierdzenie (najwyżej) jest tym, które usunęło plik. Musisz więc przywrócić drugie do ostatniego zatwierdzenia.

git checkout {second to last commit} -- path/to/file

42
2018-02-27 01:50



Tak. To było poprawne rozwiązanie dla mnie! Prosty i przejrzysty - Fabrizio Bertoglio
Po prostu skorzystałem z tego rozwiązania i nie było żadnego zobowiązania do usunięcia. Mimo to udało mi się przywrócić plik przy użyciu najnowszego identyfikatora zatwierdzenia. - Adam
+10 dla second to last commit wyjaśnienie! - colminator
Czy następne zatwierdzenie (poprzednie zatwierdzenie do usunięcia) nie zawiera najnowszej wersji usuniętego pliku? Drugi do ostatniego (commit przed poprzednim zobowiązaniem się do usunięcia) może być beznadziejnie przestarzały. - Suncat2000
To pierwsze rozwiązanie, które widziałem, jest na tyle proste, że nie będę musiał tu wracać, aby znaleźć go następnym razem. Może. - Eloff


Aby przywrócić usunięty i zatwierdzony plik:

git reset HEAD some/path
git checkout -- some/path

Został przetestowany na Git w wersji 1.7.5.4.


27
2017-07-04 04:39



To mi nie pasowało. Po kasie dostałem error: pathspec 'foo' did not match any file(s) known to git. Upewniłem się, że nazwa pliku jest poprawna. Wersja Git 2.7.0 - wisbucky
-1; To jest źle. Te polecenia cofną to usunięcie nie został jeszcze popełniony (pierwszy z nich anuluje usunięcie, jeśli zostanie zainscenizowany, a drugi odrzuci niezapisane zmiany w pliku), ale twierdzisz, że przywrócą zobowiązany usunięcie pliku, co po prostu nie jest prawdą i zakończy się niepowodzeniem z błędem podobnym do powyższego komentarza @ wisbucky. - Mark Amery
@MarkAmery Rzeczywiście, myślę, że to polecenie działa dobrze dla tych programistów, którzy nie zrobili jawnej inscenizacji dla popełnienia usuniętych plików z git add -A, ale tak, przywrócony plik był nadal w fazie niezatwierdzonej. - Fedir RYKHTIK


Jeśli wprowadziłeś tylko zmiany i usunąłeś plik, ale go nie zatwierdziłeś, a teraz zerwałeś ze zmianami

git checkout -- .

ale usunięte pliki nie powróciły, wystarczy wykonać następujące polecenie:

git checkout <file_path>

I presto, twój plik powrócił.


21
2017-09-02 15:30





Mam to rozwiązanie.

  1. Uzyskaj identyfikator zatwierdzenia, w którym plik został usunięty, korzystając z jednego z poniższych sposobów.

    • git log --grep=*word* 
    • git log -Sword
    • git log | grep --context=5 *word*
    • git log --stat | grep --context=5 *word* # zalecane, jeśli nie pamiętaj o wszystkim
  2. Powinieneś otrzymać coś takiego:

commit bfe68bd117e1091c96d2976c99b3bcc8310bebe7 Autor: Alexander   Orłow Data: czw 12 maja 23:44:27 2011   +0200

replaced deprecated GWT class
- gwtI18nKeySync.sh, an outdated (?, replaced by a Maven goal) I18n generation script

commit 3ea4e3af253ac6fd1691ff6bb89c964f54802302 Autor: Alexander   Orłow Data: czw 12 maja 22:10:22 2011   +0200

3. Teraz za pomocą identyfikatora zatwierdzenia bfe68bd117e1091c96d2976c99b3bcc8310bebe7 wykonaj:

git checkout bfe68bd117e1091c96d2976c99b3bcc8310bebe7^1 yourDeletedFile.java

Ponieważ id zatwierdzenia odwołuje się do zatwierdzenia, w którym plik został już usunięty, musisz odwołać się do zatwierdzenia tuż przed bfe68b, które możesz zrobić, dołączając ^1. Oznacza to: daj mi zatwierdzenie tuż przed bfe68b.


20
2018-03-01 10:48



To jest to samo podejście, co zaakceptowana odpowiedź, ale z kilkoma sposobami znalezienia usunięcia zatwierdzenia. Nadal podoba mi się podejście przyjęte w przyjętej odpowiedzi, ale są to dobre alternatywy. Dzięki! - avdgaag
Zakładam, że najpierw sprawdzenie usuniętego pliku, a następnie (bez zmiany) zatwierdzenie go nie powoduje utworzenia Kopiuj pliku. Dobrze? (Muszę to zrobić z obrazami, a kopie zwiększyłyby pojemność repozytorium) - Stonecrusher