Warum folgt Git-Blamage Umbenennungen nicht?

Lesezeit: 8 Minuten

Benutzer-Avatar
Eugen Konkow

$ pwd
/data/mdi2/classes

$ git blame -L22,+1 -- utils.js
99b7a802 mdi2/utils.js (user 2015-03-26 21:54:57 +0200 22)  #comment

$ git blame -L22,+1 99b7a802^ -- utils.js
fatal: no such path mdi2/classes/utils.js in 99b7a802^

Wie Sie bemerkt haben, befand sich die Datei in diesem Commit in einem anderen Verzeichnis

$ git blame -L22,+1 99b7a802^ -- ../utils.js
c5105267 (user 2007-04-10 08:00:20 +0000 22)    #comment 2

Trotz auf doc

The origin of lines is automatically followed across whole-file renames (currently there is no option to turn
       the rename-following off)

Schuld folgt nicht Umbenennungen. Wieso den?

AKTUALISIEREN: Kurze Antwort

git blame Folgen Sie Umbenennungen, aber nicht für git blame COMMIT^ -- <filename>

Dies ist jedoch zu schwierig, um Dateiumbenennungen manuell durch eine Vielzahl von Umbenennungen und eine Menge Historie zu verfolgen. Ich denke, dieses Verhalten muss behoben werden, um Umbenennungen stillschweigend zu folgen git blame COMMIT^ -- <filename>. Oder zumindest, --follow muss implementiert werden, damit ich: git blame --follow COMMIT^ -- <filename>

UPDATE2: Das ist unmöglich. Lesen Sie unten.

ANTWORT VON MAILLIST von Junio ​​C. Hamano

git blame Folgen Sie Umbenennungen, aber nicht für git blame COMMIT^ -- <filename>

Angenommen, Sie haben Datei A und Datei B in Ihrer Version v1.0.

Sechs Monate später wurde der Code stark umgestaltet, und Sie brauchen den Inhalt dieser beiden Dateien nicht separat. Sie haben A und B entfernt und vieles von dem, was sie hatten, befindet sich jetzt in Datei C. Das ist der aktuelle Stand.

git blame -C HEAD -- C

kann den Inhalt von beiden gut folgen, aber wenn Sie war
sagen darf

git blame v1.0 -- C

was bedeutet es überhaupt? C existierte v1.0 überhaupt nicht. Bitten Sie darum, den Inhalten von A damals zu folgen, oder B? Wie haben Sie gesagt, dass Sie A und nicht B gemeint haben, als Sie C in diesem Befehl gesagt haben?

“GIT BLAUE” folgt Inhaltsbewegungen und behandelt “Umbenennungen” niemals auf besondere Weise, da es dumm ist zu denken, dass eine Umbenennung etwas Besonderes ist 😉

Die Art und Weise, wie Sie dem Befehl von seiner Befehlszeile aus mitteilen, von welchem ​​​​Inhalt aus zu graben ist, besteht darin, den Startpunkt Commit (standardmäßig HEAD, aber Sie können COMMIT ^ als Beispiel angeben) und den Pfad an diesem Startpunkt anzugeben. Da es keinen Sinn macht, Git C mitzuteilen und es dann auf magische Weise vermuten zu lassen, dass Sie in einigen Fällen A und in anderen Fällen B gemeint haben. Wenn v1.0 kein C hatte, ist das einzig Vernünftige, zu beenden, anstatt zu raten (und ohne dem Benutzer zu sagen, wie er geraten hat).

Benutzer-Avatar
Torek

git blame tut Folge Umbenennungen (ebenso wie git log wenn du es gibst --follow). Das Problem liegt in der Weg es folgt Umbenennungen, was ein nicht sehr gründlicher Hack ist: Wenn es jeweils einen Commit zurückgeht (von jedem Kind zu jedem Elternteil), macht es einen Diff – die gleiche Art von Diff, mit der Sie manuell machen können:

git diff -M SHA1^ SHA1

– und überprüft, ob dieses Diff eine Umbenennung erkannt hat.1

Das ist alles in Ordnung, soweit es geht, aber es bedeutet das für git blame um eine Umbenennung zu erkennen, (a) git diff -M muß sein fähig um es zu erkennen (glücklicherweise ist das hier der Fall) und – hier ist, was Ihnen Probleme bereitet – es muss Schritt über die Umbenennung.

