Cómo leer la salida de git diff?


La página de manual de git-diff es bastante larga, y explica muchos casos que no parecen ser necesarios para un principiante. Por ejemplo:

git diff origin/master
Author: user3207158, 2010-03-27

7 answers

Echemos un vistazo al ejemplo de diferencias avanzadas del historial de git (en commit 1088261f en git.repositorio 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] == '-') {

Analicemos este parche línea por línea.

  • La primera línea

    diff --git a/builtin-http-fetch.c b/http-fetch.c
    es un encabezado "git diff" en la forma diff --git a/file1 b/file2. Los nombres de archivo a/ y b/ son los mismos a menos que esté involucrado rename/copy (como en nuestro caso). --git quiere decir que diff está en el formato "git" diff.
  • A continuación se encuentran uno o más encabezados extendidos alinear. Los tres primeros

    similarity index 95%
    rename from builtin-http-fetch.c
    rename to http-fetch.c
    nos dicen que el archivo fue renombrado de builtin-http-fetch.c a http-fetch.c y que esos dos archivos son 95% idénticos (que se utilizó para detectar este cambio de nombre).

    La última línea en el encabezado diff extendido, que es
    index f3e63d7..e8f44ba 100644
    nos dice sobre el modo del archivo dado (100644 significa que es un archivo ordinario y no, por ejemplo, un enlace simbólico, y que no tiene un bit de permiso ejecutable), y sobre el hash acortado de preimage (la versión del archivo antes del cambio dado) y postimage (la versión archivo después del cambio). Esta línea es utilizada por git am --3way para intentar hacer una fusión de 3 vías si el parche no se puede aplicar por sí mismo.

  • El siguiente es el encabezado diff unificado de dos líneas

    --- a/builtin-http-fetch.c
    +++ b/http-fetch.c
    Comparado con diff -U resultado no tiene from-file-modification-time ni to-file-modification-time después de los nombres de archivo fuente (preimage) y destino (postimage). Si se creó el archivo, el origen es /dev/null; si se eliminó el archivo, el destino es /dev/null.
    Si establece la variable de configuración diff.mnemonicPrefix a true, en lugar de a/ y b/ prefijos en este encabezado de dos líneas que puede tener en su lugar c/, i/, w/ y o/ como prefijos, respectivamente a lo que comparas; ver git-config (1)
  • Luego vienen uno o más trozos de diferencias; cada trozo muestra un área donde los archivos difieren. Hunks formato unificado comienza con la línea como

    @@ -1,8 +1,9 @@
    o
    @@ -18,6 +19,8 @@ int cmd_http_fetch(int argc, const char **argv, ...
    Está en el formato @@ from-file-range to-file-range @@ [header]. El rango de archivo de origen está en la forma -<start line>,<number of lines>, y el rango de archivo de destino es +<start line>,<number of lines>. Tanto la línea de salida como el número de líneas se refiere a la posición y longitud del trozo en preimagen y postimage, respectivamente. Si no se muestra el número de líneas, significa que es 0.

    El encabezado opcional muestra la función C donde se produce cada cambio, si es un archivo C (como la opción -p en GNU diff), o el equivalente, si lo hay, para otros tipos de archivos.

  • Luego viene la descripción de dónde difieren los archivos. Las líneas comunes a ambos archivos comienzan con un carácter de espacio. Las líneas que realmente difieren entre los dos archivos tienen uno de los siguientes caracteres indicadores en la columna de impresión de la izquierda:

    • '+' was Se añadió aquí una línea al primer archivo.
    • ' -' here Aquí se eliminó una línea del primer archivo.


    Así, por ejemplo, first chunk

     #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 que cmd_http_fetch fue reemplazado por main, y que se agregó la línea const char *prefix;.

    En otras palabras, antes del cambio, el fragmento apropiado de then 'builtin-http-fetch.c' archivo buscado así:

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

    Después del cambio este fragmento de ahora 'http-fetch.el archivo c ' se ve así:

    #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;
    
  • Podría haber {[39]]}

    \ No newline at end of file
    línea presente (no está en el ejemplo diff).

Como los becarios Donales dijeron es mejor practicar la lectura de diferencias en ejemplos de la vida real, donde sabes lo que has cambiado.

Referencias:

 420
Author: Jakub Narębski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:26:23

@@ -1,2 +3,4 @@ parte de la diferencia

Esta parte me llevó un tiempo entenderla, así que he creado un ejemplo mínimo.

El formato es básicamente el mismo que el diff unificado diff -u.

Por ejemplo:

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

Aquí eliminamos las líneas 2, 3, 14 y 15. Salida:

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

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

  • -1,6: esta pieza corresponde a la línea 1 a 6 del primer archivo:

    1
    2
    3
    4
    5
    6
    

    - significa "viejo", como solemos invocarlo como diff -u old new.

  • +1,4 dice que esta pieza corresponde a la línea 1 a 4 del segundo archivo.

    + significa "nuevo".

    ¡Solo tenemos 4 líneas en lugar de 6 porque se eliminaron 2 líneas! El nuevo pedazo es simplemente:

    1
    4
    5
    6
    

@@ -11,6 +9,4 @@ para el segundo pedazo es análogo:

  • En el archivo antiguo, tenemos 6 líneas, comenzando en la línea 11 del archivo antiguo:

    11
    12
    13
    14
    15
    16
    
  • En el nuevo archivo, tenemos 4 líneas, comenzando en la línea 9 del nuevo archivo:

    11
    12
    13
    16
    

    Tenga en cuenta que la línea 11 es la 9ª línea del nuevo archivo porque ya hemos eliminado 2 líneas en el trozo anterior: 2 y 3.

Cabecera Hunk

Dependiendo de su versión y configuración de git, también puede obtener una línea de código junto a la línea @@, por ejemplo, la func1() { en:

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

Esto también se puede obtener con la bandera -p de plain diff.

Ejemplo: archivo antiguo:

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

Si eliminamos línea 6, la diferencia muestra:

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

Tenga en cuenta que esta no es la línea correcta para func1: se saltó las líneas 1 y 2.

Esta característica impresionante a menudo dice exactamente a qué función o clase pertenece cada pedazo, lo que es muy útil para interpretar la diferencia.

Cómo funciona exactamente el algoritmo para elegir el encabezado se discute en: ¿De dónde viene el extracto en el encabezado git diff hunk?

 49
Author: Ciro Santilli 新疆改造中心 六四事件 法轮功,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:10:28

Aquí está el ejemplo 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

Aquí hay una explicación (ver detalles aquí).

  • --git no es un comando, esto significa que es una versión git de diff (no unix)
  • a/ b/ son directorios, no son reales. es solo una conveniencia cuando tratamos con el mismo archivo (en mi caso a/ está en el índice y b/ está en el directorio de trabajo)
  • 10ff2df..84d4fa2 son identificadores de blob de estos 2 archivos
  • 100644 son los "bits de modo", lo que indica que este es un archivo normal (no ejecutable y no un enlace simbólico)
  • --- a/file +++ b/file los signos menos muestran líneas en la versión a/ pero que faltan en la versión b/; y los signos más muestran líneas que faltan en a/ pero presentes en b / (en mi caso --- significa líneas eliminadas y +++ significa líneas agregadas en b/ y este es el archivo en el directorio de trabajo)
  • @@ -1,5 +1,5 @@ para entender esto es mejor trabajar con un archivo grande; si tienes dos cambios en diferentes lugares obtendrás dos entradas como @@ -1,5 +1,5 @@; supongamos que tienes línea de archivo 1 ... line100 y eliminado line10 y añadir nueva line100-obtendrá:
@@ -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
 20
Author: irudyak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-19 11:35:07

El formato de salida predeterminado (que originalmente proviene de un programa conocido como diff si desea buscar más información) se conoce como "diferencia unificada". Contiene esencialmente 4 tipos diferentes de líneas:

  • líneas de contexto, que comienzan con un solo espacio,
  • líneas de inserción que muestran una línea que se ha insertado, que comienzan con un +,
  • líneas de eliminación, que comienzan con un -, y
  • líneas de metadatos que describen cosas de nivel superior como qué archivo está hablando de, qué opciones se utilizaron para generar el diff, si el archivo cambió sus permisos, etc.

Le aconsejo que practique la lectura de diferencias entre dos versiones de un archivo donde sepa exactamente lo que cambió. Así reconocerás lo que está pasando cuando lo veas.

 12
Author: Donal Fellows,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-27 14:33:23

En mi mac:

info diff a continuación, seleccione: Output formats -> Context -> Unified format -> Detailed Unified :

O online man diff {[13] } en gnu siguiendo el mismo camino a la misma sección:

Archivo: diff.info, Nodo: Detallado Unificado, Siguiente: Ejemplo unificado, Subir: Formato Unificado

Descripción Detallada del Formato Unificado ......................................

Se inicia el formato de salida unificado con un encabezado de dos líneas, que se ve así:

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

La marca de tiempo parece `2002-02-21 23:30:39.942229878 -0800' para indicar la fecha, la hora con fracción segundos y zona horaria.

Puede cambiar el contenido del encabezado con la opción 'label label=LABEL'; ver * Note Alternate Names::.

Luego vienen uno o más trozos de diferencias; cada pedazo muestra un área donde los archivos difieren. Unificar los trozos de formato se ven así:

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

Las líneas comunes a ambos archivos comienza con un personaje espacial. El líneas que realmente difieren entre los dos archivos tienen uno de los siguientes caracteres del indicador en la impresión izquierda columna:

`+' Se agregó una línea aquí al primer archivo.

`-' Aquí se eliminó una línea del primer archivo.

 6
Author: stefanB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-27 14:23:36

No está claro a partir de su pregunta qué parte de las diferencias encuentra confusa: la diferencia real o la información de encabezado adicional que imprime git. Por si acaso, aquí hay un resumen rápido del encabezado.

La primera línea es algo así como diff --git a/path/to/file b/path/to/file - obviamente solo te está diciendo para qué archivo es esta sección del diff. Si establece la variable de configuración booleana diff.mnemonic prefix, las a y b se cambiarán a letras más descriptivas como c y w (confirmar y trabajar arbol).

A continuación, hay "líneas de modo" - líneas que le dan una descripción de cualquier cambio que no implique cambiar el contenido del archivo. Esto incluye archivos nuevos / eliminados, archivos renombrados / copiados y cambios de permisos.

Finalmente, hay una línea como index 789bd4..0afb621 100644. Probablemente nunca te importará, pero esos números hexadecimales de 6 dígitos son los hashes SHA1 abreviados de los blobs antiguos y nuevos para este archivo (un blob es un objeto git que almacena datos sin procesar como el contenido de un archivo). Y por supuesto, el 100644 es el modo del archivo-los últimos tres dígitos son obviamente permisos; los tres primeros dan información extra de metadatos del archivo (ASÍ que post describiendo eso).

Después de eso, estás en la salida de diff unificada estándar (al igual que el clásico diff -U). Se divide en trozos-un trozo es una sección del archivo que contiene los cambios y su contexto. Cada trozo está precedido por un par de líneas --- y +++ que denotan el archivo en cuestión, entonces la diferencia real es (por defecto) tres líneas de contexto a ambos lados de las líneas - y + que muestran las líneas eliminadas/añadidas.

 3
Author: Cascabel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:18:15

En el control de versiones, las diferencias entre dos versiones se presentan en lo que se llama un "diff" (o, sinónimo, un "parche"). Echemos un vistazo detallado a tal diferencia y aprendamos a leerla.

Mira la salida de un diff. Basándonos en esta salida entenderemos la salida git diff.

introduzca la descripción de la imagen aquí

Archivos comparados a / b

Nuestra diferencia compara dos elementos entre sí: elemento A y elemento B. En la mayoría de los casos, A y B serán el mismo archivo, pero en diferentes versiones. Aunque no se usa muy a menudo, un diff también podría comparar dos archivos completamente no relacionados entre sí para mostrar cómo difieren. Para dejar claro lo que realmente se compara, una salida diff siempre comienza declarando qué archivos están representados por "A"y " B".

Metadatos del archivo

Los metadatos del archivo que se muestran aquí son una información muy técnica que probablemente nunca necesitará en la práctica. Los dos primeros números representan los hashes (o, en pocas palabras: "IDs") de nuestros dos archivos: Git guarda cada versión no solo del proyecto sino también de cada archivo como un objeto. Este hash identifica un objeto de archivo en una revisión específica. El último número es un identificador interno de modo de archivo (100644 es solo un "archivo normal", mientras que 100755 especifica un archivo ejecutable y 120000 representa un enlace simbólico).

Marcadores para a/b

Más abajo en la salida, los cambios reales se marcarán como procedentes de A o B. Para diferenciarlos, A y B a cada uno se le asigna un símbolo: para la versión A, este es un signo menos ("-") y para la versión B, se usa un signo más ("+").

Chunk

Una diferencia no muestra el archivo completo de principio a fin: no querrá ver todo en un archivo de 10.000 líneas, cuando solo 2 líneas han cambiado. En su lugar, solo muestra las porciones que realmente se modificaron. Tal porción se llama un "pedazo"(o "pedazo"). Además de las líneas reales cambiadas, un trozo también contiene un poco de "contexto": algunas líneas (sin cambios) antes y después de la modificación para que pueda comprender mejor en qué contexto ocurrió ese cambio.

Cabecera de fragmento

Cada uno de estos trozos está precedido por un encabezado. Encerrado en dos signos " @ " cada uno, Git te dice qué líneas fueron afectadas. En nuestro caso, las siguientes líneas están representadas en el primer fragmento:

  • Del archivo A (representado por un " -"), se extraen 6 líneas, comenzando por la línea no. 34

  • Desde el archivo B (representado por un"+"), se muestran 8 líneas, también a partir de la línea no. 34

El texto después del par de cierre de "@@" tiene como objetivo aclarar el contexto, de nuevo: Git intenta mostrar un nombre de método u otra información contextual de dónde se tomó este fragmento en el archivo. Sin embargo, esto depende en gran medida del lenguaje de programación y no funciona en todos los escenarios.

Cambios

Cada línea cambiada se antepone con un símbolo " + "o" -". Como se explicó, estos símbolos le ayudan a entender exactamente cómo se ven las versiones A y B: una línea que se antepone con un signo "-" proviene de A, mientras que una línea con un signo "+" proviene de B. En la mayoría de los casos, Git elige A y B de tal manera que puedes pensar en A/- como contenido "antiguo" y B/+ como contenido "nuevo".

Veamos nuestro ejemplo:

  • El cambio # 1 contiene dos líneas precedidas por un"+". Dado que no existía contraparte en A para estas líneas (no líneas con" -"), esto significa que estas líneas fueron añadidas.

  • El cambio # 2 es justo lo contrario: en A, tenemos dos líneas marcadas con signos" -". Sin embargo, B no tiene un equivalente (sin líneas"+"), lo que significa que se eliminaron.

  • En el cambio #3, finalmente, algunas líneas fueron modificadas: las dos líneas " - "fueron cambiadas para parecerse a las dos líneas" + " de abajo.

Fuente

 2
Author: Breaking Benjamin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-19 13:12:21