Jak „obwinić” usuniętą linię?

506

git blamejest świetny dla zmodyfikowanych i dodanych linii, ale jak mogę znaleźć, kiedy linia, która istniała w określonym poprzednim zatwierdzeniu została ostatecznie usunięta. Myślębisect , ale liczyłem na coś wygodniejszego.

(Zanim zapytasz: w tym przypadku właśnie zrobiłem a git log -pi przeszukałem linię kodu i (a) jakiś idiota właśnie usunął kluczową linię z poprzedniego zatwierdzenia i (b) byłem tym idiotą).

Malvolio
źródło
4
Jest to nawiązanie z odpowiedzią wyjaśnienie, że git log -S<string> /path/to/filechce -club -ccjak dobrze, aby pokazać ich usunięcie podczas scalenia (konflikty)
SPI
3
Powinno być -ci --cc. @Steen: Poprawnie, dziękuję za zwrócenie uwagi! Głupi nadzór. Chciałbym móc edytować komentarz. Dodanie nowego, potem usunięcie mojego, a następnie usunięcie twojego, jest chyba zbyt kłopotliwe :)
cfi
4
Życzę git blamemiałby opcję Pokaż usunięte wiersze (z może przekreślenia tekstu lub czerwonego) z przeglądu, w którym zostały one usunięte.
Craig McQueen
Czy trudno byłoby to napisać? Nie wiem wiele o wewnętrznych elementach Git.
Malvolio,

Odpowiedzi:

636

Jeśli znasz treść wiersza, jest to idealny przypadek użycia dla:

git log -S <string> path/to/file

który pokazuje zatwierdzenia, które wprowadzają lub usuwają wystąpienie tego ciągu. Jest też to, -G<regex>co robi to samo z wyrażeniami regularnymi! Zobacz man git-logi poszukiwanie -Gi -Sopcji, lub kilof (przyjazna nazwa dla tych funkcji), aby uzyskać więcej informacji.

The -SOpcja jest faktycznie wymienione w nagłówku git-blamemanpage też, w części opisu, w którym podaje przykład używając git log -S....

Cascabel
źródło
Genialnie ... właśnie tego potrzebowałem w tej pracy przy portowaniu, nad którą pracuję +1
jkp
37
Po korzystaniu z Git przez ponad 1 rok nadal mnie zadziwia, widząc, że Git zawsze ma gdzieś polecenie / opcję, aby rozwiązać prawie każdy scenariusz użycia, jaki mam. Dzięki za udostępnienie tego, właśnie tego teraz potrzebuję!
Pascal Bourque,
22
Ta metoda działała dla mnie wcześniej, ale właśnie teraz widziałem przypadek, w którym nie znalazłem zatwierdzenia, w którym wiersz został usunięty. Okazało się, że wiersz został usunięty w zatwierdzeniu scalania - czy to wyjaśniałoby błąd? ( git blame --reverseMetoda jednak go znalazła.)
antinome
9
@antinome Aby wyświetlić zatwierdzenia ze scalenia, użyj -copcji dodatkowo.
Junzen
2
Zrobiłem ctrl + f na "-s" na stronie podręcznika i nic nie znalazłem. Gdzie na stronie to widzisz? Używam git 1.8.5.2
tymczasowego
134

Myślę, że tak naprawdę chcesz

git blame --reverse START..END filename

Z strony podręcznika :

Przejdź historię do przodu zamiast do tyłu. Zamiast pokazywania wersji, w której pojawiła się linia, pokazuje ostatnią wersję, w której linia istniała. Wymaga to szeregu zmian, takich jak START..END, gdzie ścieżka do winy istnieje w START.

Z git blame reverse możesz znaleźć ostatnie zatwierdzenie, w którym pojawiła się linia. Nadal musisz uzyskać zatwierdzenie, które nastąpi po.

Możesz użyć następującego polecenia, aby wyświetlić odwrócony dziennik git. Pierwsze pokazane zatwierdzenie będzie ostatnim wyświetleniem tego wiersza, a następne zatwierdzenie nastąpi, gdy zostanie zmienione lub usunięte.

