web-dev-qa-db-fra.com

Comment lire le résultat de git diff?

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

git diff Origin/master
248
poseid

Jetons un coup d'œil à l'exemple de diff avancé 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.c b/http-fetch.c
    est un en-tête "git diff" sous la forme _diff --git a/file1 b/file2_. Les noms de fichiers _a/_ et _b/_ sont identiques sauf si rename/copy est impliqué (comme dans notre cas). _--git_ signifie que diff est au format diff "git".
  • Viennent ensuite une ou plusieurs lignes d’en-tête étendues. Les trois premiers

    indice de similarité 95% 
     renommer de builtin-http-fetch.c 
     renommer en http-fetch.c
    nous indique que le fichier a été renommé de _builtin-http-fetch.c_ à _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 de diff étendu, qui est
    index f3e63d7..e8f44ba 100644
    Parlez-nous du mode d’un fichier donné (_100644_ signifie 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), ainsi que du hachage raccourci de la pré-image (la version de fichier avant modification donnée) et postimage (la version de fichier après modification). _git am --3way_ utilise cette ligne pour essayer d'effectuer une fusion à trois si le correctif ne peut pas être appliqué.

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

    --- a/builtin-http-fetch.c 
     +++ b/http-fetch.c
    Comparé au résultat _diff -U_, il ne possède pas de nom de fichier depuis le fichier, ni le temps de modification du fichier après le fichier source (pré-image) ni le fichier 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, les préfixes _a/_ et _b/_ de cet en-tête de deux lignes peuvent être remplacés par _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 mecs au format unifié commencent par une ligne 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 depuis le fichier est sous la forme _-<start line>,<number of lines>_ et la plage vers le fichier 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 gros morceau dans les images pré-image et post-image, respectivement. Si le nombre de lignes n'est pas affiché, cela signifie qu'il est 0.

    L'en-tête facultatif indique la fonction C où chaque modification est effectuée, s'il s'agit d'un fichier C (comme l'option _-p_ dans GNU diff), ou son é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 caractère d'espacement. 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 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 _cmd_http_fetch_ a été remplacé par main et que la ligne _const char *prefix;_ a été ajoutée.

    En d'autres termes, avant la modification, le fragment approprié du fichier 'builtin-http-fetch.c' ressemblait alors à 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 'http-fetch.c' maintenant 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 a déclaré Donal Fellows , il est préférable d’exercer à la lecture de diffs sur des exemples réels, où vous savez ce que vous avez changé.

Références:

455
Jakub Narębski

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

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

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

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. Résultat:

@@ -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 signifie que cette partie du premier fichier commence à la ligne 1 et affiche un total de 6 lignes. Par conséquent, il montre les lignes 1 à 6.

    1
    2
    3
    4
    5
    6
    

    - signifie "ancien", comme nous l'appelons généralement comme diff -u old new.

  • +1,4 signifie que cette partie du deuxième fichier commence à la ligne 1 et affiche un total de 4 lignes. Par conséquent, il montre 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 deuxième morceau est analogue:

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

    11
    12
    13
    14
    15
    16
    
  • sur le nouveau fichier, nous avons 4 lignes, commençant à 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 bloc précédent: 2 et 3.

En-tête de morceau

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

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

Ceci peut également être obtenu avec le drapeau -p de plain diff.

Exemple: ancien fichier:

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

Si nous supprimons la ligne 6, le diff indique:

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

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

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

Le fonctionnement exact de l'algorithme pour choisir l'en-tête est décrit à l'adresse suivante: D'où provient 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é quand nous traitons le même fichier (dans mon cas, un/est dans l'index et b/est dans un répertoire de travail)
  • 10ff2df..84d4fa2 sont les identifiants 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 symbolique)
  • --- a/file +++ b/file signes moins montre des lignes dans la version a/mais absentes de la version b /; et des signes plus indique des lignes manquantes dans un/mais présentes dans b/(dans mon cas, les lignes supprimées et +++, les lignes ajoutées dans b/et le fichier dans le répertoire de travail)
  • @@ -1,5 +1,5 @@ pour comprendre cela, il est préférable de travailler avec un gros fichier; si vous avez deux changements à des endroits différents, vous aurez deux entrées comme @@ -1,5 +1,5 @@; supposons que vous ayez le fichier line1 ... line100 et line10 supprimé et que vous ajoutiez le 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
21
irudyak

Le format de sortie par défaut (qui provient à l'origine d'un programme appelé diff si vous souhaitez rechercher plus d'informations) est appelé "diff unifié". Il contient essentiellement 4 types de lignes différents:

  • lignes de contexte, qui commencent par un seul espace,
  • les lignes d'insertion qui montrent une ligne qui a été insérée et qui commencent par +,
  • lignes de suppression commençant par -, et
  • les lignes de métadonnées décrivant des choses de plus haut niveau, comme le fichier dont il est question, les options utilisées pour générer le diff, si le fichier a changé ses autorisations, etc.

Je vous conseille de vous exercer à lire les 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.

14
Donal Fellows

Sur mon mac:

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

Ou diff. Homme en ligne sur GNU en suivant le même chemin vers la même section:

Fichier: diff.info, Nœud: Détaillé Unifié, Suivant: Exemple Unifié, Haut: Format Unifié

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 se présente comme suit: "2002-02-21 23: 30: 39.942229878-0800" pour indiquer la date, l’heure en secondes et le fuseau horaire.

Vous pouvez changer le contenu de l'en-tête avec l'option `--label = LABEL '; voir * Remarque 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 d'espacement. 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 ici du premier fichier.

6
stefanB

Dans le contrôle de version, les différences entre deux versions sont présentées dans ce qu'on appelle un "diff" (ou, de manière synonyme, un "patch"). Jetons un regard détaillé sur 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.

enter image description here

Fichiers comparés a/b

Notre diff compare deux éléments: l'élément 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 que peu utilisé, un diff peut également comparer deux fichiers totalement non liés entre eux pour montrer en quoi ils diffèrent. 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 de fichier

Les métadonnées de fichier présentées ici constituent une information très technique dont vous n’aurez probablement jamais besoin dans la pratique. Les deux premiers nombres représentent les hachages (ou, simplement: "ID") de nos deux fichiers: Git enregistre chaque version non seulement du projet mais également de chaque fichier sous forme d'objet. Un tel hachage identifie un objet fichier lors d'une révision spécifique. Le dernier numéro est un identifiant de mode de fichier interne (100644 est simplement un "fichier normal", alors que 100755 spécifie un fichier exécutable et 120000 représente un lien symbolique).

Marqueurs pour a/b

Plus bas dans la sortie, les modifications effectives seront marquées comme venant de A ou B. Pour les différencier, A et B se voient attribuer chacun un symbole: pour la version A, il s'agit du signe moins ("-") et pour la version B, un signe plus ("+") est utilisé.

Chunk

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, lorsque seulement 2 lignes ont été modifiées. Au lieu de cela, il ne montre que les parties qui ont été réellement modifiées. Une telle partie s'appelle un "morceau" (ou "morceau"). En plus des lignes modifiées, un bloc contient également un peu de "contexte": des lignes (inchangées) avant et après la modification afin que vous puissiez mieux comprendre dans quel contexte s'est produite la modification.

En-tête de bloc

Chacun de ces morceaux est précédé d’un en-tête, placé entre deux marqueurs @@. Git utilise l'en-tête pour vous dire quelles lignes ont été affectées. Dans notre cas, les lignes suivantes sont représentées dans le premier bloc:

  • A partir du fichier A (représenté par un "-"), 6 lignes sont extraites à partir de la ligne no. 34

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

Le texte après la dernière paire de "@@" vise à clarifier le contexte, encore une fois: Git essaie d'afficher un nom de méthode ou une autre information contextuelle indiquant d'où provient ce bloc dans le fichier. Toutefois, cela dépend grandement du langage de programmation et ne fonctionne pas dans tous les scénarios.

Modifications

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

Regardons notre exemple:

  • Le changement n ° 1 contient deux lignes précédées d'un "+". Puisqu'aucune contrepartie en A n'existait pour ces lignes (aucune ligne avec "-"), cela signifie que ces lignes ont été ajoutées.

  • Le changement n ° 2 est tout le contraire: en A, nous avons deux lignes signalé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.

Source

5
Breaking Benjamin

Votre question ne permet pas de savoir quelle partie des diffs vous trouvez déroutante: les diffs réellement ou les informations supplémentaires d’en-tête git sont imprimées. 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 s'agit évidemment de vous dire à quel fichier cette section du fichier diff est destinée. Si vous définissez la variable booléenne de configuration diff.mnemonic prefix, les variables a et b seront remplacées par des lettres plus descriptives telles que c et w (commit et work tree) .

Ensuite, il y a des "lignes de mode" - des lignes qui décrivent toutes les modifications qui n'impliquent pas de changer le contenu du fichier. Cela inclut les nouveaux fichiers/supprimés, les fichiers renommés/copiés et les modifications d’autorisations.

Enfin, il y a une ligne comme index 789bd4..0afb621 100644. Vous n'allez probablement jamais vous en soucier, mais ces nombres hexadécimaux à 6 chiffres sont les hachages SHA1 abrégés de l'ancien et du nouveau 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, 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 fichier ( SO poste décrivant cela ).

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

3
Cascabel