So durchsuchen Sie festgeschriebenen Code im Git-Verlauf

1434

Ich habe irgendwann in der Vergangenheit eine Datei oder einen Code in einer Datei gelöscht. Kann ich den Inhalt (nicht die Commit-Nachrichten) überprüfen?

Eine sehr schlechte Lösung besteht darin, das Protokoll zu durchsuchen:

git log -p | grep <pattern>

Dies gibt den Commit-Hash jedoch nicht sofort zurück. Ich spielte git grepvergeblich herum .

Ortwin Gentz
quelle
2
Diese Blog-Beiträge von Junio ​​C Hamano (Git-Betreuer) könnten für Sie interessant sein: * Linus 'ultimatives Tool zur Inhaltsverfolgung (über Spitzhackensuche, dh git log -Sund Schuld) * [Spaß mit "git log --grep"] [2] (Suche nach Commit-Nachrichten) ) * [Spaß mit "git grep"] [3] [2]: gitster.livejournal.com/30195.html [3]: gitster.livejournal.com/27674.html
Jakub Narębski
Antwort von möglichen Duplikaten funktioniert tatsächlich: stackoverflow.com/a/1340245/492
CAD-
Problem dabei ist, dass es keinen Kontext für die Änderung gibt .. dh wer / wann
Sonic Soul

Antworten:

1889

Für die Suche nach commit Inhalt (dh tatsächliche Linien der Quelle, im Gegensatz zu Nachrichten zu begehen und dergleichen), was Sie tun müssen:

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

git rev-list --all | xargs git grep <expression> funktioniert, wenn der Fehler "Argumentliste zu lang" auftritt.

Wenn Sie die Suche auf einen Teilbaum beschränken möchten (z. B. "lib / util"), müssen Sie diesen an den rev-listUnterbefehl übergeben und grepaußerdem:

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

Dies durchsucht Ihren gesamten Commit-Text nach regexp.

Der Grund für die Übergabe des Pfads in beiden Befehlen liegt darin, dass rev-listdie Revisionsliste zurückgegeben wird, in der alle Änderungen vorgenommen wurdenlib/util Sie müssen jedoch auch an übergeben, grepdamit nur gesucht wird lib/util.

Stellen Sie sich folgendes Szenario vor: grep Möglicherweise finden Sie dasselbe <regexp>in anderen Dateien, die in derselben Revision enthalten sind, die von zurückgegeben wurde rev-list(auch wenn diese Datei in dieser Revision nicht geändert wurde).

Hier sind einige andere nützliche Möglichkeiten zum Durchsuchen Ihrer Quelle:

Suchen Sie im Arbeitsbaum nach Text, der dem regulären Ausdruck für reguläre Ausdrücke entspricht:

git grep <regexp>

Durchsuchen Sie den Arbeitsbaum nach Textzeilen, die dem regulären Ausdruck regexp1 oder regexp2 entsprechen:

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

Durchsuchen Sie den Arbeitsbaum nach Textzeilen, die mit den regulären Ausdrücken regexp1 und regexp2 übereinstimmen, und melden Sie nur Dateipfade:

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

Durchsuchen Sie den Arbeitsbaum nach Dateien, deren Textzeilen mit dem regulären Ausdruck regexp1 und den Textzeilen mit dem regulären Ausdruck regexp2 übereinstimmen:

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

Durchsuchen Sie den Arbeitsbaum nach geänderten Textzeilen-Übereinstimmungsmustern:

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

Durchsuchen Sie alle Revisionen nach Text, der dem regulären Ausdruck regulärer Ausdrücke entspricht:

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

Durchsuchen Sie alle Revisionen zwischen rev1 und rev2 nach Text, der dem regulären Ausdruck regulärer Ausdrücke entspricht:

git grep <regexp> $(git rev-list <rev1>..<rev2>)
Jeet
quelle
61
Danke, funktioniert super! Es ist jedoch traurig, dass "$ (git rev-list --all)" benötigt wird und kein praktischer Schalter zum Festlegen der Suche im gesamten Verlauf eines Zweigs vorhanden ist.
Ortwin Gentz
3
Ausgezeichnet. +1. Das GitBook fügt einige Details hinzu ( book.git-scm.com/4_finding_with_git_grep.html ), und Junio ​​C Hamano veranschaulicht einige Ihrer Punkte: gitster.livejournal.com/27674.html
VonC
18
Leider kann ich das mit msysgit-1.7.4 nicht in Gang bringen. Es sagt mir sh.exe": /bin/git: Bad file number. Die Antwort von VonC funktioniert auch mit msysgit.
eckes
4
Wenn beim Aufrufen des Git Grep-Verlaufs mit der Rev-Liste der Fehler "Baum kann nicht gelesen werden" angezeigt wird, müssen Sie möglicherweise die Daten bereinigen. Probieren Sie es aus git gcoder checken Sie es aus: stackoverflow.com/questions/1507463/…
Anthony Panozzo
8
Ja, das scheint leider auch unter Windows zu scheitern.
mlissner
552