git log --reverse --ancestry-path COMMIT^..master
Chronial
źródło
14
Jeśli istnieje wiele połączeń z gałęzi, w której linia została dodana do gałęzi, w której brakuje linii (lub w innym przypadku, w którym istnieje wiele ścieżek w linii od START do KOŃCA), git blame --reversepokaże korektę przed scaleniem, która była chronologicznie wreszcie, nie rewizja przed pierwszym scaleniem, w którym podjęto decyzję o nieprzyjęciu linii. Czy jest jakiś sposób, aby znaleźć najwcześniejszą wersję, w której linia przestała istnieć, zamiast najnowszej?
rakslice
2
@rakslice, do tego możesz użyć winy --reverse --first-parent, jest to nieco lepsze.
max630 17.07.17
17

Aby wypełnić odpowiedź Cascabel :

git log --full-history -S <string> path/to/file

Miałem ten sam problem, jak tutaj wspomniany, ale okazało się, że brakuje linii, ponieważ zatwierdzenie scalania z gałęzi zostało cofnięte, a następnie scalone z powrotem, skutecznie usuwając daną linię. W --full-historyzapobiega flag omijając te rewizje.

estani
źródło
9

git winę --reverse może dostać się blisko do miejsca, gdzie linia zostanie usunięty. Ale tak naprawdę nie jest wskazuje wersji, w której wiersz jest usuwany. Wskazuje na ostatnią wersję, w której linia była obecna . Następnie, jeśli następująca wersja jest zwykłym zatwierdzeniem, masz szczęście i masz usuniętą wersję. OTOH, jeśli następująca wersja jest zatwierdzeniem scalania , sprawy mogą stać się trochę dzikie.

W ramach wysiłku stworzenia difflame rozwiązałem ten problem, więc jeśli masz już zainstalowanego Pythona na swoim urządzeniu i chcesz spróbować, nie czekaj dłużej i daj mi znać, jak to działa.

https://github.com/eantoranz/difflame

eftshift0
źródło
0

W przypadku zmian ukrytych w zatwierdzeniach scalania

Polecenia scalania automatycznie ukrywają swoje zmiany w danych wyjściowych dziennika Git. Zarówno kilof, jak i odwrotna wina nie znalazły zmiany. Więc wiersz, który chciałem został dodany, a później usunięty, i chciałem znaleźć scalenie, które go usunęło. git log -p -- path/fileHistoria plików pokazuje tylko, że jest dodawana. Oto najlepszy sposób na znalezienie tego:

git log -p -U9999 -- path/file

Wyszukaj zmianę, a następnie wyszukaj wstecz „^ commit” - pierwsze „^ commit” to zatwierdzenie, w którym plik miał ostatnią linię. Drugie „zatwierdzenie” następuje po jego zniknięciu. Drugim zatwierdzeniem może być ten, który go usunął. The-U9999 on wyświetlać całą zawartość pliku (po każdej zmianie pliku), przy założeniu, że wszystkie pliki mają maksymalnie 9999 wierszy.

Znajduje wszelkie powiązane scalenia za pomocą brutalnej siły (różni każde możliwe scalenie zatwierdzenia z pierwszym rodzicem, działa przeciwko tonom commits)

git log --merges --pretty=format:"git diff %h^...%h | grep target_text" HEAD ^$(git merge-base A B) | sh -v 2>&1 | less

(Próbowałem bardziej ograniczyć filtr wersji, ale natknąłem się na problemy i nie polecam tego. Poszukiwane zmiany dodawania / usuwania dotyczyły różnych gałęzi, które zostały scalone w różnym czasie i A ... B nie obejmował kiedy zmiany faktycznie zostały scalone z linią główną).

Pokaż drzewo Git z tymi dwoma zatwierdzeniami (i usunięto wiele złożonych historii Git):

git log --graph --oneline A B ^$(git merge-base A B) (A jest pierwszym zatwierdzeniem powyżej, B jest drugim zatwierdzeniem powyżej)

Pokaż historię A i historię B minus historię zarówno A, jak i B.

Alternatywna wersja (wydaje się, że pokazuje ścieżkę bardziej liniowo niż zwykłe drzewo historii Git - jednak wolę zwykłe drzewo historii Git):

git log --graph --oneline A...B

Trzy, a nie dwie kropki - trzy kropki oznaczają „r1 r2 - nie $ (git merge-base - all r1 r2). Jest to zestaw zatwierdzeń, które są osiągalne z jednego z r1 (lewa strona) lub r2 (prawa strona) z boku), ale nie z obu ”. - źródło: „man gitrevisions”

Curtis Yallop
źródło