Sprawdź, kiedy plik został usunięty w Git

1034

Mam repozytorium Git z n zatwierdzeniami.

Mam plik, którego potrzebuję i który był kiedyś w repozytorium, i nagle szukam i myślę „Och! Gdzie ten plik poszedł?”

Czy istnieje (szereg) poleceń Git, które powiedzą mi, że „plik naprawdę_needed.txt został usunięty przy zatwierdzeniu n-13”?

Innymi słowy, nie patrząc na poszczególne zatwierdzenia i wiedząc, że moje repozytorium Git ma każdą zmianę każdego pliku, czy mogę szybko znaleźć ostatnie zatwierdzenie, które MA ten plik, aby go odzyskać?

JohnMetta
źródło
2
Link udostępniony przez Pedro miał odpowiedź na moje pytanie: jak znaleźć usunięty plik, gdy nie pamiętasz ścieżki.
Gordon Bean

Odpowiedzi:

1126

git log --full-history -- [file path] pokazuje zmiany pliku, działa, nawet jeśli plik został usunięty.

Przykład:

git log --full-history  -- myfile

Jeśli chcesz zobaczyć tylko ostatnie zatwierdzenie, które usunęło plik, użyj dodatkowo -1, np. git log --full-history -1 -- [file path]

Zobacz, które zatwierdzenie usunęło plik

Vogella
źródło
16
czy można szukać wzorów? Zapomniałem całej nazwy pliku = (być może można uzyskać dziennik wszystkich usunięć?
wutzebaer
6
znalazłem to tutaj: stackoverflow.com/questions/6017987/…
wutzebaer
6
Uwaga: jeśli używasz programu PowerShell, myślniki muszą być poprzedzone znakami ucieczki: git log '-' [ścieżka pliku]. Mam nadzieję, że to samo może komuś zgrzytać zębami.
A. Wilson,
68
Byłem w stanie wyszukiwać, git log -- */<<filename>>.<<file extension>>nie znając całej ścieżki pliku.
Tom Howard
2
@MERose nawiasy kwadratowe występują jako symbol zastępczy rzeczywistej ścieżki pliku.
Emile Bergeron
229

Krótka odpowiedź:

git log --full-history -- your_file

pokaże wszystkie zatwierdzenia w historii twojego repozytorium, w tym zatwierdzenia scalenia, które się dotknęły your_file. Ostatni (górny) to ten, który usunął plik.

Niektóre wyjaśnienia:

--full-historyFlaga tutaj jest bardzo ważne. Bez tego Git wykonuje „uproszczenie historii”, gdy poprosi go o dziennik pliku. Dokumenty zawierają szczegółowe informacje na temat tego, jak to dokładnie działa, i brakuje mi odwagi i odwagi, aby spróbować to rozgryźć na podstawie kodu źródłowego, ale dokumenty git-log mają tak wiele do powiedzenia:

Tryb domyślny

Upraszcza historię do najprostszej historii wyjaśniającej ostateczny stan drzewa. Najprostsze, ponieważ przycina niektóre gałęzie boczne, jeśli wynik końcowy jest taki sam (tj. Łączenie gałęzi z tą samą zawartością)

Jest to oczywiście niepokojące, gdy usuwany jest plik, którego historii chcemy , ponieważ najprostszą historią wyjaśniającą końcowy stan usuniętego pliku jest brak historii . Czy istnieje ryzyko, że git logbez --full-historybędzie po prostu twierdzić, że plik nigdy nie został utworzony? Niestety tak. Oto demonstracja:

mark@lunchbox:~/example$ git init
Initialised empty Git repository in /home/mark/example/.git/
mark@lunchbox:~/example$ touch foo && git add foo && git commit -m "Added foo"
[master (root-commit) ddff7a7] Added foo
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 foo
mark@lunchbox:~/example$ git checkout -b newbranch
Switched to a new branch 'newbranch'
mark@lunchbox:~/example$ touch bar && git add bar && git commit -m "Added bar"
[newbranch 7f9299a] Added bar
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 bar
mark@lunchbox:~/example$ git checkout master
Switched to branch 'master'
mark@lunchbox:~/example$ git rm foo && git commit -m "Deleted foo"
rm 'foo'
[master 7740344] Deleted foo
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 foo
mark@lunchbox:~/example$ git checkout newbranch
Switched to branch 'newbranch'
mark@lunchbox:~/example$ git rm bar && git commit -m "Deleted bar"
rm 'bar'
[newbranch 873ed35] Deleted bar
 1 file changed, 0 insertions(+), 0 deletions(-)
 delete mode 100644 bar
mark@lunchbox:~/example$ git checkout master
Switched to branch 'master'
mark@lunchbox:~/example$ git merge newbranch
Already up-to-date!
Merge made by the 'recursive' strategy.
mark@lunchbox:~/example$ git log -- foo
commit 77403443a13a93073289f95a782307b1ebc21162
Author: Mark Amery 
Date:   Tue Jan 12 22:50:50 2016 +0000

    Deleted foo

commit ddff7a78068aefb7a4d19c82e718099cf57be694
Author: Mark Amery 
Date:   Tue Jan 12 22:50:19 2016 +0000

    Added foo
mark@lunchbox:~/example$ git log -- bar
mark@lunchbox:~/example$ git log --full-history -- foo
commit 2463e56a21e8ee529a59b63f2c6fcc9914a2b37c
Merge: 7740344 873ed35
Author: Mark Amery 
Date:   Tue Jan 12 22:51:36 2016 +0000

    Merge branch 'newbranch'

commit 77403443a13a93073289f95a782307b1ebc21162
Author: Mark Amery 
Date:   Tue Jan 12 22:50:50 2016 +0000

    Deleted foo

commit ddff7a78068aefb7a4d19c82e718099cf57be694
Author: Mark Amery 
Date:   Tue Jan 12 22:50:19 2016 +0000

    Added foo
mark@lunchbox:~/example$ git log --full-history -- bar
commit 873ed352c5e0f296b26d1582b3b0b2d99e40d37c
Author: Mark Amery 
Date:   Tue Jan 12 22:51:29 2016 +0000

    Deleted bar

commit 7f9299a80cc9114bf9f415e1e9a849f5d02f94ec
Author: Mark Amery 
Date:   Tue Jan 12 22:50:38 2016 +0000

    Added bar

Zauważ, jak git log -- barpowyższy zrzut terminala spowodował dosłownie brak danych wyjściowych; Git „upraszcza” historię w fikcję, w której barnigdy nie istniał. git log --full-history -- bar, z drugiej strony, daje nam zatwierdzenie, które utworzyło bari zatwierdzenie, które je usunęło.

Żeby było jasne: ten problem nie jest jedynie teoretyczny. Przejrzałem tylko dokumenty i odkryłem --full-historyflagę, ponieważ git log -- some_filezawiodło mnie w prawdziwym repozytorium, w którym próbowałem wyśledzić usunięty plik. Uproszczenie historii może czasem być pomocne, gdy próbujesz zrozumieć, w jaki sposób obecnie istniejący plik znalazł się w jego obecnym stanie, ale gdy próbujesz wyśledzić usunięcie pliku , bardziej prawdopodobne jest to, że Cię przekręcisz, ukrywając zatwierdzenie, na którym ci zależy. . Zawsze używaj --full-historyflagi dla tego przypadku użycia.

Mark Amery
źródło
4
Zauważ, że przeszukuje tylko historię istotną dla bieżącej gałęzi (nie „całą historię repo”) ... tzn. Jeśli plik nie jest jeszcze usunięty w bieżącej gałęzi, ale był w innej gałęzi, to nie znajdzie zatwierdzenia usuwania. Musisz znajdować się w dowolnym oddziale, w którym plik został już usunięty . Być może jest to oczywiste, gdy o tym myślę, ale na początku mnie to złapało.
Anentropic
1
Ta odpowiedź działa. Ale z samego git logwyniku nie jest wcale oczywiste, że ostatnie zatwierdzenie usunęło plik. Próbowałem również git log --name-status --full-history -- file_namei git log -p --stat --full-history -- file_name, ale żaden z nich wyraźnie nie wskazuje, że plik został usunięty w ostatnim zatwierdzeniu. To wygląda na błąd.
Martin_W,
@Martin_ATS mkdir somedir && cd somedir && git init && touch foo && git add foo && git commit -m "Added foo" && git checkout -b newbranch && touch bar && git add bar && git commit -m "Added bar" && git checkout master && git rm foo && git commit -m "Deleted foo" && git checkout newbranch && git rm bar && git commit -m "Deleted bar" && git checkout master && git merge newbranch && git log --name-status --full-history -- barzawiera D bari A bardla mnie w danych wyjściowych dziennika z Git 2.12.2. Czy nie widzisz tych wierszy na wyjściu? Jaką masz wersję?
Mark Amery
git version 2.15.1Tak, sekwencja poleceń zgłasza D bari A bar. Być może mój problem dotyczy konkretnie historii mojego pliku. .htaccessŚledziłem historię pliku, który został gitignore'ed i usunięty. W końcu doszedłem do tego i dodałem plik z powrotem. Po dołączeniu --name-statusdo git logpolecenia widzę dwa A .htaccesswpisy (ponieważ dodałem go ponownie w ostatnim zatwierdzeniu), ale nie D .htaccess. Wydaje się więc, że w niektórych przypadkach, nawet jeśli plik został usunięty z repozytorium, git lognie będzie zawierał wyraźnego D file_namewpisu.
Martin_W
@Martin_ATS Ciekawy. Zastanawiam się, czy być może .htaccessdodany został w X zatwierdzenia, ale nie został uwzględniony w zatwierdzeniu przez scalenie, który wprowadził X do mastera? To jedyna rzecz, o której mogę myśleć, że mogłabym argumentować, że powinna wyglądać, jakby plik został dodany i nigdy nie został usunięty, a mimo to nadal nie był obecny. Byłoby ciekawie wypróbować MCVE, a następnie dowiedzieć się, czy to błąd Git, a jeśli nie, czy można poprawić moją odpowiedź, aby załatwić twoją sprawę.
Mark Amery
84

Git log, ale musisz poprzedzić ścieżkę --

Na przykład:

dan-mac:test dani$ git log file1.txt
fatal: ambiguous argument 'file1.txt': unknown revision or path not in the working tree.

dan-mac:test dani$ git log -- file1.txt
 commit 0f7c4e1c36e0b39225d10b26f3dea40ad128b976
 Author: Daniel Palacio <[email protected]>
 Date:   Tue Jul 26 23:32:20 2011 -0500

 foo
Daniel
źródło
31

Właśnie dodałem tutaj rozwiązanie (czy istnieje git, aby wyświetlić listę wszystkich usuniętych plików w repozytorium?) W celu znalezienia zatwierdzeń usuniętych plików za pomocą wyrażenia regularnego:

git log --diff-filter=D --summary | sed -n '/^commit/h;/\/some_dir\//{G;s/\ncommit \(.*\)/ \1/gp}'

Zwraca wszystko usunięte w katalogu o nazwie some_dir(kaskadowe). Każde wyrażenie regularne tam, gdzie \/some_dir\/jest zrobi.

OSX (dzięki @triplee i @keif)

git log --diff-filter=D --summary | sed -n -e '/^commit/h' -e '\:/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }
estani
źródło
1
Miły. Trochę niedopasowań podczas bash w OS X:sed: 1: "/^commit/h;/\/some_dir\ ...": bad flag in substitute command: '}'
Brent Faust
@BrentFoust Szkoda, że ​​nie mogę tego przetestować ... spróbuj dodać spację na końcu (po nawiasach, ale przed pojedynczym cytatem), strona podręcznika online nie jest do końca jasna ...
estani
Niezła sugestia. Ale dodanie spacji przed pojedynczym cytatem nie pomogło. Nie zrobił też spacji przed nawiasem zamykającym.
Brent Faust
1
BSD / OSX sednajwyraźniej nie zawsze jest dobry w przypadku średników jako separatorów poleceń. Spróbuj zmienić je na nowe linie lub zmień nased -n -e '/^commit/h' -e '\:/some_dir/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }
tripleee
1
Przetestowałem to i git log --diff-filter=D --summary | sed -n -e '/^commit/h' -e '\:/:{' -e G -e 's/\ncommit \(.*\)/ \1/gp' -e }pracowałem dla mnie na OSX.
keif
21

Ostatni zatwierdzenie, które usunął plik, możesz znaleźć w następujący sposób:

git rev-list -n 1 HEAD -- [file_path]

Więcej informacji jest dostępnych tutaj

Akif
źródło
11
Podstawowe upvocowane rozwiązanie nie działało dla mnie, ale to zadziałało.
Nick Heiner
0

Próbować:

git log --stat | grep file
Eric Woodruff
źródło