Sie sollten die Option pickaxe ( -S) von verwenden git log.

So suchen Sie nach Foo:

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

Weitere Informationen finden Sie unter Git-Verlauf - Finden Sie verlorene Zeilen nach Schlüsselwörtern .


Wie Jakub Narębski kommentierte:

  • Dies sucht nach Unterschieden, die eine Instanz von einführen oder entfernen<string> . Es bedeutet normalerweise "Revisionen, bei denen Sie mit 'Foo' eine Zeile hinzugefügt oder entfernt haben".

  • Mit dieser --pickaxe-regexOption können Sie erweiterten POSIX-Regex verwenden, anstatt nach einer Zeichenfolge zu suchen. Beispiel (von git log):git log -S"frotz\(nitfol" --pickaxe-regex


Wie Rob kommentierte, wird bei dieser Suche zwischen Groß- und Kleinschreibung unterschieden. Er eröffnete eine Folgefrage zur Suche ohne Berücksichtigung der Groß- und Kleinschreibung.

VonC
quelle
3
Danke, mir war diese Option nicht bekannt. Sieht so aus, als wäre dies die beste Lösung, wenn Sie an den Festschreibungsnachrichten interessiert sind, und die Lösung von Jeet ist am besten geeignet, wenn Sie das traditionelle UNIX-Grep-Verhalten des reinen Zeilenabgleichs benötigen.
Ortwin Gentz
@Ortwin: stimmte zu (und ich habe die gewählte Lösung positiv bewertet). Das git logbisschen in deiner Frage hatte mich verwirrt;)
VonC
12
Kombinieren Sie es mit dem -pFlag, um auch das Diff auszugeben.
Sander
Gibt es eine Möglichkeit, alle Verzeichnisse, die mit einem bestimmten Muster übereinstimmen, mit git log -S auszuschließen?
BakaKuna
3
@Anentropic Sie würden die --branches --allOptionen benötigen , um nach dem All Repo zu suchen.
VonC
249

Am liebsten mache ich das mit git logder -GOption (hinzugefügt in Version 1.7.4).

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

Es gibt einen subtilen Unterschied zwischen der Art -Gund Weise, wie die -SOptionen und bestimmen, ob ein Commit übereinstimmt:

  • Die -SOption zählt im Wesentlichen, wie oft Ihre Suche in einer Datei vor und nach einem Commit übereinstimmt. Das Commit wird im Protokoll angezeigt, wenn die Vorher- und Nachher-Zählungen unterschiedlich sind. Dies zeigt beispielsweise keine Commits an, bei denen eine Ihrer Suche entsprechende Zeile verschoben wurde.
  • Mit dieser -GOption wird das Commit im Protokoll angezeigt, wenn Ihre Suche mit einer Zeile übereinstimmt, die hinzugefügt, entfernt oder geändert wurde.

Nehmen Sie dieses Commit als Beispiel:

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

Da die Häufigkeit, mit der "Hallo" in der Datei angezeigt wird, vor und nach diesem Festschreiben gleich ist, stimmt die Verwendung nicht überein -Shello. Da jedoch eine Änderung an einer Zeilenübereinstimmung vorgenommen wurde hello, wird das Festschreiben mit angezeigt -Ghello.

Tyler Holien
quelle
2
Gibt es eine Möglichkeit, den passenden Änderungskontext in der Git-Protokollausgabe anzuzeigen?
Thilo-Alexander Ginkel
13
@ Thilo-AlexanderGinkel - Normalerweise füge ich nur die -pOption hinzu, für jedes Commit einen Unterschied anzuzeigen. Wenn das Protokoll in meinem Pager geöffnet wird, suche ich nach dem, wonach ich suche. Wenn Ihr Pager lessund Sie sind git log -Ghello -p, können Sie eingeben /hello, drücken Enterund verwenden nund Ndie nächsten / vorherigen Vorkommen von "Hallo" suchen.
Tyler Holien
Ich habe ein interessantes Problem mit -Gund Regex gefunden: Wenn die Befehlszeile UTF-8 verwendet und die angezeigte Datei eine ISO-Latin-Codierung (8 Bit) verwendet, .*schlägt dies fehl. Zum Beispiel habe ich eine Änderung Vierter Entwurf-> Fünfter Entwurfund während 'V.*ter Entwurf'eine Übereinstimmung erzeugt wird, 'F.*ter Entwurf'nicht.
U. Windl
51

