Jak grep (wyszukiwanie) popełnionego kodu w historii Git

1432

Kiedyś usunąłem plik lub kod z pliku. Czy mogę grepować w treści (nie w komunikatach zatwierdzania)?

Bardzo złym rozwiązaniem jest grep log:

git log -p | grep <pattern>

Jednak to nie zwraca natychmiast skrótu zatwierdzenia. I bawił się git grepbezskutecznie.

Ortwin Gentz
źródło
2
Te posty na blogu autorstwa Junio ​​C Hamano (git opiekun) mogą być dla Ciebie interesujące: * Najlepsze narzędzie do śledzenia treści Linusa (o wyszukiwaniu git log -Skilofów i obwinianiu) * [Zabawa z "git log - grep"] [2] (wyszukiwanie wiadomości zatwierdzenia ) * [Zabawa z „git grep”] [3] [2]: gitster.livejournal.com/30195.html [3]: gitster.livejournal.com/27674.html
Jakub Narębski
odpowiedź z możliwego duplikatu faktycznie działa: stackoverflow.com/a/1340245/492
CAD bloke
Problem polega na tym, że nie daje to żadnego kontekstu dla zmiany .. tj. kto / kiedy
Sonic Soul

Odpowiedzi:

1888

Aby wyszukać treść zatwierdzenia (tj. Rzeczywiste wiersze źródła, w przeciwieństwie do komunikatów zatwierdzania itp.), Musisz:

git grep <regexp> $(git rev-list --all)

git rev-list --all | xargs git grep <expression> będzie działać, jeśli wystąpi błąd „Zbyt długa lista argumentów”.

Jeśli chcesz ograniczyć wyszukiwanie do pewnego poddrzewa (na przykład „lib / util”), musisz przekazać to do rev-listpodkomendy, a greptakże:

git grep <regexp> $(git rev-list --all -- lib/util) -- lib/util

Spowoduje to przejrzenie całego tekstu zatwierdzenia regexp.

Powodem przekazania ścieżki w obu komendach jest to, że rev-listzwróci listę poprawek, w której miały lib/utilmiejsce wszystkie zmiany , ale musisz także przejść do, grepaby tylko wyszukiwała lib/util.

Wyobraź sobie następujący scenariusz: grepmoże znaleźć to samo <regexp>w innych plikach, które są zawarte w tej samej wersji zwróconej przez rev-list(nawet jeśli nie było zmian w tym pliku w tej wersji).

Oto kilka innych przydatnych sposobów wyszukiwania źródła:

Wyszukaj w drzewie roboczym pasujące do wyrażenia regularnego wyrażenie regularne:

git grep <regexp>

Wyszukaj w drzewie roboczym wiersze tekstu pasujące do wyrażenia regularnego regexp1 lub regexp2:

git grep -e <regexp1> [--or] -e <regexp2>

Wyszukaj w drzewie roboczym wiersze tekstu pasujące do wyrażenia regularnego regexp1 i regexp2, zgłaszając tylko ścieżki plików:

git grep -l -e <regexp1> --and -e <regexp2>

Wyszukaj w drzewie roboczym pliki, których wiersze tekstu pasują do wyrażenia regularnego regexp1 i wiersze tekstu pasującego do wyrażenia regularnego regexp2:

git grep -l --all-match -e <regexp1> -e <regexp2>

Wyszukaj w drzewie roboczym zmienione linie wzoru dopasowania tekstu:

git diff --unified=0 | grep <pattern>

Wyszukaj we wszystkich wersjach wyrażenia regularne pasujące do wyrażenia regularnego:

git grep <regexp> $(git rev-list --all)

Wyszukaj we wszystkich wersjach między wersją 1 a wersją 2 wyrażenia regularne pasujące do wyrażenia regularnego:

git grep <regexp> $(git rev-list <rev1>..<rev2>)
Jeet
źródło
61
Dzięki, działa świetnie! Smutne jest jednak to, że „$ (git rev-list --all)” jest potrzebny i nie ma wygodnego przełącznika, aby określić wyszukiwanie w całej historii oddziału.
Ortwin Gentz
3
Świetny. +1. GitBook dodaje kilka szczegółów ( book.git-scm.com/4_finding_with_git_grep.html ), a Junio ​​C Hamano ilustruje niektóre z twoich punktów: gitster.livejournal.com/27674.html
VonC
18
Niestety nie mogę tego zrobić z msysgit-1.7.4. To mi mówi sh.exe": /bin/git: Bad file number. Odpowiedź VonC działa również z msysgit.
eckes
4
Jeśli podczas wywoływania historii git grep z listą błędów pojawia się błąd „nie można odczytać drzewa”, być może trzeba będzie wszystko wyczyścić. Wypróbuj git gclub sprawdź: stackoverflow.com/questions/1507463/…
Anthony Panozzo
8
Tak, niestety wydaje się, że zawiedzie to również w systemie Windows.
mlissner
551

Powinieneś użyć opcji pickaxe ( -S) z git log.

Aby wyszukać Foo:

git log -SFoo -- path_containing_change
git log -SFoo --since=2009.1.1 --until=2010.1.1 -- path_containing_change

Zobacz historię Git - znajdź utraconą linię według słowa kluczowego, aby uzyskać więcej.


Jak skomentował Jakub Narębski :

  • to wygląda na różnice, które wprowadzają lub usunąć instancję<string> . Zazwyczaj oznacza to „poprawki, w których dodałeś lub usunąłeś wiersz za pomocą„ Foo ”.

  • --pickaxe-regexopcja pozwala na użycie rozszerzony POSIX regex zamiast szukać sznurku. Przykład (z git log):git log -S"frotz\(nitfol" --pickaxe-regex


Jak skomentował Rob , w wyszukiwaniu rozróżniana jest wielkość liter - otworzył kolejne pytanie, w jaki sposób wyszukiwać wielkość liter.

VonC
źródło
3
Dzięki, nie wiedziałem o tej opcji. Wygląda na to, że jest to najlepsze rozwiązanie, jeśli interesują Cię komunikaty zatwierdzania, a rozwiązanie Jeeta jest najbardziej odpowiednie, jeśli potrzebujesz tradycyjnego zachowania grep w systemie UNIX polegającego na czystym dopasowywaniu linii.
Ortwin Gentz
@Ortwin: zgodził się (i głosowałem za wybranym rozwiązaniem). git lognieco w swoim pytaniu miał mnie mylić;)
VonC
12
Połącz go z -pflagą, aby również wyprowadzić różnicę.
Sander,
Czy jest jakiś sposób na wykluczenie wszystkich katalogów pasujących do określonych wzorców za pomocą git log -S?
BakaKuna,
3
@Anentropic potrzebujesz --branches --allopcji wyszukiwania wszystkich repozytoriów.
VonC
249

Mój ulubiony sposób na to jest z git log„s -Gopcji (dodana w wersji 1.7.4).

-G<regex>
       Look for differences whose added or removed line matches the given <regex>.

Istnieje niewielka różnica między sposobem, w jaki opcje -Gi -Sokreślają, czy zatwierdzenie pasuje:

  • -SOpcja zasadniczo zlicza liczbę razy swoje mecze wyszukiwania w pliku przed i po zatwierdzeniu. Zatwierdzenie jest wyświetlane w dzienniku, jeśli liczenia przed i po są różne. Nie będzie to na przykład pokazywać zatwierdzeń, w których przeniesiono linię pasującą do wyszukiwania.
  • Po -Gwybraniu opcji zatwierdzenie jest wyświetlane w dzienniku, jeśli wyszukiwanie pasuje do dowolnego wiersza, który został dodany, usunięty lub zmieniony.

Weź to zatwierdzenie jako przykład:

