Jak znaleźć usunięty plik w historii zatwierdzeń projektu?

1274

Dawno, dawno temu w moim projekcie był plik, który chciałbym teraz uzyskać.

Problem polega na tym: nie mam pojęcia, kiedy go usunąłem i na której ścieżce to było.

Jak zlokalizować zatwierdzenia tego pliku, gdy on istniał?

Pedro Rolo
źródło
Podobne pytanie tutaj: stackoverflow.com/questions/7093602/…
eckes
3
Możliwy duplikat Find po usunięciu pliku w Git
Dan Dascalescu
13
Odpowiedzi tutaj są dla mnie bardziej przydatne niż odpowiedzi w duplikatach .
Felipe Alvarez
5
zgodzili się ... bez względu na duplikaty ... nie pojawili się w wyszukiwarce Google ... ten zrobił ... mam nadzieję, że przestaniemy tracić czas na ściganie duplikatów ... tylko czas i algorytm google powiedz, które pytanie jest najlepsze.
Tim Boland,

Odpowiedzi:

1598

Jeśli nie znasz dokładnej ścieżki, której możesz użyć

git log --all --full-history -- "**/thefile.*"

Jeśli znasz ścieżkę do pliku, możesz to zrobić:

git log --all --full-history -- <path-to-file>

Powinno to pokazać listę zatwierdzeń we wszystkich gałęziach, które dotknęły tego pliku. Następnie możesz znaleźć żądaną wersję pliku i wyświetlić ją za pomocą ...

git show <SHA> -- <path-to-file>

Lub przywróć go do kopii roboczej za pomocą:

git checkout <SHA>^ -- <path-to-file>

Zwróć uwagę na symbol karetki ( ^), który pobiera kasę przed zidentyfikowanym, ponieważ w momencie <SHA>zatwierdzenia plik jest usuwany, musimy spojrzeć na poprzednie zatwierdzenie, aby uzyskać zawartość usuniętego pliku

Bursztyn
źródło
2
Spróbuj użyć ścieżki względnej zamiast bezwzględnej (jeśli jeszcze tego nie zrobiłeś).
Bursztyn
63
Co jeśli nie znasz dokładnej ścieżki? Wszystko co wiesz to nazwa pliku?
priestc
17
@PedroMorteRolo nie git log -- <path>będzie miał danych wyjściowych, gdy jesteś w gałęzi, w której plik nigdy nie istniał. Powinieneś zawsze używać git log --all -- <path>, aby upewnić się, że nie przegapisz zmian, które miały miejsce w innych gałęziach. Polecenie git log -- <path>może być bardzo niebezpieczne, jeśli masz więcej niż jedną gałąź i często zapominasz o ścieżkach i gałęziach (takich jak ja), a także niebezpieczne, jeśli pracujesz z innymi programistami.
płyty grzewcze
4
@Amber, rozważ dodanie --all(dzięki Filipowi ) do swojej git logodpowiedzi, aby ludzie nie przegapili zmian i plików w innych oddziałach. Pozwoliłoby to zaoszczędzić zapomnianym ludziom, takim jak ja, wiele smutku.
płyty grzewcze
3
Jak stwierdzono w odpowiedzi poniżej, przywracaniem pliku powinno być git checkout <SHA>^ -- <path-to-file>(zwróć uwagę na symbol ^), ponieważ w momencie zatwierdzenia <SHA> plik jest usuwany, musimy spojrzeć na poprzednie zatwierdzenie, aby uzyskać zawartość usuniętego pliku
kipelovets
393

Uzyskaj listę usuniętych plików i skopiuj pełną ścieżkę usuniętego pliku

git log --diff-filter=D --summary | grep delete

Wykonaj następne polecenie, aby znaleźć identyfikator zatwierdzenia tego zatwierdzenia i skopiuj identyfikator zatwierdzenia

git log --all -- FILEPATH

Pokaż różnicę usuniętego pliku

git show COMMIT_ID -- FILE_PATH

Pamiętaj, że możesz zapisać dane wyjściowe do pliku za pomocą polecenia >like

git show COMMIT_ID -- FILE_PATH > deleted.diff
Fatih Acet
źródło
1
Chociaż znalazłem ścieżkę przy pomocy pierwszego etapu, drugi etap zgłasza ten błąd: unknown revision or path not in the working tree.
jvannistelrooy
6
Aby zobaczyć skróty zatwierdzania wraz z usunięciem, możesz to zrobićgit log --diff-filter=D --summary | grep -E 'delete|^commit\s+\S+'
Chris Middleton
1
Krok 2 nic nie zwraca. Wszelkie pomysły na to, dlaczego tak się dzieje? Moja nazwa pliku jest poprawna.
Denis Kniazhev
2
Aby znaleźć połączyć trzy w jedną funkcję, dodaj to do swojego .bashrc lub .zshrc: git-grep-latest(){ result_path=$(git log --diff-filter=D --summary | grep $1 | head -1 | awk '{print $4;}'); latest_commit=$(git log --all -- $result_path | head -1 | awk '{print $2;}'); git show $latest_commit -- $result_path; }a teraz możesz po prostu zrobić:git-grep-latest some_text
losowo
1
@TylerJones za pomocą potoków możesz karmić wszystko czymkolwiek z linuksem - google linux pipes.. spodoba ci się.
John Hunt
37

Nie można edytować zaakceptowanej odpowiedzi, dlatego dodając ją jako odpowiedź tutaj,

aby przywrócić plik w git, użyj następującego polecenia (zwróć uwagę na znak „^” zaraz po SHA)

