Comment lire la sortie de git diff?

La page de manuel de git-diff est plutôt longue et explique de nombreux cas qui ne semblent pas nécessaires pour un débutant. Par exemple:

 git diff origin/master 

Jetons un coup d’oeil à l’exemple de diff avancé de l’historique de git (dans commit 1088261f dans le repository git.git ):

 diff --git a/builtin-http-fetch.cb/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'parsingr ce patch ligne par ligne.

  • La première ligne

      diff --git un / builtin-http-fetch.cb / http-fetch.c 

    est un en-tête "git diff" sous la forme diff --git a/file1 b/file2 . Les noms de fichiers a a/ et b/ sont les mêmes sauf si rename / copy est impliqué (comme dans notre cas). Le - --git signifie que diff est dans le format diff "git".

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

      indice de similarité 95%
     renommer à partir de http-fetch.c
     renommer en http-fetch.c 

    Dites-nous que le fichier a été renommé de builtin-http-fetch.c en http-fetch.c et que ces deux fichiers sont identiques à 95% (ce qui a été utilisé pour détecter ce changement de nom).

    La dernière ligne de l'en-tête diff étendu, qui est

      index f3e63d7..e8f44ba 100644 

    100644 -nous du mode de fichier donné ( 100644 signifie qu'il s'agit d'un fichier ordinaire et non pas 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 modification) et postimage ( la version du fichier après modification). Cette ligne est utilisée par git am --3way pour essayer de faire une fusion à trois si patch ne peut pas être appliqué lui-même.

  • Suivant est un en-tête diff unifié à deux lignes

      --- a / builtin-http-fetch.c
     +++ b / http-fetch.c 

    Par rapport à diff -U résultat n'a pas de nom de fichier de modification de fichier ni de modification de fichier après la 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 variable de configuration diff.mnemonicPrefix sur true, à la place des préfixes a/ et b/ dans cet en-tête à deux lignes, vous pouvez avoir à la place les préfixes c/ , i/ , w/ et o/ , respectivement. voir git-config (1)

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

      @@ -1,8 +1,9 @@ 

    ou

      @@ -18,6 +19,8 @@ int cmd_http_fetch (int argc, const char ** argv, ... 

    Il est au format @@ from-file-range to-file-range @@ [header] . La plage from-file-range se présente sous la forme suivante: -, et la plage de fichiers est +, . La ligne de départ et le nombre de lignes se réfèrent respectivement à la position et à la longueur du morceau en pré-image et en post-image. 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 modification se produit, s'il s'agit d'un fichier C (comme l'option -p dans diff GNU) ou l'équivalent, le cas échéant, pour d'autres types de fichiers.

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

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

    Donc, par exemple, 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 cmd_http_fetch été remplacé par main , et que ce const char *prefix; ligne a été ajoutée.

    En d'autres termes, avant le changement, 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 le changement, ce fragment du fichier "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 en fin de fichier 

    ligne présente (il ne s'agit pas d'un exemple de diff).

Comme Donal Fellows a déclaré qu'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é.

Les références:

  • Page de manuel de git-diff (1) , section "Générer des correctifs avec -p"
  • (diff.info) Nœud unifié détaillé , "Description détaillée du format unifié".

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

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

Le format est fondamentalement le même que le diff -u unified.

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 @@ signifie:

  • -1,6 : cette pièce correspond aux lignes 1 à 6 du premier fichier:

     1 2 3 4 5 6 

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

  • +1,4 dit que cette pièce correspond aux lignes 1 à 4 du deuxième fichier.

    + 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 deuxième morceau est analogue:

  • sur l'ancien fichier, nous avons 6 lignes, en commençant à 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 11 est la 9ème ligne du nouveau fichier car nous avons déjà supprimé 2 lignes sur le morceau précédent: 2 et 3.

Hunk en-tête

Selon votre version et votre configuration 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 l’indicateur -p de 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 ligne correcte pour func1 : il a sauté les lignes 1 et 2 .

Cette fonctionnalité géniale dit souvent exactement à quelle fonction ou à quelle classe appartient chaque morceau, ce qui est très utile pour interpréter le diff.

Le fonctionnement exact de l'algorithme pour le choix de l'en-tête est expliqué ci-dessous: D'où vient l'extrait de l'en-tête git diff hunk?

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 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 avec le même fichier (dans mon cas, un / est dans l’index et b / est dans le répertoire de travail)
  • 10ff2df..84d4fa2 sont les identifiants blob de ces 2 fichiers
  • 100644 correspond aux «bits de mode», indiquant qu’il s’agit d’un fichier normal (non exécutable et non symbolique)
  • --- a/file +++ b/file signe --- a/file +++ b/file minus affiche des lignes dans la version a / mais manquant dans la version b /; et les signes plus indiquent les lignes manquantes dans un / mais présent dans b / (dans mon cas — signifie que les lignes supprimées et +++ signifient les lignes ajoutées dans b / et ceci le fichier dans le répertoire de travail)
  • @@ -1,5 +1,5 @@ afin de comprendre cela, il est préférable de travailler avec un gros fichier; Si vous avez deux modifications à différents endroits, vous obtiendrez deux entrées comme @@ -1,5 +1,5 @@ ; Supposons que vous ayez le fichier line1 … line100 et que vous supprimiez line10 et que vous ajoutiez une nouvelle ligne100 – 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 

Le format de sortie par défaut (qui provient à l’origine d’un programme appelé diff si vous souhaitez rechercher plus d’informations) est connu sous le nom de «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
  • les lignes de métadonnées qui décrivent des éléments de niveau supérieur, tels que le fichier dont il s’agit, les options utilisées pour générer le diff, si le fichier a changé ses permissions, etc.

Je vous conseille de pratiquer la lecture des différences entre deux versions d’un fichier où vous savez exactement ce que vous avez modifié. Comme ça, vous reconnaîtrez ce qui se passe quand vous le voyez.

Sur mon mac:

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

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

Fichier: diff.info, Node: Unified Detailed, Next: Exemple Unified, Up: Format unifié

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

Le format de sortie unifié commence par un en-tête à 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 une fraction de seconde et le fuseau horaire.

Vous pouvez changer le contenu de l’en-tête avec l’option `–label = LABEL ‘; voir * Note Autres noms ::.

Viennent ensuite un ou plusieurs morceaux de différences; chaque morceau montre une zone où les fichiers diffèrent. Les formats unifiés 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 espace. Les lignes qui diffèrent réellement entre les deux fichiers ont l’un des caractères d’indicateur suivants dans la colonne d’impression de gauche:

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

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

Vous ne savez pas quelle partie des diffs vous semble déroutante: le diff réellement ou les informations d’en-tête supplémentaires git impriment. Juste au cas où, voici un aperçu rapide de l’en-tête.

La première ligne est quelque chose comme diff --git a/path/to/file b/path/to/file – il est évident que cela ne fait que vous dire à quel fichier cette section est destinée. Si vous définissez le diff.mnemonic prefix variable de configuration booléenne, les diff.mnemonic prefix a et b seront remplacées par des lettres plus descriptives comme c et w (commit et arbre de travail).

Ensuite, il y a des “lignes de mode” – des lignes vous donnant une description de toutes les modifications qui n’impliquent pas de modifier le contenu du fichier. Cela inclut les fichiers nouveaux / supprimés, les fichiers renommés / copiés et les modifications d’permissions.

Enfin, il y a une ligne comme index 789bd4..0afb621 100644 . Vous ne vous en souciez probablement jamais, mais ces nombres hexadécimaux à 6 chiffres sont les hachages abrégés SHA1 des anciens et nouveaux blobs pour ce fichier (un object blob est un object git stockant des données brutes comme le contenu d’un fichier). Et bien sûr, le 100644 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 de fichiers ( message SO décrivant cela ).

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

Dans le contrôle de version, les différences entre deux versions sont présentées dans ce qu’on appelle un “diff” (ou, en synonyme, un “patch”). Jetons un coup d’oeil détaillé à un tel diff – et apprenons à le lire.

Regardez la sortie d’un diff. Sur la base de cette sortie, nous comprendrons la sortie de git diff.

entrer la description de l'image ici

Fichiers comparés a / b

Notre diff compare deux éléments entre eux: l’article A et l’élément B. Dans la plupart des cas, A et B seront le même fichier, mais dans des versions différentes. Bien qu’il ne soit pas utilisé très souvent, un diff peut également comparer deux fichiers complètement indépendants l’un de l’autre pour montrer leur différence. Pour clarifier ce qui est réellement comparé, une sortie diff commence toujours par déclarer quels fichiers sont représentés par “A” et “B”.

Métadonnées du fichier

Les métadonnées de fichier présentées ici sont des informations très techniques dont vous n’aurez probablement jamais besoin dans la pratique. Les deux premiers chiffres représentent les hachages (ou, simplement: “ID”) de nos deux fichiers: Git enregistre toutes les versions non seulement du projet mais aussi de chaque fichier en tant qu’object. Un tel hachage identifie un object fichier à une révision spécifique. Le dernier numéro est un identificateur de mode de fichier interne (100644 est juste un “fichier normal”, alors que 100755 spécifie un fichier exécutable et 120000 un lien symbolique).

Marqueurs pour a / b

Plus bas dans la sortie, les changements réels seront marqués comme venant de A ou B. Pour les distinguer, A et B se voient atsortingbuer un symbole: pour la version A, il s’agit d’un signe moins (“-“) et pour la version B, un signe plus (“+”) est utilisé.

Tronçon

Un diff ne montre pas le fichier complet du début à la fin: vous ne voudriez pas tout voir dans un fichier de 10 000 lignes, alors que seulement 2 lignes ont été modifiées. Au lieu de cela, il ne montre que les parties réellement modifiées. Une telle partie est appelée un “morceau” (ou “morceau”). En plus des lignes modifiées, un morceau contient aussi un peu de “contexte”: certaines lignes (inchangées) avant et après la modification, pour que vous puissiez mieux comprendre dans quel contexte ce changement s’est produit.

En-tête de morceau

Chacun de ces morceaux est précédé d’un en-tête. Enfermé dans deux “@” signes chacun, Git vous dit quelles lignes ont été affectées. Dans notre cas, les lignes suivantes sont représentées dans le premier morceau:

  • A partir du fichier A (représenté par un “-“), 6 lignes sont extraites, en commençant par la ligne no. 34

  • A partir du fichier B (représenté par un “+”), 8 lignes sont affichées, à partir de la ligne no. 34

Le texte après la paire de fermeture de “@@” vise à clarifier le contexte, encore une fois: Git essaie d’afficher un nom de méthode ou d’autres informations contextuelles d’où ce morceau a été pris dans le fichier. Cependant, cela dépend grandement du langage de programmation et ne fonctionne pas dans tous les scénarios.

Changements

Chaque ligne modifiée est précédée d’un symbole “+” ou “-“. Comme expliqué, ces symboles vous aident à comprendre exactement à quoi ressemblent les versions A et B: une ligne qui est précédée d’un signe “-” vient de A, tandis qu’une ligne avec un signe “+” vient de B. Dans la plupart des cas, Git choisit A et B de manière à ce que vous puissiez considérer A / – comme “ancien” contenu et B / + comme “nouveau” contenu.

Regardons notre exemple:

  • La modification n ° 1 contient deux lignes précédées d’un “+”. Comme il n’existait aucune contrepartie dans A pour ces lignes (pas de lignes avec “-“), cela signifie que ces lignes ont été ajoutées.

  • Le changement n ° 2 est exactement le contraire: en A, nous avons deux lignes marquées par des signes “-“. Cependant, B n’a pas d’équivalent (pas de lignes “+”), ce qui signifie qu’elles ont été supprimées.

  • Dans la modification n ° 3, enfin, certaines lignes ont été modifiées: les deux lignes “-” ont été modifiées pour ressembler aux deux lignes “+” ci-dessous.

La source