Wie lese ich die Ausgabe von Git Diff?

270

Die Manpage für git-diffist ziemlich lang und erklärt viele Fälle, die für einen Anfänger nicht notwendig zu sein scheinen. Zum Beispiel:

git diff origin/master
Poseid
quelle
1
Durch die Verwendung eines anderen Texteditors wurden die @ ... @ -Bereichsnotationen für Zeilennummern offensichtlich.
Poseid
Welcher Texteditor?
Jus12

Antworten:

488

Schauen wir uns ein Beispiel für einen erweiterten Unterschied zum Git-Verlauf an (in Commit 1088261f im git.git-Repository ):

diff --git a/builtin-http-fetch.c b/http-fetch.c
similarity index 95%
rename from builtin-http-fetch.c
rename to http-fetch.c
index f3e63d7..e8f44ba 100644
--- a/builtin-http-fetch.c
+++ b/http-fetch.c
@@ -1,8 +1,9 @@
 #include "cache.h"
 #include "walker.h"

-int cmd_http_fetch(int argc, const char **argv, const char *prefix)
+int main(int argc, const char **argv)
 {
+       const char *prefix;
        struct walker *walker;
        int commits_on_stdin = 0;
        int commits;
@@ -18,6 +19,8 @@ int cmd_http_fetch(int argc, const char **argv, const char *prefix)
        int get_verbosely = 0;
        int get_recover = 0;

+       prefix = setup_git_directory();
+
        git_config(git_default_config, NULL);

        while (arg < argc && argv[arg][0] == '-') {

Analysieren wir diesen Patch Zeile für Zeile.

  • Die erste Zeile

    diff --git a / builtin-http-fetch.cb / http-fetch.c
    ist ein "git diff" -Header in der Form diff --git a/file1 b/file2. Die a/und b/Dateinamen sind identisch, es sei denn, es handelt sich um eine Umbenennung / Kopie (wie in unserem Fall). Das --gitsoll bedeuten, dass diff im Diff-Format "git" vorliegt.

  • Als nächstes folgen eine oder mehrere erweiterte Kopfzeilen. Die ersten drei

    Ähnlichkeitsindex 95%
    Umbenennen von builtin-http-fetch.c
    Umbenennen in http-fetch.c
    sagen uns , dass die Datei umbenannt wurde aus builtin-http-fetch.czu , http-fetch.cund dass diese beiden Dateien sind zu 95% identisch (die verwendet wurde dieses Umbenennungs zu erkennen).

    Die letzte Zeile im erweiterten Diff-Header
    Index f3e63d7..e8f44ba 100644
    Erzählen Sie uns über den Modus einer bestimmten Datei ( 100644bedeutet, dass es sich um eine normale Datei handelt und nicht z. B. über einen Symlink, und dass sie kein ausführbares Berechtigungsbit hat) und über den verkürzten Hash von Preimage (die Version der Datei vor der angegebenen Änderung) und Postimage (die Version der Datei nach Änderung). Diese Zeile wird verwendet, um git am --3wayzu versuchen, eine 3-Wege-Zusammenführung durchzuführen, wenn der Patch nicht selbst angewendet werden kann.

  • Als nächstes folgt ein zweizeiliger einheitlicher Diff-Header

    --- a / builtin-http-fetch.c
    +++ b / http-fetch.c
    Im Vergleich zum diff -UErgebnis hat es weder eine Änderungszeit von Datei noch eine Änderungszeit von Datei nach Quell- (Preimage) und Zieldateinamen (Postimage). Wenn eine Datei erstellt wurde, ist die Quelle /dev/null; Wenn die Datei gelöscht wurde, ist das Ziel /dev/null.
    Wenn Sie setzen diff.mnemonicPrefixKonfigurationsvariable auf true, anstelle von a/und b/Präfixe in diesem zweizeiligen Header können Sie stattdessen c/, i/, w/und o/als Präfixe, die jeweils zu dem, was Sie vergleichen; siehe git-config (1)

  • Als nächstes kommen ein oder mehrere Unterschiede; Jedes Stück zeigt einen Bereich, in dem sich die Dateien unterscheiden. Unified Format Hunks beginnen mit zeilenartig

    @@ -1,8 +1,9 @@
    oder
    @@ -18,6 +19,8 @@ int cmd_http_fetch (int argc, const char ** argv, ...
    Es ist im Format @@ from-file-range to-file-range @@ [header]. Der Dateibereich liegt in der Form vor -<start line>,<number of lines>und der Dateibereich ist +<start line>,<number of lines>. Sowohl die Startlinie als auch die Anzahl der Linien beziehen sich auf die Position und Länge des Stücks im Vorbild bzw. im Nachbild. Wenn die Anzahl der Zeilen nicht angezeigt wird, bedeutet dies, dass sie 0 ist.

    Der optionale Header zeigt die C-Funktion, bei der jede Änderung auftritt, wenn es sich um eine C-Datei handelt (wie die -pOption in GNU diff), oder die entsprechende Funktion, falls vorhanden, für andere Dateitypen.

  • Als nächstes folgt die Beschreibung, wo sich Dateien unterscheiden. Die beiden Dateien gemeinsamen Zeilen beginnen mit einem Leerzeichen. Die Zeilen, die sich zwischen den beiden Dateien tatsächlich unterscheiden, haben eines der folgenden Kennzeichen in der linken Druckspalte:

    • '+' - Hier wurde der ersten Datei eine Zeile hinzugefügt.
    • '-' - Hier wurde eine Zeile aus der ersten Datei entfernt.


    Also zum Beispiel das erste Stück

     #include "cache.h"
     #include "walker.h"
    
    -int cmd_http_fetch(int argc, const char **argv, const char *prefix)
    +int main(int argc, const char **argv)
     {
    +       const char *prefix;
            struct walker *walker;
            int commits_on_stdin = 0;
            int commits;
    

    bedeutet, dass cmd_http_fetchdurch ersetzt mainund diese const char *prefix;Zeile hinzugefügt wurde.

    Mit anderen Worten, vor der Änderung sah das entsprechende Fragment der Datei 'builtin-http-fetch.c' folgendermaßen aus:

    #include "cache.h"
    #include "walker.h"
    
    int cmd_http_fetch(int argc, const char **argv, const char *prefix)
    {
           struct walker *walker;
           int commits_on_stdin = 0;
           int commits;
    

    Nach der Änderung sieht dieses Fragment der aktuellen Datei 'http-fetch.c' stattdessen folgendermaßen aus:

    #include "cache.h"
    #include "walker.h"
    
    int main(int argc, const char **argv)
    {
           const char *prefix;
           struct walker *walker;
           int commits_on_stdin = 0;
           int commits;
    
  • Es könnte geben

    \ Kein Zeilenumbruch am Dateiende
    Linie vorhanden (es ist nicht im Beispiel diff).

Wie Donal Fellows sagte , ist es am besten, das Lesen von Unterschieden an realen Beispielen zu üben, bei denen Sie wissen, was Sie geändert haben.

Verweise:

Jakub Narębski
quelle
1
@Geremia: Git verwendet auf Ähnlichkeit basierende Heuristiken für die Erkennung von Umbenennungen ... und auch für die Erkennung von Codeverschiebungen und Kopien. So git blame -C -Cfunktioniert es. Es ist eine Entscheidung für das Git-Design. Das Git-Diff-Format zeigt dem Benutzer nur den Ähnlichkeits- (oder Unähnlichkeits-) Index an.
Jakub Narębski
1
@Geremia: Um genauer zu sein, [header]ist der nächste Vorgänger wie der Beginn der Funktion, der einem Stück vorausgeht. In den meisten Fällen enthält diese Zeile den Namen der Funktion, in der sich der Diff-Block befindet. Dies kann mit diffgitattribute konfiguriert werden, das auf diff-Treiber und diff-Treiber einschließlich xfuncnameKonfigurationsvariable eingestellt ist.
Jakub Narębski
1
@AnthonyGeoghegan: Zeilen werden möglicherweise gelöscht (dann beträgt die Anzahl der Zeilen im Postimage 0) oder hinzugefügt (dann beträgt die Anzahl der Zeilen im Preimage 0).
Jakub Narębski
1
@KasunSiyambalapitiya: Das von Git verwendete einheitliche Diff-Format (im Gegensatz zum Kontext-Diff-Format ^ [1]) unterscheidet nicht zwischen geänderter Zeile und entfernter und hinzugefügter Zeile. [1]: gnu.org/software/diffutils/manual/html_node/Context-Format.html
Jakub Narębski
1
@ JakubNarębski: Die Anzahl der Zeilen ist standardmäßig 1, nicht 0. Es ist so einfach. In der Praxis wird es nur für einzeilige Dateien als "-1" und / oder "+1" angezeigt, da kein Kontext zum Anzeigen vorhanden ist.
Guido Flohr
68

@@ -1,2 +3,4 @@ Teil des Diff

Ich habe eine Weile gebraucht, um diesen Teil zu verstehen, also habe ich ein minimales Beispiel erstellt.

Das Format ist im Grunde das gleiche wie das diff -ueinheitliche Diff.

Zum Beispiel:

diff -u <(seq 16) <(seq 16 | grep -Ev '^(2|3|14|15)$')

Hier haben wir die Zeilen 2, 3, 14 und 15 entfernt. Ausgabe:

@@ -1,6 +1,4 @@
 1
-2
-3
 4
 5
 6
@@ -11,6 +9,4 @@
 11
 12
 13
-14
-15
 16

@@ -1,6 +1,4 @@ meint:

  • -1,6bedeutet, dass dieses Stück der ersten Datei in Zeile 1 beginnt und insgesamt 6 Zeilen anzeigt. Daher werden die Zeilen 1 bis 6 angezeigt.

    1
    2
    3
    4
    5
    6
    

    -bedeutet "alt", wie wir es normalerweise als aufrufen diff -u old new.

  • +1,4bedeutet, dass dieses Stück der zweiten Datei in Zeile 1 beginnt und insgesamt 4 Zeilen anzeigt. Daher werden die Zeilen 1 bis 4 angezeigt.

    + bedeutet "neu".

    Wir haben nur 4 statt 6 Zeilen, da 2 Zeilen entfernt wurden! Das neue Stück ist nur:

    1
    4
    5
    6
    

@@ -11,6 +9,4 @@ für das zweite Stück ist analog:

  • In der alten Datei haben wir 6 Zeilen, beginnend in Zeile 11 der alten Datei:

    11
    12
    13
    14
    15
    16
    
  • In der neuen Datei haben wir 4 Zeilen, beginnend in Zeile 9 der neuen Datei:

    11
    12
    13
    16
    

    Beachten Sie, dass die Zeile 11die 9. Zeile der neuen Datei ist, da wir bereits 2 Zeilen auf dem vorherigen Hunk entfernt haben: 2 und 3.

Hunk-Header

Je nach Ihrer git - Version und Konfiguration können Sie auch eine Codezeile neben der bekommen @@Linie, zum Beispiel des func1() {in:

@@ -4,7 +4,6 @@ func1() {

Dies kann auch mit der -pFlagge der Ebene erhalten werden diff.

Beispiel: alte Datei:

func1() {
    1;
    2;
    3;
    4;
    5;
    6;
    7;
    8;
    9;
}

Wenn wir die Linie entfernen 6, zeigt der Unterschied :

@@ -4,7 +4,6 @@ func1() {
     3;
     4;
     5;
-    6;
     7;
     8;
     9;

Beachten Sie, dass dies nicht die richtige Zeile ist für func1: Es wurden Zeilen 1und übersprungen 2.

Diese großartige Funktion sagt oft genau, zu welcher Funktion oder Klasse jeder Hunk gehört, was sehr nützlich ist, um den Unterschied zu interpretieren.

Wie der Algorithmus zur Auswahl des Headers genau funktioniert, wird unter folgender Adresse erläutert: Woher kommt der Auszug im Git-Diff-Hunk-Header?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
11
Dies ist für alle, die es noch nicht ganz verstanden haben. In @@ -1,6 +1,4 @@pls lesen Sie nicht -1als minus oneoder +1als plus onestattdessen lesen Sie dies wie line 1 to 6in der alten (ersten) Datei. Beachten Sie hier - implies "old"nicht minus. Übrigens, danke für die Klarstellung ... haash.
dkjain
von diesem @@ -1,8 +1,9 @@ ist es möglich zu interpretieren, was tatsächlich passiert ist. Zum Beispiel 1) eine Zeile wurde hinzugefügt 2) eine Zeile wird geändert und eine Zeile wird hinzugefügt und so weiter. Oder ist es von einem anderen Weg, da es einen Weg geben sollte, sie zu erhalten, da Git Diff Correclty identifiziert, welche Zeilen im Code geändert wurden. Bitte helfen Sie mir, da ich das wirklich
klären
Bitte beachten Sie, dass diese Aussage in der obigen Antwort falsch und sehr irreführend ist: " +1,4sagt, dass dieses Stück den Zeilen 1 bis 4 der zweiten Datei entspricht ". Dies liegt daran, dass +1,4möglicherweise nicht kontingente Kontextzeilen verwendet werden. Vielmehr +1,4bedeutet " " tatsächlich, dass " in dieser 'Version' der Datei Zeilen (dh Kontextzeilen) vorhanden sind4 ". Es ist wichtig , die Bedeutung der zu verstehen +, -und <whitespace>zu Beginn dieser Zeilen, wie es um die Auslegung von hunks gilt. Ein visuelleres Beispiel: youtube.com/watch?v=1tqMjJeyKpw
Damilola Olowookere
23

Hier ist das einfache Beispiel.

diff --git a/file b/file 
index 10ff2df..84d4fa2 100644
--- a/file
+++ b/file
@@ -1,5 +1,5 @@
 line1
 line2
-this line will be deleted
 line4
 line5
+this line is added

Hier ist eine Erklärung (siehe Details hier ).

  • --git ist kein Befehl, das heißt, es ist eine Git-Version von Diff (nicht Unix)
  • a/ b/sind Verzeichnisse, sie sind nicht real. Es ist nur eine Annehmlichkeit, wenn wir mit derselben Datei arbeiten (in meinem Fall befindet sich a / im Index und b / im Arbeitsverzeichnis).
  • 10ff2df..84d4fa2 sind Blob-IDs dieser 2 Dateien
  • 100644 ist das "Modusbit", das angibt, dass dies eine reguläre Datei ist (nicht ausführbar und keine symbolische Verknüpfung).
  • --- a/file +++ b/fileMinuszeichen zeigen Linien in der a / Version, fehlen aber in der b / Version; und Pluszeichen zeigen Zeilen an, die in a / fehlen, aber in b / vorhanden sind (in meinem Fall bedeutet --- gelöschte Zeilen und +++ bedeutet hinzugefügte Zeilen in b / und dies ist die Datei im Arbeitsverzeichnis).
  • @@ -1,5 +1,5 @@Um dies zu verstehen, ist es besser, mit einer großen Datei zu arbeiten. Wenn Sie zwei Änderungen an verschiedenen Stellen haben, erhalten Sie zwei Einträge wie @@ -1,5 +1,5 @@; Angenommen, Sie haben die Datei line1 ... line100 und haben line10 gelöscht und fügen neue line100 hinzu - Sie erhalten:
@@ -7,7 +7,6 @@ line6
 line7
 line8
 line9
-this line10 to be deleted
 line11
 line12
 line13
@@ -98,3 +97,4 @@ line97
 line98
 line99
 line100
+this is new line100
irudyak
quelle
Vielen Dank. "100644 sind die Modusbits, die anzeigen, dass dies eine reguläre Datei ist (nicht ausführbar und keine symbolische Verknüpfung)". Ist "Mode Bits" ein Konzept unter Linux oder nur in Git?
Tim
@ Tim Nicht spezifisch für Git. Die rechten 3 Ziffern ( 644) sind oktal zu lesen (Werte: 1, 2, 4 bzw. eXecute-, Write- und Read-Berechtigung) und entsprechen in dieser Reihenfolge den Berechtigungen Eigentümer (Benutzer), dann Gruppe und dann Andere. Kurz gesagt 644würde dies bedeuten, wenn es symbolisch geschrieben wird u=rw,og=r, dass es für alle lesbar ist, aber nur vom Eigentümer geschrieben werden kann. Die anderen Ziffern auf der linken Seite codieren andere Informationen, z. B. wenn es sich um einen Symlink usw. handelt. Die Werte sind unter github.com/git/git/blob/… zu sehen . Die erste 1 an dieser Position ist "reguläre Datei".
Patrick Mevzek
15

Das Standardausgabeformat (das ursprünglich von einem Programm stammt, das bekannt ist, als diffob Sie nach weiteren Informationen suchen möchten) wird als "Unified Diff" bezeichnet. Es enthält im Wesentlichen 4 verschiedene Arten von Linien:

  • Kontextzeilen, die mit einem einzelnen Leerzeichen beginnen,
  • Einfügungslinien, die eine eingefügte Linie zeigen, die mit a beginnen +,
  • Löschzeilen, die mit a beginnen -, und
  • Metadatenzeilen, die übergeordnete Dinge beschreiben, z. B. um welche Datei es sich handelt, welche Optionen zum Generieren des Diff verwendet wurden, ob die Datei ihre Berechtigungen geändert hat usw.

Ich rate Ihnen, das Lesen von Unterschieden zwischen zwei Versionen einer Datei zu üben, in denen Sie genau wissen, was Sie geändert haben. So erkennen Sie genau, was los ist, wenn Sie es sehen.

Donal Fellows
quelle
5
+1: Der Vorschlag zur Übung ist sehr gut - wahrscheinlich viel schneller als der Versuch, die Dokumentation zwanghaft zu lesen.
Cascabel
6

Auf meinem Mac:

info diffWählen Sie dann: Output formats-> Context-> Unified format-> Detailed Unified:

Oder online man diff on gnu auf dem gleichen Weg zum gleichen Abschnitt:

Datei: diff.info, Knoten: Detailliertes Unified, Weiter: Beispiel Unified, Up: Unified Format

Detaillierte Beschreibung des einheitlichen Formats ......................................

Das einheitliche Ausgabeformat beginnt mit einem zweizeiligen Header, der folgendermaßen aussieht:

 --- FROM-FILE FROM-FILE-MODIFICATION-TIME
 +++ TO-FILE TO-FILE-MODIFICATION-TIME

Der Zeitstempel sieht aus wie "2002-02-21 23: 30: 39.942229878 -0800", um Datum, Uhrzeit in Sekundenbruchteilen und Zeitzone anzugeben.

Sie können den Inhalt des Headers mit der Option "--label = LABEL" ändern. siehe * Hinweis Alternative Namen ::.

Als nächstes kommen ein oder mehrere Unterschiede; Jedes Stück zeigt einen Bereich, in dem sich die Dateien unterscheiden. Unified Format Hunks sehen folgendermaßen aus:

 @@ FROM-FILE-RANGE TO-FILE-RANGE @@
  LINE-FROM-EITHER-FILE
  LINE-FROM-EITHER-FILE...

Die beiden Dateien gemeinsamen Zeilen beginnen mit einem Leerzeichen. Die Zeilen, die sich zwischen den beiden Dateien tatsächlich unterscheiden, haben eines der folgenden Kennzeichen in der linken Druckspalte:

`+ 'Hier wurde der ersten Datei eine Zeile hinzugefügt.

`- 'Hier wurde eine Zeile aus der ersten Datei entfernt.

stefanB
quelle
1
Beachten Sie, dass git den Teil 'XXX-FILE-MODIFICATION-TIME' nicht druckt, da dies für das Versionskontrollsystem nicht sinnvoll ist. Zum Vergleichen von Dateien in Dateisystemen können Zeitstempel als Versionskontrolle für "arme Männer" fungieren.
Jakub Narębski
3

Aus Ihrer Frage geht nicht hervor, welchen Teil der Unterschiede Sie verwirren: den tatsächlichen Unterschied oder die zusätzlichen Header-Informationen, die Git druckt. Nur für den Fall, hier ist eine kurze Übersicht über den Header.

Die erste Zeile ist so etwas wie diff --git a/path/to/file b/path/to/file- offensichtlich sagt sie Ihnen nur, für welche Datei dieser Abschnitt des Diff bestimmt ist. Wenn Sie die boolesche Konfigurationsvariable festlegen diff.mnemonic prefix, wird das aund bin aussagekräftigere Buchstaben wie cund w(Festschreibungs- und Arbeitsbaum) geändert .

Als nächstes gibt es "Moduszeilen" - Zeilen, in denen Sie alle Änderungen beschreiben, bei denen der Inhalt der Datei nicht geändert wird. Dies umfasst neue / gelöschte Dateien, umbenannte / kopierte Dateien und Berechtigungsänderungen.

Schließlich gibt es eine Linie wie index 789bd4..0afb621 100644. Sie werden sich wahrscheinlich nie darum kümmern, aber diese 6-stelligen Hex-Zahlen sind die abgekürzten SHA1-Hashes der alten und neuen Blobs für diese Datei (ein Blob ist ein Git-Objekt, das Rohdaten wie den Inhalt einer Datei speichert). Und natürlich 100644ist dies der Dateimodus - die letzten drei Ziffern sind offensichtlich Berechtigungen. Die ersten drei geben zusätzliche Informationen zu Dateimetadaten ( SO-Post, die dies beschreibt ).

Danach können Sie mit der einheitlichen Standard-Diff-Ausgabe fortfahren (genau wie beim Klassiker diff -U). Es ist in Hunks aufgeteilt - ein Hunk ist ein Abschnitt der Datei, der Änderungen und deren Kontext enthält. Vor jedem Hunk stehen zwei Zeilen ---und +++Zeilen, die die betreffende Datei bezeichnen. Der tatsächliche Unterschied besteht (standardmäßig) aus drei Kontextzeilen auf beiden Seiten der -und +-Zeilen, in denen die entfernten / hinzugefügten Zeilen angezeigt werden.

Cascabel
quelle
++ für die indexZeile. Bestätigt mitgit hash-object ./file
Ciro Santilli 法轮功 冠状 病 六四 事件 25