Comment lire la sortie de git diff?

272

La page de manuel de git-diffest assez longue et explique de nombreux cas qui ne semblent pas nécessaires pour un débutant. Par exemple:

git diff origin/master
poseid
la source
1
en utilisant un éditeur de texte différent, les notations de plage @ ... @ pour les numéros de ligne sont devenues évidentes.
poseid
Quel éditeur de texte?
Jus12

Réponses:

489

Jetons un coup d'œil à l'exemple de différence avancée de l'historique git (dans commit 1088261f dans le référentiel 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] == '-') {

Permet d'analyser ce patch ligne par ligne.

  • La première ligne

    diff --git a / builtin-http-fetch.cb / http-fetch.c
    est un en-tête "git diff" dans le formulaire diff --git a/file1 b/file2. Les noms de fichiers a/et b/sont les mêmes sauf si un changement de nom / copie est impliqué (comme dans notre cas). Le --gitest de dire que diff est dans le format diff « git ».

  • Viennent ensuite une ou plusieurs lignes d'en-tête étendues. Les trois premiers

    indice de similitude 95%
    renommer depuis builtin-http-fetch.c
    renommer en http-fetch.c
    nous dire que le fichier a été renommé builtin-http-fetch.c en http-fetch.cet que ces deux fichiers sont identiques à 95% (ce qui a été utilisé pour détecter ce renommage).

    La dernière ligne de l'en-tête diff étendu, qui est
    index f3e63d7..e8f44ba 100644
    nous parler du mode du fichier donné ( 100644signifie qu'il s'agit d'un fichier ordinaire et non par exemple d'un lien symbolique, et qu'il n'a pas de bit d'autorisation exécutable), et du hachage raccourci de la pré-image (la version du fichier avant le changement donné) et de la post-image (le version du fichier après modification). Cette ligne est utilisée par git am --3waypour essayer de faire une fusion à 3 voies si le patch ne peut pas être appliqué lui-même.

  • Vient ensuite l'en-tête de diff unifié sur deux lignes

    --- un / builtin-http-fetch.c
    +++ b / http-fetch.c
    Comparé au diff -Urésultat, il n'a pas de date de modification de fichier ni de date de modification de fichier après les noms de fichier source (préimage) et de destination (postimage). Si le fichier a été créé, la source est /dev/null; si le fichier a été supprimé, la cible est /dev/null.
    Si vous définissez la diff.mnemonicPrefixvariable de configuration true, à la place a/et b/préfixes dans cet en- tête de deux lignes , vous pouvez avoir la place c/, i/, w/et o/comme préfixes, respectivement à ce que vous comparez; voir git-config (1)

  • Viennent ensuite un ou plusieurs morceaux de différences; chaque morceau montre une zone où les fichiers diffèrent. Les morceaux au format unifié commencent par des lignes comme

    @@ -1,8 +1,9 @@
    ou
    @@ -18,6 +19,8 @@ int cmd_http_fetch (int argc, const char ** argv, ...
    C'est dans le format @@ from-file-range to-file-range @@ [header]. La plage de fichiers est sous la forme -<start line>,<number of lines>et la plage de fichiers est +<start line>,<number of lines>. La ligne de départ et le nombre de lignes font référence à la position et à la longueur du morceau dans la pré-image et la post-image, respectivement. Si le nombre de lignes n'est pas affiché, cela signifie qu'il est égal à 0.

    L'en-tête facultatif montre la fonction C où chaque changement se produit, s'il s'agit d'un fichier C (comme l' -poption dans diff GNU), ou l'équivalent, le cas échéant, pour d'autres types de fichiers.

  • Vient ensuite la description des différences entre les fichiers. Les lignes communes aux deux fichiers commencent par un caractère espace. Les lignes qui diffèrent réellement entre les deux fichiers ont l'un des caractères indicateurs suivants dans la colonne d'impression de gauche:

    • '+' - Une ligne a été ajoutée ici au premier fichier.
    • '-' - Une ligne a été supprimée ici du premier fichier.


    Ainsi, par exemple, le premier morceau

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

    signifie que cela a cmd_http_fetchété remplacé par main, et cette const char *prefix;ligne a été ajoutée.

    En d'autres termes, avant la modification, le fragment approprié du fichier 'builtin-http-fetch.c' ressemblait à ceci:

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

    Après la modification, ce fragment du fichier maintenant 'http-fetch.c' ressemble à ceci:

    #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;
    
  • Il pourrait y avoir

    \ Pas de nouvelle ligne à la fin du fichier
    ligne présente (ce n'est pas dans l'exemple diff).

Comme Donal Fellows l'a dit, il est préférable de pratiquer la lecture des différences sur des exemples réels, où vous savez ce que vous avez changé.

Références:

Jakub Narębski
la source
1
@Geremia: Git utilise des heuristiques basées sur la similitude pour la détection de renommage ... et aussi pour la détection de déplacement de code et de copie dans git blame -C -C, c'est comme ça que ça fonctionne; c'est la décision de conception de Git. Le format git diff montre juste l'index de similitude (ou dissimilarité) à l'utilisateur.
Jakub Narębski
1
@Geremia: Pour être plus exact, [header]est le précédent le plus proche comme avec le début de la fonction qui précède un morceau. Dans la plupart des cas, cette ligne inclut le nom de la fonction dans laquelle se trouve le morceau de diff. Ceci est configurable avec diffgitattribute défini sur diff driver, et diff driver incluant la xfuncnamevariable de configuration.
Jakub Narębski
1
@AnthonyGeoghegan: les lignes peuvent être supprimées (alors le nombre de lignes dans la post-image est 0), ou ajoutées (alors le nombre de lignes dans la pré-image est 0).
Jakub Narębski
1
@KasunSiyambalapitiya: Le format diff unifié que Git utilise (par opposition au format diff contextuel ^ [1]) ne fait pas de distinction entre la ligne modifiée et la ligne supprimée et ajoutée. [1]: gnu.org/software/diffutils/manual/html_node/Context-Format.html
Jakub Narębski
1
@ JakubNarębski: Le nombre de lignes par défaut est 1, pas 0. C'est aussi simple que cela. En pratique, il n'apparaît que "-1" et / ou "+1" pour les fichiers à ligne unique car il n'y a pas de contexte à afficher.
Guido Flohr
68

@@ -1,2 +3,4 @@ une partie du diff

Cette partie m'a pris du temps à comprendre, j'ai donc créé un exemple minimal.

Le format est fondamentalement le même que le diff -udiff unifié.

Par exemple:

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

Ici, nous avons supprimé les lignes 2, 3, 14 et 15. Sortie:

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

@@ -1,6 +1,4 @@ veux dire:

  • -1,6signifie que cette partie du premier fichier commence à la ligne 1 et affiche un total de 6 lignes. Il montre donc les lignes 1 à 6.

    1
    2
    3
    4
    5
    6
    

    -signifie "ancien", comme nous l'appelons habituellement comme diff -u old new.

  • +1,4signifie que cette partie du deuxième fichier commence à la ligne 1 et affiche un total de 4 lignes. Il montre donc les lignes 1 à 4.

    + signifie "nouveau".

    Nous n'avons que 4 lignes au lieu de 6 car 2 lignes ont été supprimées! Le nouveau morceau est juste:

    1
    4
    5
    6
    

@@ -11,6 +9,4 @@ pour le second morceau est analogue:

  • sur l'ancien fichier, nous avons 6 lignes, à partir de la ligne 11 de l'ancien fichier:

    11
    12
    13
    14
    15
    16
    
  • sur le nouveau fichier, nous avons 4 lignes, à partir de la ligne 9 du nouveau fichier:

    11
    12
    13
    16
    

    Notez que la ligne 11est la 9ème ligne du nouveau fichier car nous avons déjà supprimé 2 lignes sur le morceau précédent: 2 et 3.

En-tête de morceau

Selon votre version et configuration de git, vous pouvez également obtenir une ligne de code à côté de la @@ligne, par exemple func1() {dans:

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

Cela peut également être obtenu avec le -pdrapeau de la plaine diff.

Exemple: ancien fichier:

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

Si nous supprimons la ligne 6, le diff affiche:

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

Notez que ce n'est pas la bonne ligne pour func1: elle a sauté les lignes 1et 2.

Cette fonctionnalité impressionnante indique souvent exactement à quelle fonction ou classe chaque morceau appartient, ce qui est très utile pour interpréter la différence.

Comment l'algorithme pour choisir l'en-tête fonctionne exactement est discuté à: D'où provient l'extrait de l'en-tête de morceau git diff?

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
11
C'est pour tous ceux qui ne comprennent toujours pas très bien. En @@ -1,6 +1,4 @@pls, ne lisez pas en -1tant que minus oneou en +1tant que tel, plus onelisez-le comme line 1 to 6dans l'ancien (premier) fichier. Notez ici - implies "old"pas moins. BTW, merci pour la clarification ... haash.
dkjain
à partir de cela @@ -1,8 +1,9 @@ est-il possible d'interpréter ce qui s'est réellement passé. par exemple 1) une ligne a été ajoutée 2) une ligne est en cours de modification et une ligne est ajoutée et ainsi de suite. Ou est-ce d'une autre manière, car il devrait y avoir un moyen de les obtenir car git diff correclty identifie les lignes qui ont été modifiées dans le code. S'il vous plaît, aidez-moi car j'ai vraiment besoin de
résoudre ce problème
Veuillez noter qu'elle est incorrecte et très trompeuse, cette affirmation dans la réponse ci-dessus: " +1,4dit que cette pièce correspond aux lignes 1 à 4 du deuxième fichier ". En effet, +1,4peut faire référence à des lignes de contexte non contingentes. Au contraire, ce que " +1,4" signifie en réalité, c'est " qu'il y a des 4lignes (c'est-à-dire des lignes de contexte) dans cette" version "du fichier ". Il est important de comprendre la signification de la +, -et <whitespace>au début de ces lignes, car elle s'applique à l'interprétation des mecs. Un exemple plus visuel: youtube.com/watch?v=1tqMjJeyKpw
Damilola Olowookere
23

Voici l'exemple simple.

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

Voici une explication (voir les détails ici ).

  • --git n'est pas une commande, cela signifie que c'est une version git de diff (pas unix)
  • a/ b/sont des répertoires, ils ne sont pas réels. c'est juste une commodité lorsque nous traitons le même fichier (dans mon cas, a / est dans l'index et b / est dans le répertoire de travail)
  • 10ff2df..84d4fa2 sont les ID de blob de ces 2 fichiers
  • 100644 est le "mode bits", indiquant qu'il s'agit d'un fichier normal (non exécutable et non pas un lien symbolique)
  • --- a/file +++ b/fileles signes moins montrent des lignes dans la version a / mais manquantes dans la version b /; et le signe plus indique les lignes manquantes dans a / mais présentes dans b / (dans mon cas --- signifie lignes supprimées et +++ signifie lignes ajoutées dans b / et ceci le fichier dans le répertoire de travail)
  • @@ -1,5 +1,5 @@pour comprendre cela, il vaut mieux travailler avec un gros fichier; si vous avez deux changements dans des endroits différents, vous obtiendrez deux entrées comme @@ -1,5 +1,5 @@; Supposons que vous ayez le fichier line1 ... line100 et supprimé line10 et ajoutez un nouveau line100 - vous obtiendrez:
@@ -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
la source
Merci. "100644 est les bits de mode, indiquant qu'il s'agit d'un fichier normal (non exécutable et non un lien symbolique)". Les "bits de mode" sont-ils un concept sous Linux, ou simplement dans Git?
Tim
@Tim Non spécifique à git. Les 3 chiffres de droite ( 644) doivent être lus en octal (valeurs: 1, 2, 4 respectivement autorisation eXecute, Write et Read) et correspondent dans cet ordre au propriétaire (utilisateur), puis au groupe, puis aux autres autorisations. Donc, en bref, 644cela signifierait que s'il était écrit de manière symbolique u=rw,og=r, il est lisible par tout le monde mais accessible en écriture uniquement par le propriétaire. Les autres chiffres sur la gauche codent d'autres informations, comme s'il s'agit d'un lien symbolique, etc. Les valeurs peuvent être vues sur github.com/git/git/blob/… , le premier 1 dans cette position est "fichier normal".
Patrick Mevzek
15

Le format de sortie par défaut (qui provient à l'origine d'un programme connu comme diffsi vous voulez chercher plus d'informations) est connu comme un «diff unifié». Il contient essentiellement 4 types de lignes différents:

  • les lignes de contexte, qui commencent par un seul espace,
  • lignes d'insertion qui montrent une ligne qui a été insérée, qui commence par un +,
  • lignes de suppression, qui commencent par un -, et
  • des lignes de métadonnées qui décrivent des éléments de niveau supérieur comme le fichier dont il s'agit, les options qui ont été utilisées pour générer le diff, si le fichier a changé ses autorisations, etc.

Je vous conseille de vous entraîner à lire les différences entre deux versions d'un fichier où vous savez exactement ce que vous avez changé. Comme ça, vous reconnaîtrez exactement ce qui se passe quand vous le voyez.

Associés Donal
la source
5
+1: La suggestion concernant la pratique est très bonne - probablement beaucoup plus rapide que d'essayer de lire la documentation de manière obsessionnelle.
Cascabel
6

Sur mon mac:

info diffpuis sélectionnez: Output formats-> Context-> Unified format-> Detailed Unified:

Ou diff homme en ligne sur gnu suivant le même chemin vers la même section:

Fichier: diff.info, Noeud: Unified détaillé, Next: Exemple Unified, Up: Format Unified

Description détaillée du format unifié ......................................

Le format de sortie unifié commence par un en-tête de deux lignes, qui ressemble à ceci:

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

L'horodatage ressemble à `2002-02-21 23: 30: 39.942229878 -0800 'pour indiquer la date, l'heure avec des fractions de seconde et le fuseau horaire.

Vous pouvez changer le contenu de l'en-tête avec l'option `--label = LABEL '; voir * Remarque sur les noms alternatifs ::.

Viennent ensuite un ou plusieurs morceaux de différences; chaque morceau montre une zone où les fichiers diffèrent. Les mecs au format unifié ressemblent à ceci:

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

Les lignes communes aux deux fichiers commencent par un caractère espace. Les lignes qui diffèrent réellement entre les deux fichiers ont l'un des caractères indicateurs suivants dans la colonne d'impression de gauche:

`+ 'Une ligne a été ajoutée ici au premier fichier.

`- 'Une ligne a été supprimée ici du premier fichier.

stefanB
la source
1
Notez que git n'imprime pas la partie 'XXX-FILE-MODIFICATION-TIME', car cela n'a pas de sens pour le système de contrôle de version. Pour comparer des fichiers sur des horodateurs de système de fichiers, cela peut fonctionner comme un contrôle de version "pauvre".
Jakub Narębski
3

Votre question ne permet pas de savoir quelle partie des différences vous semble déroutante: la différence réelle ou les informations supplémentaires de l'en-tête que git imprime. Juste au cas où, voici un bref aperçu de l'en-tête.

La première ligne est quelque chose comme diff --git a/path/to/file b/path/to/file- évidemment, elle vous indique simplement à quel fichier cette section du diff est destinée. Si vous définissez la variable de configuration booléenne diff.mnemonic prefix, le aet bsera remplacé par des lettres plus descriptives comme cet w(validation et arborescence de travail).

Ensuite, il y a des "lignes de mode" - des lignes vous donnant une description de tout changement qui n'implique pas de changer le contenu du fichier. Cela inclut les fichiers nouveaux / supprimés, les fichiers renommés / copiés et les modifications des autorisations.

Enfin, il y a une ligne comme index 789bd4..0afb621 100644. Vous ne vous en soucierez probablement jamais, mais ces nombres hexadécimaux à 6 chiffres sont les hachages SHA1 abrégés des anciens et nouveaux blobs pour ce fichier (un blob est un objet git stockant des données brutes comme le contenu d'un fichier). Et bien sûr, 100644c'est le mode du fichier - les trois derniers chiffres sont évidemment des permissions; les trois premiers donnent des informations supplémentaires sur les métadonnées des fichiers ( SO post décrivant que ).

Après cela, vous êtes sur la sortie diff unifiée standard (tout comme le classique diff -U). Il est divisé en morceaux - un morceau est une section du fichier contenant les modifications et leur contexte. Chaque morceau est précédé d'une paire de ---et +++lignes indiquant le fichier en question, alors la comparaison est réelle (par défaut) trois lignes de contexte de chaque côté du -et des +lignes montrant les lignes supprimées / ajoutées.

Cascabel
la source
++ pour la indexligne. Confirmé pargit hash-object ./file
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功