diff --git a/test b/test
index dddc242..60a8ba6 100644
--- a/test
+++ b/test
@@ -1 +1 @@
-hello hello
+hello goodbye hello

Ponieważ liczba wystąpień „cześć” w pliku jest taka sama przed zatwierdzeniem i po nim, nie będzie pasował do użycia -Shello. Ponieważ jednak nastąpiła zmiana dopasowania linii hello, zatwierdzenie zostanie pokazane za pomocą -Ghello.

Tyler Holien
źródło
2
Czy istnieje sposób na pokazanie kontekstu pasujących zmian w danych wyjściowych dziennika git?
Thilo-Alexander Ginkel
13
@ Thilo-AlexanderGinkel - Zazwyczaj po prostu dodaję -popcję wyświetlania różnic dla każdego zatwierdzenia. Następnie, gdy dziennik jest otwarty w moim pagerze, szukam tego, czego szukam. Jeśli twój pager jest lessi ty git log -Ghello -p, możesz pisać /hello, naciskać Enteri używać noraz, Naby znaleźć następne / poprzednie wystąpienia „cześć”.
Tyler Holien
Znalazłem interesujący problem z -Gi Regex: Jeśli wiersz poleceń używa UTF-8, a plik, na który patrzysz, używa kodowania ISO-Latin (8 bitów), .*zawiedzie. Na przykład mam zmianę Vierter Entwurf-> Fünfter Entwurfi chociaż 'V.*ter Entwurf'tworzy dopasowanie, 'F.*ter Entwurf'nie ma.
U. Windl
51

Jeśli chcesz przeglądać zmiany kodu (zobacz, co faktycznie zostało zmienione dla danego słowa w całej historii), przejdź do patchtrybu - znalazłem bardzo przydatną kombinację robienia:

git log -p
# Hit '/' for search mode.
# Type in the word you are searching.
# If the first search is not relevant, hit 'n' for next (like in Vim ;) )
Bartek Skwira
źródło
11
Zaakceptowane rozwiązanie nie działa dla mnie ani dla git log -S. Ten zrobił!
rodvlopes
29

git log może być bardziej skutecznym sposobem wyszukiwania tekstu we wszystkich gałęziach, szczególnie jeśli jest wiele dopasowań, a chcesz najpierw zobaczyć najnowsze (istotne) zmiany.

git log -p --all -S 'search string'
git log -p --all -G 'match regular expression'

Te listy poleceń dziennika zatwierdzają, które dodają lub usuwają podany ciąg wyszukiwania / wyrażenia regularnego, (ogólnie) najpierw nowsze. Ta -popcja powoduje wyświetlenie odpowiedniego diff w miejscu dodania lub usunięcia wzorca, dzięki czemu można go zobaczyć w kontekście.

Po znalezieniu odpowiedniego zatwierdzenia, które dodaje szukany tekst (na przykład 8beeff00d), znajdź gałęzie zawierające zatwierdzenie:

git branch -a --contains 8beeff00d
Edward Anderson
źródło
Cześć, te linie w ogóle nie działają. Moje polecenie to> git log -p --all -S publiczny ciąg DOB {get; zestaw; } = string.Empty; ' i za każdym razem, gdy próbuję go uruchomić, otrzymuję> fatal: dwuznaczny argument „string”: nieznana wersja lub ścieżka nie działa w drzewie roboczym. > Użyj „-”, aby oddzielić ścieżki od wersji, na przykład:> 'git <polecenie> [<wersja> ...] - [<plik> ...]'
użytkownik216652
@ user216652 Z jakiegoś powodu 'cytaty nie grupują szukanego ciągu jako pojedynczego argumentu. Zamiast tego 'publicjest argumentem -S, a resztę traktuje jako osobne argumenty. Nie jestem pewien, w jakim środowisku pracujesz, ale ten kontekst byłby niezbędny, aby pomóc w rozwiązaniu problemu. Sugeruję otwarcie osobnego pytania StackOverflow, jeśli jest to potrzebne, aby pomóc ci w rozwiązaniu problemu, z całym kontekstem, w jaki sposób polecenie git jest wysyłane do powłoki. Wydaje mi się, że jest wysyłany przez inne polecenie? Komentarze tutaj nie są właściwym miejscem, aby to rozgryźć.
Edward Anderson
26

