Wie kann ich eine gelöschte Zeile „beschuldigen“?

506

git blameeignet sich hervorragend für geänderte und hinzugefügte Zeilen. Wie kann ich jedoch feststellen, wann eine Zeile, die in einem bestimmten vorherigen Commit vorhanden war, gelöscht wurde? Ich denke nach bisect, aber ich hoffte auf etwas Handlicheres.

(Bevor Sie fragen: In diesem Fall habe ich gerade a ausgeführt git log -pund nach der Codezeile gesucht und (a) ein Idiot hat gerade die wichtige Zeile im vorherigen Commit gelöscht und (b) ich war dieser Idiot.)

Malvolio
quelle
4
Es gibt ein Follow-up mit einer Antwort, die klarstellt, git log -S<string> /path/to/filedass ein -coder -ccauch Entfernungen während der Zusammenführung (Konflikte)
angezeigt werden sollen
3
Es sollte -cund sein --cc. @Steen: Richtig, danke für den Hinweis! Dummes Versehen. Ich wünschte, ich könnte den Kommentar bearbeiten. Das Hinzufügen eines neuen, dann das Löschen
meines
4
Ich wünschte, ich git blamehätte die Möglichkeit, gelöschte Zeilen (mit möglicherweise durchgestrichenem oder rotem Text) mit der Revision anzuzeigen, in der sie gelöscht wurden.
Craig McQueen
Wäre das schwer zu schreiben? Ich weiß nicht viel über Git-Interna.
Malvolio

Antworten:

636

Wenn Sie den Inhalt der Zeile kennen, ist dies ein idealer Anwendungsfall für:

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

Dies zeigt Ihnen Commits, die eine Instanz dieser Zeichenfolge einführen oder entfernen. Es gibt auch das, -G<regex>was dasselbe mit regulären Ausdrücken macht! Siehe man git-logund die Suche nach den -Gund -SOptionen oder pickaxe (der Name für diese Funktionen) für weitere Informationen.

Die -SOption wird auch in der Kopfzeile der git-blameManpage im Beschreibungsabschnitt erwähnt, wo ein Beispiel mit verwendet wird git log -S....

Cascabel
quelle
Genial ... genau das, was ich für diesen Portierungsjob brauchte. Ich arbeite an +1
jkp
37
Nachdem ich Git über 1 Jahr lang verwendet habe, wundert es mich immer noch, dass Git immer irgendwo einen Befehl / eine Option hat, um fast jedes Nutzungsszenario anzusprechen, das ich habe. Vielen Dank für das Teilen dieses, es ist genau das, was ich jetzt brauche!
Pascal Bourque
22
Diese Methode hat bei mir schon früher funktioniert, aber gerade habe ich einen Fall gesehen, in dem das Commit, bei dem die Zeile gelöscht wurde, nicht gefunden wurde. Es stellte sich heraus, dass die betreffende Zeile bei einem Zusammenführungs-Commit gelöscht wurde - würde das den Fehler erklären? (Die git blame --reverseMethode fand es jedoch.)
Antinom
9
@antinome Verwenden Sie die -cOption zusätzlich, um die Commits aus dem Zusammenführen anzuzeigen .
Yunzen
2
Ich habe auf der Manpage Strg + F auf "-s" gedrückt und nichts gefunden. Wo auf der Seite sehen Sie es? Ich benutze Git 1.8.5.2
temporärer_Benutzername
134

Ich denke, was du wirklich willst, ist

git blame --reverse START..END filename

Aus der Manpage :

Gehen Sie die Geschichte vorwärts statt rückwärts. Anstatt die Revision anzuzeigen, in der eine Zeile angezeigt wurde, wird die letzte Revision angezeigt, in der eine Zeile vorhanden war. Dies erfordert eine Reihe von Revisionen wie START..END, bei denen der Weg der Schuld in START vorhanden ist.

Mit git blame reverse können Sie das letzte Commit finden, in dem die Zeile angezeigt wurde. Sie müssen noch das Commit abrufen, das danach kommt.

Mit dem folgenden Befehl können Sie ein umgekehrtes Git-Protokoll anzeigen. Das erste angezeigte Commit ist das letzte Mal, dass diese Zeile angezeigt wird, und das nächste Commit ist, wenn es geändert oder entfernt wird.