Wenn Sie Codeänderungen durchsuchen möchten (sehen Sie, was mit dem angegebenen Wort im gesamten Verlauf tatsächlich geändert wurde), wählen Sie den patchModus - ich habe eine sehr nützliche Kombination gefunden:

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
quelle
11
Die akzeptierte Lösung funktioniert weder für mich noch für das Git-Protokoll -S. Dieser hat es getan!
Rodvlopes
29

git log Dies kann eine effektivere Methode sein, um in allen Zweigen nach Text zu suchen, insbesondere wenn viele Übereinstimmungen vorhanden sind und Sie zuerst neuere (relevante) Änderungen sehen möchten.

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

Diese Protokollbefehle listen Commits auf, die die angegebene Suchzeichenfolge / Regex (im Allgemeinen) zuerst hinzufügen oder entfernen. Das-p Option bewirkt, dass das relevante Diff dort angezeigt wird, wo das Muster hinzugefügt oder entfernt wurde, sodass Sie es im Kontext sehen können.

Nachdem Sie ein relevantes Commit gefunden haben, das den gesuchten Text hinzufügt (z. B. 8beeff00d), suchen Sie die Zweige, die das Commit enthalten:

git branch -a --contains 8beeff00d
Edward Anderson
quelle
Hallo, diese Zeilen scheinen überhaupt nicht zu funktionieren. Mein Befehl lautet> git log -p --all -S 'öffentlicher String DOB {get; einstellen; } = string.Empty; ' und jedes Mal, wenn ich versuche, es auszuführen, wird> fatal: mehrdeutiges Argument 'string': unbekannte Revision oder Pfad nicht im Arbeitsbaum. > Verwenden Sie '-', um Pfade von Revisionen wie
folgt
@ user216652 Aus irgendeinem Grund 'gruppieren die Anführungszeichen Ihre Suchzeichenfolge nicht als ein einziges Argument. Stattdessen 'publicist das Argument zu -S, und es behandelt den Rest als separate Argumente. Ich bin nicht sicher, in welcher Umgebung Sie ausgeführt werden, aber dieser Kontext wäre zur Fehlerbehebung erforderlich. Ich würde vorschlagen, bei Bedarf eine separate StackOverflow-Frage zu öffnen, um Ihnen bei der Fehlerbehebung zu helfen, mit dem gesamten Kontext, in dem Ihr git-Befehl an die Shell gesendet wird. Es scheint mir, dass es durch einen anderen Befehl gesendet wird? Kommentare hier sind nicht der richtige Ort, um dies herauszufinden.
Edward Anderson
26

Ich nahm Jeets Antwort und passte sie an Windows an (dank dieser Antwort ):

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

Beachten Sie, dass für mich aus irgendeinem Grund das tatsächliche Commit, das diesen regulären Ausdruck gelöscht hat, nicht in der Ausgabe des Befehls angezeigt wurde, sondern ein Commit davor.

ripper234
quelle
2
+1 - und wenn Sie vermeiden möchten, nach jedem Fund "q" zu --no-pager
drücken
2
Außerdem möchte ich darauf hinweisen, dass das Anhängen an eine Textdatei den zusätzlichen Vorteil hat, dass der passende Text tatsächlich angezeigt wird. (an eine Textdatei anhängen, die >>results.txtfür diejenigen verwendet wird, die sich nicht mit Windows-Piping
auskennen
1
Und ich fand Bashs Syntax hässlich :)
Smido
23

Suche in jeder Revision, jeder Datei :

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

Suchen Sie nur in bestimmten Dateien, z. B. XML-Dateien:

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

Die Ergebniszeilen sollten folgendermaßen aussehen: 6988bec26b1503d45eb0b2e8a4364afb87dde7af: bla.xml: Text der gefundenen Zeile ...

Sie können dann weitere Informationen wie Autor, Datum und Diff erhalten, indem Sie git show:

git show 6988bec26b1503d45eb0b2e8a4364afb87dde7af
Christophe Roussy
quelle
11

Der Einfachheit halber würde ich die Verwendung von GUI vorschlagen: gitk - Der Git-Repository-Browser . Es ist ziemlich flexibel

  1. So suchen Sie nach Code:

    Geben Sie hier die Bildbeschreibung ein
  2. So suchen Sie nach Dateien:

    Geben Sie hier die Bildbeschreibung ein
  3. Natürlich unterstützt es auch reguläre Ausdrücke:

    Geben Sie hier die Bildbeschreibung ein

Mit den Aufwärts- / Abwärtspfeilen können Sie durch die Ergebnisse navigieren.

watashiSHUN
quelle
6

Für alle anderen, die dies in Sourcetree versuchen , gibt es keinen direkten Befehl in der Benutzeroberfläche (ab Version 1.6.21.0). Sie können jedoch die in der akzeptierten Antwort angegebenen Befehle verwenden, indem Sie Terminal öffnen (Schaltfläche in der Hauptsymbolleiste verfügbar) und darin kopieren / einfügen.