Wziąłem odpowiedź Jeeta i dostosowałem ją do systemu Windows (dzięki tej odpowiedzi ):

FOR /F %x IN ('"git rev-list --all"') DO @git grep <regex> %x > out.txt

Zauważ, że dla mnie, z jakiegoś powodu, faktyczne zatwierdzenie, które usunęło to wyrażenie regularne, nie pojawiło się w danych wyjściowych polecenia, a raczej jedno zatwierdzenie przed nim.

ripper234
źródło
2
+1 - a jeśli chcesz uniknąć wciskania „q” po każdym znalezieniu, dodaj --no-pagerdo polecenia git na końcu
cgp
2
Chciałbym również zauważyć, że dodanie do pliku tekstowego ma tę dodatkową zaletę, że wyświetla pasujący tekst. (dołącz do pliku tekstowego >>results.txtdla tych, którzy nie są zaznajomieni z systemem Windows ...
cgp
1
I pomyślałem, że składnia basha jest brzydka :)
smido
23

Szukaj w dowolnej wersji, dowolnym pliku :

git rev-list --all | xargs git grep <regexp>

Szukaj tylko w niektórych podanych plikach, na przykład plikach XML:

git rev-list --all | xargs -I{} git grep <regexp> {} -- "*.xml"

Linie wynikowe powinny wyglądać następująco: 6988bec26b1503d45eb0b2e8a4364afb87dde7af: bla.xml: tekst znalezionej linii ...

Następnie możesz uzyskać więcej informacji, takich jak autor, data i diff, używając git show:

git show 6988bec26b1503d45eb0b2e8a4364afb87dde7af
Christophe Roussy
źródło
11

Dla uproszczenia sugerowałbym użycie GUI: gitk - przeglądarka repozytorium Git . Jest dość elastyczny

  1. Aby wyszukać kod:

    Wpisz opis zdjęcia tutaj
  2. Aby wyszukać pliki:

    Wpisz opis zdjęcia tutaj
  3. Oczywiście obsługuje również wyrażenia regularne:

    Wpisz opis zdjęcia tutaj

Możesz przeglądać wyniki za pomocą strzałek w górę / w dół.

watashiSHUN
źródło
6

Dla każdego, kto próbuje to zrobić w Sourcetree , nie ma do tego bezpośredniego polecenia w interfejsie użytkownika (od wersji 1.6.21.0). Można jednak użyć poleceń określonych w zaakceptowanej odpowiedzi, otwierając okno Terminala (przycisk dostępny na głównym pasku narzędzi) i kopiując / wklejając je.

Uwaga: widok Szukaj w Sourcetree może częściowo wyszukiwać tekst. Naciśnij Ctrl+, 3aby przejść do widoku wyszukiwania (lub kliknij kartę Wyszukaj dostępną u dołu). Z prawej strony ustaw Typ wyszukiwania na Zmiany pliku, a następnie wpisz ciąg, który chcesz wyszukać. Ta metoda ma następujące ograniczenia w porównaniu do powyższego polecenia:

  1. Sourcetree pokazuje tylko zatwierdzenia zawierające szukane słowo w jednym ze zmienionych plików. Znalezienie dokładnego pliku zawierającego szukany tekst jest ponownie zadaniem ręcznym.
  2. RegEx nie jest obsługiwany.
dotNET
źródło
4

Ilekroć znajdę się u ciebie, używam następującego wiersza poleceń:

git log -S "<words/phrases i am trying to find>" --all --oneline  --graph