git checkout <SHA>^ -- /path/to/file
Akshay Agarwal
źródło
Nie rozumiem, dlaczego chcesz ^. Plik jest w zatwierdzeniu z tym SHA ... dlaczego chcesz cofnąć kolejne zatwierdzenie?
Tony K.
19
Jest w zatwierdzeniu z tym sha jako „usunięty”, co oznacza, że ​​nadal nie będzie istniał. Musisz wcześniej przejść do zatwierdzenia, aby go odzyskać.
tandrewnichols,
6
@ tandrewnichols, co oznacza po prostu, że używasz niewłaściwego SHA zatwierdzenia - chcesz zatwierdzenia dla wersji pliku, którą chcesz ... która prawdopodobnie nie jest wersją, w której plik jest usuwany.
Amber
6
@Amber i żądane zatwierdzenie jest najprawdopodobniej najnowsze przed jego usunięciem, stąd ta odpowiedź.
Sam Holder
1
@AlexR: <SHA>~1powinien działać tak samo, bez konieczności owijania go cudzysłowami.
CodeManX
37

Załóżmy, że chcesz odzyskać plik o nazwie MyFile, ale nie masz pewności co do jego ścieżki (lub rozszerzenia, jeśli o to chodzi):

Prelim .: Unikaj zamieszania, przechodząc do korzenia git

Nietypowy projekt może mieć wiele katalogów o podobnych lub identycznych nazwach.

> cd <project-root>
  1. Znajdź pełną ścieżkę

    git log --diff-filter = D - podsumowanie | grep delete | grep MyFile

    delete mode 100644 full/path/to/MyFile.js

full/path/to/MyFile.js to ścieżka i plik, którego szukasz.

  1. Określ wszystkie zatwierdzenia, które wpłynęły na ten plik

    git log --oneline --follow - full / path / to / MyFile.js

    bd8374c Some helpful commit message

    ba8d20e Another prior commit message affecting that file

    cfea812 The first message for a commit in which that file appeared.

  2. Pobierz plik

Jeśli wybierzesz pierwszy wymieniony zatwierdzenie (ostatni chronologicznie, tutaj bd8374c), plik nie zostanie znaleziony, ponieważ został usunięty w tym zatwierdzeniu.

> git checkout bd8374c -- full/path/to/MyFile.js

`error: pathspec 'full/path/to/MyFile.js' did not match any file(s) known to git.`

Po prostu wybierz poprzedni (dodaj kursor) zatwierdzenie:

> git checkout bd8374c^ -- full/path/to/MyFile.js
Calaf
źródło
3
Jest to o wiele jaśniejsze niż zaakceptowana odpowiedź
Pouyan Chhodabakhsh
dla konsoli Windows (cmd), użyj find zamiast grep w kroku 2: git log --diff-filter=D --summary | find "delete" | find "MyFile"I kroku 3 , zanotuj cudzysłowy wokół skrótu:git checkout "bd8374c^" -- full/path/to/MyFile.js
user5542121
30

@Amber podał prawidłową odpowiedź! Jeszcze jeden dodatek, jeśli nie znasz dokładnej ścieżki do pliku, możesz użyć symboli wieloznacznych! To zadziałało dla mnie.

git log --all -- **/thefile.*
Petur Subev
źródło
4
@PedroMorteRolo Hmm. Nie wiem, co sądzę o skopiowaniu istniejącej odpowiedzi do najczęściej głosowanej: / Ta odpowiedź sama w sobie była przydatna; opinia mogła być wystarczająca?
Clément
1
Nie znajdzie pliku, jeśli znajduje się w katalogu głównym projektu (testowane w Cygwin).
wortwart
19

Poniżej znajduje się proste polecenie, w którym użytkownik programisty lub użytkownik git może przekazać usuniętą nazwę pliku z katalogu głównego repozytorium i uzyskać historię:

git log --diff-filter=D --summary | grep filename | awk '{print $4; exit}' | xargs git log --all -- 

Jeśli ktoś może poprawić polecenie, zrób to.

Jason
źródło
1
Wielkie dzieki! Wygląda na to, że mój plik w ogóle nie istniał, ale to osobny i znacznie bardziej
upewnij się, że uruchomisz to z katalogu głównego repozytorium, jeśli wydaje się, że brakuje
pliku
Dzięki @samaspin zaktualizowałem odpowiedź.
Jason
18

Spróbuj użyć jednej z przeglądarek, na przykład gitk, aby przeglądać historię i znaleźć ten na pół zapamiętany plik. (użyj w gitk --allrazie potrzeby dla wszystkich oddziałów)

Philip Oakley
źródło
4
Ta --allopcja ma kluczowe znaczenie zarówno dla Twojej odpowiedzi, jak i dla odpowiedzi zaakceptowanej.
płyty grzewcze
3
Przeszukiwanie historii zajmie wyjątkowo dużo czasu w przypadku większości projektów.
mikemaccana
5

Podsumowanie:

  1. Krok 1

Przeszukujesz pełną ścieżkę pliku w historii usuniętych plików git log --diff-filter=D --summary | grep filename

  1. Krok 2

Przywracasz plik z zatwierdzenia przed jego usunięciem

restore () {
  filepath="$@"
  last_commit=$(git log --all --full-history -- $filepath | grep commit | head -1 | awk '{print $2; exit}')
  echo "Restoring file from commit before $last_commit"
  git checkout $last_commit^ -- $filepath
}

restore my/file_path
srghma
źródło
0

Oto moje rozwiązanie:

git log --all --full-history --oneline -- <RELATIVE_FILE_PATH>
git checkout <COMMIT_SHA>^ -- <RELATIVE_FILE_PATH>
Antonio Petricca
źródło