Angenommen, das Commit-Diagramm sieht etwa so aus:

A <-- B <-- ... Q <-- R <-- S <-- T

wobei jeder Großbuchstabe ein Commit darstellt. Nehmen Sie weiter an, dass eine Datei beim Festschreiben umbenannt wurde Rdamit in Commits R durch T es hat Namen newname während in Commits A durch Q es hat Namen oldname.

Wenn du läufst git blame -- newnamedie Sequenz beginnt bei Tvergleicht S und Tvergleicht R und Sund vergleicht Q und R. Wann es vergleicht Q und R, git blame entdeckt die Namensänderung und beginnt mit der Suche oldname bei Verpflichtungen Q und früher, also wenn es vergleicht P und Q Es vergleicht Dateien oldname und oldname in diesen beiden Commits.

Wenn Sie dagegen laufen git blame R^ -- newname (oder git blame Q -- newname), sodass die Sequenz beim Commit beginnt Qes gibt keine Datei newname in diesem Commit, und es gibt keine Umbenennung beim Vergleichen P und Qund git blame gibt einfach auf.

Der Trick besteht darin, dass Sie, wenn Sie mit einem Commit beginnen, in dem die Datei den vorherigen Namen hatte, git den alten Namen geben müssen:

git blame R^ -- oldname

und dann funktioniert alles wieder.


1In dem git diff Dokumentationwerden Sie sehen, dass es eine gibt -M Option, die steuert wie git diff erkennt Umbenennungen. Das blame Code modifiziert dies ein wenig (und macht tatsächlich zwei Durchgänge, einen mit -M ausgeschaltet und eine zweite mit -M eingeschaltet) und verwendet seine eigene (andere) -M Option für etwas andere Zwecke, aber letztendlich wird derselbe Code verwendet.


[Edit to add reply to comment (didn’t fit as a comment itself)]:

Gibt es ein Tool, das mir Dateiumbenennungen anzeigen kann wie: git renames SHA date oldname->newname

Nicht ganz, aber git diff -M kommt nah und kann nah genug sein.

Ich bin mir nicht sicher, was Sie hier mit “SHA-Datum” meinen, aber git diff -M ermöglicht es Ihnen, zwei SHA-1s bereitzustellen und links-gegen-rechts zu vergleichen. Hinzufügen --name-status um nur Dateinamen und Dispositionen zu erhalten. Somit git diff -M --name-status HEAD oldsha1 kann berichten, dass von zu konvertieren HEAD zu oldsha1Git glaubt, dass Sie sollten Reine Datei umbenennen und den alten Namen als “neuen” Namen melden. Zum Beispiel gibt es im Git-Repository selbst eine Datei mit dem aktuellen Namen Documentation/giteveryday.txt die früher einen etwas anderen Namen hatten:

$ git diff -M --name-status HEAD 992cb206
M       .gitignore
M       .mailmap
[...snip...]
M       Documentation/diff-options.txt
R097    Documentation/giteveryday.txt   Documentation/everyday.txt
D       Documentation/everyday.txto
[...]

Wenn das die Datei ist, die Ihnen wichtig ist, sind Sie gut. Die beiden Probleme hier sind:

  • Suche nach einem SHA1: wo haben 992cb206 komme aus? Wenn Sie bereits einen SHA-1 haben, ist das einfach; wenn nicht, git rev-list ist das SHA1-Erkennungstool; lesen Sie die Dokumentation;
  • und die Tatsache, dass nach einer Reihe von Umbenennungen durch jeden Commit ein Commit nach dem anderen folgt, as git blame tut, kann zu ganz anderen Antworten führen als der Vergleich eines viel späteren Commits (HEAD) gegen einen viel früheren Commit (992cb206 oder Wasauchimmer). In diesem Fall kommt es auf das gleiche heraus, aber der “Ähnlichkeitsindex” beträgt hier 97 von 100. Wenn er in einigen Zwischenschritten viel stärker modifiziert worden wäre, könnte dieser Ähnlichkeitsindex unter 50% fallen … doch, wenn wir eine Überarbeitung nur a vergleichen würden wenig nach 992cb206 zu 992cb206 (wie git blame würde), könnte der Ähnlichkeitsindex zwischen diesen beiden Dateien möglicherweise höher sein.