Hinweis: Sourcetree der Suchansicht können teilweise für Sie Textsuche tun. Drücken Sie Ctrl+ 3, um zur Suchansicht zu gelangen (oder klicken Sie unten auf die Registerkarte Suchen). Setzen Sie von ganz rechts den Suchtyp auf Dateiänderungen und geben Sie dann die Zeichenfolge ein, nach der Sie suchen möchten. Diese Methode weist im Vergleich zum obigen Befehl die folgenden Einschränkungen auf:

  1. Sourcetree zeigt nur die Commits an , die das Suchwort in einer der geänderten Dateien enthalten. Das Finden der genauen Datei, die den Suchtext enthält, ist wiederum eine manuelle Aufgabe.
  2. RegEx wird nicht unterstützt.
Punkt net
quelle
4

Immer wenn ich mich bei Ihnen befinde, benutze ich die folgende Befehlszeile:

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

Erläuterung:

  1. git log- Muss ich hier mehr schreiben? Es zeigt die Protokolle in chronologischer Reihenfolge.
  2. -S "<words/phrases i am trying to find>" - Es werden alle Git-Commits angezeigt, bei denen jede Datei (hinzugefügt / geändert / gelöscht) die Wörter / Phrasen enthält, die ich ohne '<>' Symbole zu finden versuche.
  3. --all - Durchsetzen und Durchsuchen aller Filialen.
  4. --oneline - Es komprimiert das Git-Protokoll in einer Zeile.
  5. --graph - Es wird ein Diagramm mit chronologisch geordneten Commits erstellt.
surajs1n
quelle
1
"Immer wenn ich mich bei dir befinde, habe ich das Bedürfnis, Git zu benutzen!"
Sebi
1
Das ist eine großartige Antwort!
Alf Eaton
@ AlfEaton mein Vergnügen!
surajs1n
2

Jeets Antwort funktioniert in PowerShell.

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

Im Folgenden werden alle Dateien in einem Commit angezeigt, die a enthalten password.

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

# Display unique file names
$result | select -unique { $_ -replace "(^.*?:)|(:.*)", "" }
Shaun Luttin
quelle
1

Versuchen Sie also, ältere Versionen des Codes zu durchsuchen, um festzustellen, wo zuletzt etwas vorhanden ist?

Wenn ich das tun würde, würde ich wahrscheinlich Git Bisect verwenden . Mit bisect können Sie eine bekannte gute Version, eine bekannte schlechte Version und ein einfaches Skript angeben, das überprüft, ob die Version gut oder schlecht ist (in diesem Fall ein Grep, um festzustellen, ob der gesuchte Code vorhanden ist ). Wenn Sie dies ausführen, wird festgestellt, wann der Code entfernt wurde.

Rob Di Marco
quelle
2
Ja, aber Ihr "Test" kann ein Skript sein, das nach dem Code sucht und "true" zurückgibt, wenn der Code vorhanden ist, und "false", wenn dies nicht der Fall ist.
Rob Di Marco
2
Nun, was ist, wenn Code in Revision 10 schlecht war, in Revision 11 gut und in Revision 15 wieder schlecht wurde ...
Paolo
2
Ich stimme Paolo zu. Die binäre Suche ist nur für "geordnete" Werte geeignet. Im Fall von Git Bisect bedeutet dies, dass alle "guten" Revisionen vor allen "schlechten" Revisionen beginnen, beginnend mit dem Referenzpunkt. Diese Annahme kann jedoch nicht getroffen werden, wenn nach Übergangscode gesucht wird. Diese Lösung funktioniert in einigen Fällen möglicherweise, ist jedoch keine gute Allzwecklösung.
Kent
Ich denke, dies ist sehr ineffizient, da der gesamte Baum mehrmals auf Halbierung überprüft wird.
U. Windl
0

Szenario: Sie haben Ihren Code mithilfe Ihrer IDE gründlich bereinigt. Problem: Die IDE wurde mehr bereinigt als sie sollte und jetzt wird Ihr Code nicht kompiliert (fehlende Ressourcen usw.)

Lösung:

git grep --cached "text_to_find"

Es findet die Datei, in der "text_to_find" geändert wurde.

Sie können diese Änderung jetzt rückgängig machen und Ihren Code kompilieren.

Garytech
quelle
0
git rev-list --all | xargs -n 5 git grep EXPRESSION

ist eine Optimierung der Lösung von Jeet , sodass die Ergebnisse während der Suche angezeigt werden und nicht nur am Ende (was in einem großen Repository lange dauern kann).

Laktak
quelle
-1

In meinem Fall musste ich ein kurzes Commit suchen und die aufgelisteten Lösungen funktionierten leider nicht.

Ich habe es geschafft mit (ersetze das REGEX- Token):

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
user9869932
quelle