Come leggere l'output da git diff?


271

La pagina man per git-diffè piuttosto lunga e spiega molti casi che non sembrano essere necessari per un principiante. Per esempio:

git diff origin/master

1
usando un diverso editor di testo le annotazioni di intervallo @ ... @ per i numeri di riga sono diventate ovvie.
poseid

Quale editor di testo?
Jus12

Risposte:


489

Diamo un'occhiata all'esempio della differenza avanzata dalla cronologia di git (in commit 1088261f nel repository git.git ):

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] == '-') {

Analizziamo questa patch riga per riga.

  • La prima riga

    diff --git a / builtin-http-fetch.cb / http-fetch.c
    è un'intestazione "git diff" nel modulo diff --git a/file1 b/file2. I nomi di file a/e b/sono gli stessi a meno che non sia coinvolto rinominare / copiare (come nel nostro caso). La --gitsta a significare che la diff è nel formato diff "git".

  • Successivamente ci sono una o più righe di intestazione estese. I primi tre

    indice di somiglianza 95%
    rinominare da builtin-http-fetch.c
    rinominare in http-fetch.c
    dicci che il file è stato rinominato da builtin-http-fetch.ca http-fetch.ce che quei due file sono identici al 95% (che è stato utilizzato per rilevare questa ridenominazione).

    L'ultima riga nell'intestazione diff estesa, che è
    indice f3e63d7..e8f44ba 100644
    parlaci della modalità di un determinato file ( 100644significa che si tratta di un file ordinario e non di un collegamento simbolico e che non ha un bit di autorizzazione eseguibile) e di un hash accorciato di preimage (la versione del file prima della modifica) e di postimage (il versione del file dopo la modifica). Questa riga viene utilizzata git am --3wayper provare a eseguire un'unione a 3 vie se la patch non può essere applicata da sola.

  • Il prossimo è l'intestazione diff unificata a due righe

    --- a / builtin-http-fetch.c
    +++ b / http-fetch.c
    Rispetto al diff -Urisultato, non ha nomi di file di modifica del file né di modifica del file dopo i tempi di origine (preimage) e destinazione (postimage). Se il file è stato creato, l'origine è /dev/null; se il file è stato eliminato, la destinazione è /dev/null.
    Se si imposta diff.mnemonicPrefixvariabile di configurazione true, in luogo di a/e b/prefissi in questa intestazione due linee si può avere invece c/, i/, w/e o/come prefissi, rispettivamente a ciò che si confrontano; vedi git-config (1)

  • Poi arrivano uno o più pezzi di differenze; ogni pezzo mostra un'area in cui i file differiscono. Gli hunk in formato unificato iniziano con la linea come

    @@ -1,8 +1,9 @@
    o
    @@ -18,6 +19,8 @@ int cmd_http_fetch (int argc, const char ** argv, ...
    È nel formato @@ from-file-range to-file-range @@ [header]. L'intervallo da file è nella forma -<start line>,<number of lines>e l'intervallo a file è +<start line>,<number of lines>. Sia la linea di partenza che il numero di linee si riferiscono rispettivamente alla posizione e alla lunghezza del pezzo in preimagine e postimage. Se il numero di righe non viene visualizzato significa che è 0.

    L'intestazione opzionale mostra la funzione C in cui si verifica ogni modifica, se si tratta di un file C (come l' -popzione in GNU diff), o l'eventuale equivalente per altri tipi di file.

  • Segue la descrizione di dove differiscono i file. Le linee comuni ad entrambi i file iniziano con un carattere spazio. Le righe che differiscono effettivamente tra i due file hanno uno dei seguenti caratteri indicatore nella colonna di stampa a sinistra:

    • '+' - Una riga è stata aggiunta qui al primo file.
    • '-' - Una riga è stata rimossa qui dal primo file.


    Quindi, ad esempio, il primo pezzo

     #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;
    

    significa che è cmd_http_fetchstato sostituito da maine quella const char *prefix;linea è stata aggiunta.

    In altre parole, prima della modifica, il frammento appropriato del file 'builtin-http-fetch.c' appariva così:

    #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;
    

    Dopo la modifica, questo frammento del file 'http-fetch.c' appare invece come questo:

    #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;
    
  • Ci potrebbe essere

    \ Nessuna nuova riga alla fine del file
    linea presente (non è ad esempio diff).

Come ha detto Donal Fellows , è meglio esercitarsi nella lettura di diff su esempi di vita reale, in cui si sa cosa è cambiato.

Riferimenti:


1
@Geremia: Git utilizza l'euristica basata sulla somiglianza per il rilevamento della ridenominazione ... e anche per il rilevamento dello spostamento del codice e della copia git blame -C -C, ecco come funziona; è una decisione di progettazione Git. Il formato git diff mostra solo l'indice di somiglianza (o dissomiglianza) con l'utente.
Jakub Narębski,

1
@Geremia: per essere più precisi, [header]è il precedente più vicino come con l'inizio della funzione che precede un pezzo. Nella maggior parte dei casi questa riga include il nome della funzione in cui si trova il blocco di diff. Questo è configurabile con diffgitattribute impostato su driver diff e driver diff inclusa la xfuncnamevariabile di configurazione.
Jakub Narębski,

1
@AnthonyGeoghegan: le righe potrebbero essere eliminate (quindi il numero di righe nel postimage è 0) o aggiunte (quindi il numero di righe nel preimage è 0).
Jakub Narębski,

1
@KasunSiyambalapitiya: il formato diff unificato che Git utilizza (al contrario del formato diff del contesto ^ [1]) non distingue tra linea modificata e linea rimossa e aggiunta. [1]: gnu.org/software/diffutils/manual/html_node/Context-Format.html
Jakub Narębski

1
@ JakubNarębski: il numero di righe predefinito è 1, non a 0. È così semplice. In pratica, appare solo come "-1" e / o "+1" per i file a riga singola perché non è presente alcun contesto da visualizzare.
Guido Flohr,

68

@@ -1,2 +3,4 @@ parte del diff

Questa parte mi ha richiesto un po 'di tempo per capire, quindi ho creato un esempio minimo.

Il formato è sostanzialmente lo stesso del diff -udiff unificato.

Per esempio:

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

Qui abbiamo rimosso le righe 2, 3, 14 e 15. Output:

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

@@ -1,6 +1,4 @@ si intende:

  • -1,6significa che questo pezzo del primo file inizia alla riga 1 e mostra un totale di 6 righe. Pertanto mostra le righe da 1 a 6.

    1
    2
    3
    4
    5
    6
    

    -significa "vecchio", come di solito lo invochiamo come diff -u old new.

  • +1,4significa che questo pezzo del secondo file inizia alla riga 1 e mostra un totale di 4 righe. Pertanto mostra le righe da 1 a 4.

    + significa "nuovo".

    Abbiamo solo 4 righe anziché 6 perché sono state rimosse 2 righe! Il nuovo pezzo è solo:

    1
    4
    5
    6
    

@@ -11,6 +9,4 @@ per il secondo pezzo è analogo:

  • sul vecchio file, abbiamo 6 righe, a partire dalla riga 11 del vecchio file:

    11
    12
    13
    14
    15
    16
    
  • sul nuovo file, abbiamo 4 righe, a partire dalla riga 9 del nuovo file:

    11
    12
    13
    16
    

    Nota che la riga 11è la nona riga del nuovo file perché abbiamo già rimosso 2 righe nel precedente hunk: 2 e 3.

Intestazione Hunk

A seconda della versione e della configurazione di git, puoi anche ottenere una riga di codice accanto alla @@riga, ad es. func1() {In:

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

Questo può essere ottenuto anche con la -pbandiera della pianura diff.

Esempio: vecchio file:

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

Se rimuoviamo la linea 6, il diff mostra:

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

Nota che questa non è la linea corretta per func1: ha saltato le linee 1e 2.

Questa fantastica funzionalità spesso dice esattamente a quale funzione o classe appartiene ciascun pezzo, il che è molto utile per interpretare il diff.

Il modo in cui l'algoritmo per scegliere l'intestazione funziona esattamente è discusso in: Da dove viene l'estratto nell'intestazione hunk git diff?


11
Questo è per chiunque non abbia ancora capito bene. In @@ -1,6 +1,4 @@pls non leggere -1come minus oneo +1come plus oneinvece leggi questo come line 1 to 6nel vecchio (primo) file. Nota qui - implies "old"non meno. A proposito, grazie per il chiarimento ... haash.
dkjain,

da questo @@ -1,8 +1,9 @@ è possibile interpretare ciò che è realmente accaduto. per esempio 1) una riga è stata aggiunta 2) una riga è stata modificata e una riga è stata aggiunta e così via. O è da un altro modo, in quanto dovrebbe esserci un modo per ottenerli poiché git diff identecleness identifica quali righe sono state modificate nel codice. Ti prego, aiutami perché ho davvero bisogno di risolvere questo problema
Kasun Siyambalapitiya,

Si noti che è errato e molto fuorviante, questa affermazione nella risposta sopra: " +1,4dice che questo pezzo corrisponde alle righe da 1 a 4 del secondo file ". Questo perché +1,4può riferirsi a linee di contesto non contingenti. Piuttosto, ciò che " +1,4" significa in realtà è che " ci sono 4linee (cioè linee di contesto) in quella" versione "del file ". E 'importante capire il significato della +, -e <whitespace>all'inizio di quelle linee, in quanto si applica l'interpretazione di hunk. Un esempio più visivo: youtube.com/watch?v=1tqMjJeyKpw
Damilola Olowookere

23

Ecco il semplice esempio.

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

Ecco una spiegazione (vedi dettagli qui ).

  • --git non è un comando, questo significa che è una versione git di diff (non unix)
  • a/ b/sono directory, non sono reali. è solo una comodità quando trattiamo lo stesso file (nel mio caso a / è in indice e b / è nella directory di lavoro)
  • 10ff2df..84d4fa2 sono gli ID BLOB di questi 2 file
  • 100644 sono i "bit di modalità", che indicano che si tratta di un file normale (non eseguibile e non un collegamento simbolico)
  • --- a/file +++ b/filei segni meno mostrano le linee nella versione a / ma mancano nella versione b /; e i segni più mostrano le righe mancanti in a / ma presenti in b / (nel mio caso --- significa righe eliminate e +++ significa righe aggiunte in b / e questo è il file nella directory di lavoro)
  • @@ -1,5 +1,5 @@per capirlo è meglio lavorare con un file di grandi dimensioni; se hai due modifiche in luoghi diversi otterrai due voci simili @@ -1,5 +1,5 @@; supponiamo di avere il file line1 ... line100 e cancellato line10 e aggiungere nuova line100 - otterrai:
@@ -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

Grazie. "100644 sono i bit della modalità, a indicare che si tratta di un file normale (non eseguibile e non un collegamento simbolico)". "Bit di modalità" è un concetto in Linux o semplicemente in Git?
Tim

@Tim Non specifico per git. Le 3 cifre giuste ( 644) devono essere lette in ottale (valori: 1, 2, 4 rispettivamente autorizzazione eXecute, Write e Read) e corrispondono in quell'ordine al proprietario (utente), quindi al gruppo, quindi ad altre autorizzazioni. Quindi in breve 644significherebbe se scritto simbolicamente u=rw,og=r, è leggibile da tutti ma scrivibile solo dal proprietario. Le altre cifre a sinistra codificano altre informazioni, ad esempio se si tratta di un collegamento simbolico, ecc. I valori possono essere visualizzati github.com/git/git/blob/… , il primo 1 in questa posizione è "file normale".
Patrick Mevzek,

15

Il formato di output predefinito (che originariamente proviene da un programma noto come diffse si desidera cercare ulteriori informazioni) è noto come "diff unificato". Contiene essenzialmente 4 diversi tipi di linee:

  • linee di contesto, che iniziano con un unico spazio,
  • linee di inserimento che mostrano una linea che è stata inserita, che iniziano con un +,
  • righe di eliminazione, che iniziano con a -e
  • linee di metadati che descrivono cose di livello superiore come il file di cui sta parlando, quali opzioni sono state utilizzate per generare il diff, se il file ha cambiato le sue autorizzazioni, ecc.

Ti consiglio di esercitarti a leggere le differenze tra due versioni di un file in cui sai esattamente cosa hai cambiato. In questo modo riconoscerai esattamente cosa sta succedendo quando lo vedi.


5
+1: Il suggerimento sulla pratica è molto valido, probabilmente molto più veloce del tentativo di leggere ossessivamente la documentazione.
Cascabel,

6

Sul mio mac:

info diffquindi selezionare: Output formats-> Context-> Unified format-> Detailed Unified:

Oppure man online diff su gnu seguendo lo stesso percorso per la stessa sezione:

File: diff.info, nodo: dettagliato unificato, successivo: esempio unificato, su: formato unificato

Descrizione dettagliata del formato unificato ......................................

Il formato di output unificato inizia con un'intestazione a due righe, che assomiglia a questo:

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

Il timestamp appare come "2002-02-21 23: 30: 39.942229878 -0800" per indicare la data, l'ora con i secondi frazionari e il fuso orario.

Puoi cambiare il contenuto dell'intestazione con l'opzione `--label = LABEL '; vedi * Nota Nomi alternativi ::.

Poi arrivano uno o più pezzi di differenze; ogni pezzo mostra un'area in cui i file differiscono. Gli hunk in formato unificato si presentano così:

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

Le linee comuni ad entrambi i file iniziano con un carattere spazio. Le righe che differiscono effettivamente tra i due file hanno uno dei seguenti caratteri indicatore nella colonna di stampa a sinistra:

`+ 'Una riga è stata aggiunta qui al primo file.

`- 'Una riga è stata rimossa qui dal primo file.


1
Notare che git non stampa la parte 'XXX-FILE-MODIFICATION-TIME', poiché non ha senso per il sistema di controllo della versione. Per confrontare i file su file system i timestam possono funzionare come controllo versione "uomo povero".
Jakub Narębski,

3

Non è chiaro dalla tua domanda quale parte delle differenze trovi confusa: le informazioni di intestazione effettivamente diff o l'intestazione extra vengono stampate. Per ogni evenienza, ecco una rapida panoramica dell'intestazione.

La prima riga è qualcosa del genere diff --git a/path/to/file b/path/to/file- ovviamente ti sta solo dicendo a quale file è destinata questa sezione del diff. Se si imposta la variabile di configurazione booleana diff.mnemonic prefix, ae bverrà modificato in lettere più descrittive come ce w(commit e albero di lavoro).

Successivamente, ci sono "righe di modalità": righe che forniscono una descrizione di eventuali modifiche che non comportano la modifica del contenuto del file. Ciò include file nuovi / eliminati, file rinominati / copiati e modifiche alle autorizzazioni.

Infine, c'è una linea come index 789bd4..0afb621 100644. Probabilmente non ti interesserà mai, ma quei numeri esadecimali a 6 cifre sono gli hash SHA1 abbreviati dei vecchi e nuovi BLOB per questo file (un BLOB è un oggetto git che memorizza dati grezzi come il contenuto di un file). E, naturalmente, 100644è la modalità del file: le ultime tre cifre sono ovviamente autorizzazioni; i primi tre forniscono ulteriori informazioni sui metadati del file ( post SO che lo descrive ).

Successivamente, si passa all'output diff unificato standard (proprio come il classico diff -U). È suddiviso in blocchi: un blocco è una sezione del file che contiene le modifiche e il loro contesto. Ogni pezzo è preceduto da una coppia di ---e +++linee che indicano il file in questione, quindi la differenza effettiva è (per impostazione predefinita) tre linee di contesto su entrambi i lati delle linee -e +che mostrano le linee rimosse / aggiunte.


++ per la indexlinea. Confermato congit hash-object ./file
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功 il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.