Was benötigt wird (und fehlt), ist für git rev-list selbst umzusetzen --followsodass alle Befehle, die verwenden git rev-list intern – dh die meisten Befehle, die auf mehr als nur einer Revision funktionieren – können es tun. Unterwegs wäre es schön, wenn es in die andere Richtung funktionieren würde (derzeit --follow ist nur von neuer zu älter, dh funktioniert gut mit git blame und funktioniert gut mit git log solange du nicht zuerst mit nach der ältesten Geschichte fragst --reverse).

  • Gibt es ein Tool, das mir Dateiumbenennungen anzeigen kann wie: git renames <filename> SHA-Datum alter Name -> neuer Name

    – Eugen Konkow

    6. April 2015 um 12:30 Uhr

  • Siehe hinzugefügte Bearbeitung; beachte auch das git blame selbst zeigt Ihnen den aktualisierten Dateinamen.

    – Torek

    6. April 2015 um 17:08 Uhr

  • Mit diesem Befehl versuche ich zu beantworten, was DATE und was SHA-1 ist, als umbenannt wurde. Also kann ich oldname und sha1 nehmen, um diese als Schuldargumente zu verwenden. Aber wenn –follow wie von Ihnen vorgeschlagen implementiert wird, benötige ich diesen Befehl überhaupt nicht

    – Eugen Konkow

    7. April 2015 um 7:31 Uhr

Benutzer-Avatar
Eugen Konkow

**siehe UPD. Jetzt können Sie umbenannte Dateien verfolgen

Der neueste Git hat einen interessanten Befehl. Fügen Sie neben Ihrer Konfiguration hinzu:

[alias]
    follow= "!sh -c 'git log --topo-order -u -L $2,${3:-$2}:"$1"'" -

Jetzt kannst du:

$git follow <filename> <linefrom> [<lineto>]

Und Sie werden jeden Commit sehen, der bestimmte Zeilen ändert <filename>.

Auch das kann Sie interessieren --follow Option von git log Befehl:

Fortsetzen der Auflistung des Verlaufs einer Datei über Umbenennungen hinaus (funktioniert nur für eine einzelne Datei).

Wenn Sie an der Kopiererkennung interessiert sind, verwenden Sie -C:

Erkennen Sie Kopien sowie Umbenennungen. Siehe auch –find-copies-harder. Wenn n angegeben ist, hat es die gleiche Bedeutung wie für -M.

-C sieht verschiedene Dateien im gleichen Commit aus. Wenn Sie erkennen möchten, dass Code aus einer anderen Datei stammt, die in diesem Commit nicht geändert wurde. Dann solltest du dich versorgen --find-copies-harder Möglichkeit.

Aus Leistungsgründen findet die Option -C standardmäßig nur dann Kopien, wenn die Originaldatei der Kopie im selben Änderungssatz geändert wurde. Dieses Flag veranlasst den Befehl, unveränderte Dateien als Kandidaten für die Quelle der Kopie zu untersuchen. Dies ist eine sehr teure Operation für große Projekte, also verwenden Sie sie mit Vorsicht. Das Angeben von mehr als einer -C-Option hat den gleichen Effekt.

UPD

Ich verbessere diesen Alias:

[alias]
follow = "!bash -c '                                                 \
    if [[ $1 == \"/\"* ]]; then                                      \
        FILE=$1;                                                     \
    else                                                             \
        FILE=${GIT_PREFIX}$1;                                        \
    fi;                                                              \
    echo \"git log --topo-order -u -L $2,${3:-$2}:\\\"$FILE\\\" $4 \";   \
    git log -w -b -p --ignore-blank-lines --topo-order -u -L $2,${3:-$2}:\"$FILE\" $4;\
' --"

Jetzt können Sie verfolgen, wie der angegebene Zeilenbereich geändert wird:

git follow file_name.c 30 35

Sie können sogar einer anderen Datei folgen, beginnend mit commit (@arg4)

git follow old_file_name.c 30 35 85ce061

85ce061 – ist festgeschrieben, wo die Datei umbenannt wurde

HINWEIS: Leider berücksichtigt Git keine Änderungen im Arbeitsverzeichnis. Wenn Sie also lokale Änderungen an einer Datei vornehmen, müssen Sie sie speichern, bevor Sie können follow Änderungen

1289360cookie-checkWarum folgt Git-Blamage Umbenennungen nicht?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy