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ć?



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


16
czy można szukać wzorów? Zapomniałem całej nazwy pliku = (być może można uzyskać dziennik wszystkich usunięć?
— 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.


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 <danpal@gmail.com>
 Date:   Tue Jul 26 23:32:20 2011 -0500

 foo

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 }

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


11
Podstawowe upvocowane rozwiązanie nie działało dla mnie, ale to zadziałało.
— Nick Heiner

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.