git log --reverse --ancestry-path COMMIT^..master
Chronial
quelle
14
Wenn es mehrere Zusammenführungen von dem Zweig gibt, in dem die Linie hinzugefügt wurde, zu dem Zweig, in dem die Linie fehlt (oder in jedem anderen Fall, in dem mehrere Pfade in der Linie von START bis END vorhanden sind), git blame --reversewird die Revision vor dem Zusammenführen angezeigt, die chronologisch war Zuletzt nicht die Überarbeitung vor der ersten Zusammenführung, bei der die Entscheidung getroffen wurde, die Linie nicht zu übernehmen. Gibt es eine Möglichkeit, die früheste Revision zu finden, bei der die Leitung nicht mehr existiert, anstatt die letzte?
Rakslice
2
@rakslice, dafür kannst du tadel verwenden --reverse --first-parent, es ist etwas besser.
max630
17

Nur um Cascabels Antwort zu vervollständigen :

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

Ich hatte das gleiche Problem wie hier erwähnt, aber es stellte sich heraus, dass die Zeile fehlte, da ein Zusammenführungs-Commit von einem Zweig zurückgesetzt und dann wieder zusammengeführt wurde, wodurch die betreffende Zeile effektiv entfernt wurde. Das --full-historyFlag verhindert das Überspringen dieser Commits.

estani
quelle
9

git Schuld --reverse können Sie erhalten nah an , wo die Linie gelöscht. Es zeigt jedoch nicht auf die Revision, in der die Zeile gelöscht wird. Es zeigt auf die letzte Revision, in der die Linie vorhanden war . Wenn es sich bei der folgenden Revision um ein einfaches Commit handelt, haben Sie Glück und haben die Löschrevision erhalten. OTOH, wenn die folgende Revision ein Merge-Commit ist , können die Dinge ein wenig wild werden.

Als Teil der Bemühungen, Difflame zu erstellen, habe ich genau dieses Problem gelöst. Wenn Sie also Python bereits auf Ihrer Box installiert haben und bereit sind, es auszuprobieren, warten Sie nicht länger und lassen Sie mich wissen, wie es geht.

https://github.com/eantoranz/difflame

eftshift0
quelle
0

Für Änderungen, die in Merge-Commits versteckt sind

Beim Zusammenführen von Commits werden die Änderungen automatisch in der Git-Protokollausgabe ausgeblendet. Sowohl die Spitzhacke als auch die umgekehrte Schuld fanden die Änderung nicht. Die gewünschte Zeile wurde also hinzugefügt und später entfernt, und ich wollte die Zusammenführung finden, die sie entfernt hat. Der Dateiverlauf git log -p -- path/filezeigte nur, dass er hinzugefügt wurde. Hier ist der beste Weg, den ich gefunden habe, um es zu finden:

git log -p -U9999 -- path/file

Suchen Sie nach der Änderung und suchen Sie dann rückwärts nach "^ commit" - das erste "^ commit" ist das Commit, bei dem die Datei zuletzt diese Zeile hatte. Das zweite "^ commit" erfolgt nach dem Verschwinden. Das zweite Commit ist möglicherweise dasjenige, das es entfernt hat. Das-U9999 soll den gesamten Dateiinhalt anzeigen (nach jedem Ändern der Datei), vorausgesetzt, Ihre Dateien bestehen aus maximal 9999 Zeilen.

Findet alle zugehörigen Zusammenführungen über Brute Force (differenziert jedes mögliche Zusammenführungs-Commit mit seinem ersten Elternteil, läuft gegen Tonnen von Commits)

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

(Ich habe versucht, den Revisionsfilter stärker einzuschränken, bin jedoch auf Probleme gestoßen und empfehle dies nicht. Die von mir gesuchten Änderungen zum Hinzufügen / Entfernen betrafen verschiedene Zweige, die zu unterschiedlichen Zeiten zusammengeführt wurden und A ... B nicht enthalten waren wann die Änderungen tatsächlich in die Hauptzeile übernommen wurden.)

Zeigen Sie einen Git-Baum mit diesen beiden Commits (und entfernen Sie einen Großteil des komplexen Git-Verlaufs):

git log --graph --oneline A B ^$(git merge-base A B) (A ist das erste Commit oben, B ist das zweite Commit oben)

Zeige die Geschichte von A und die Geschichte von B minus die Geschichte von A und B.

Alternative Version (scheint den Pfad eher linear als den regulären Git-Verlaufsbaum anzuzeigen - ich bevorzuge jedoch den regulären Git-Verlaufsbaum):

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

Drei, nicht zwei Punkte - drei Punkte bedeuten "r1 r2 - nicht $ (Git-Merge-Basis - alle r1 r2). Dies ist die Menge von Commits, die entweder von r1 (linke Seite) oder r2 (rechts) erreichbar sind Seite), aber nicht von beiden. " - Quelle: "man gitrevisions"

Curtis Yallop
quelle