Wyjaśnienie:

  1. git log- Muszę napisać więcej tutaj; pokazuje dzienniki w kolejności chronologicznej.
  2. -S "<words/phrases i am trying to find>" - Pokazuje wszystkie te polecenia Git, w których dowolny plik (dodany / zmodyfikowany / usunięty) zawiera słowa / frazy, które próbuję znaleźć bez symboli „<>”.
  3. --all - Aby wymusić i przeszukać wszystkie oddziały.
  4. --oneline - Kompresuje dziennik Git w jednym wierszu.
  5. --graph - Tworzy wykres chronologicznie uporządkowanych zatwierdzeń.
surajs1n
źródło
1
„Ilekroć znajdę się u ciebie, czuję potrzebę używania git!”
Sebi
1
To świetna odpowiedź!
Alf Eaton
@AlfEaton moja przyjemność!
surajs1n
2

Odpowiedź Jeeta działa w PowerShell.

git grep -n <regex> $(git rev-list --all)

Poniżej przedstawiono wszystkie pliki, w dowolnym zatwierdzeniu, które zawierają password.

# Store intermediate result
$result = git grep -n "password" $(git rev-list --all)

# Display unique file names
$result | select -unique { $_ -replace "(^.*?:)|(:.*)", "" }
Shaun Luttin
źródło
1

Czy starasz się przeszukiwać starsze wersje kodu, aby zobaczyć, gdzie coś jeszcze istnieje?

Gdybym to robił, prawdopodobnie użyłbym git bisect . Używając dwusiecznej, możesz określić znaną dobrą wersję, znaną złą wersję i prosty skrypt, który sprawdza, czy wersja jest dobra czy zła (w tym przypadku grep, aby sprawdzić, czy szukany kod jest obecny ). Uruchomienie tego spowoduje, że kod zostanie usunięty.

Rob Di Marco
źródło
2
Tak, ale „test” może być skryptem, który wyszukuje kod i zwraca „prawda”, jeśli kod istnieje, i „fałsz”, jeśli nie istnieje.
Rob Di Marco
2
Cóż, co jeśli kod był zły w wersji 10, stał się dobry w wersji 11 i stał się zły w wersji 15 ...
Paolo,
2
Zgadzam się z Paolo. Wyszukiwanie binarne jest odpowiednie tylko dla „uporządkowanych” wartości. W przypadku git bisect oznacza to, że wszystkie „dobre” wersje pojawiają się przed wszystkimi „złymi” wersjami, zaczynając od punktu odniesienia, ale założenia tego nie można przyjąć, szukając kodu przejściowego. To rozwiązanie może działać w niektórych przypadkach, ale nie jest dobrym rozwiązaniem ogólnego zastosowania.
Kent
Myślę, że jest to wysoce nieefektywne, ponieważ całe drzewo jest wielokrotnie sprawdzane pod kątem dwudzielności.
U. Windl
0

Scenariusz: dokładnie wyczyściłeś swój kod używając IDE. Problem: IDE wyczyściło więcej niż powinno, a teraz kod się nie kompiluje (brakuje zasobów itp.)

Rozwiązanie:

git grep --cached "text_to_find"

Znajdzie plik, w którym zmieniono „text_to_find”.

Możesz teraz cofnąć tę zmianę i skompilować kod.

Garytech
źródło
0
git rev-list --all | xargs -n 5 git grep EXPRESSION

to poprawka do rozwiązania Jeet , więc pokazuje wyniki podczas wyszukiwania, a nie tylko na końcu (co może zająć dużo czasu w dużym repozytorium).

laktak
źródło
-1

W moim przypadku musiałem wyszukać krótkie zatwierdzenie, a wymienione rozwiązania niestety nie działały.

Udało mi się to zrobić (wymienić token REGEX ):

for commit in $(git rev-list --all --abbrev-commit)
do
    if [[ $commit =~ __REGEX__ ]]; then 
        git --no-pager show -s --format='%h %an - %s' $commit
    fi
done
użytkownik9869932
źródło