¿Cómo leer el resultado de git diff?

Resuelto poseid asked hace 14 años • 6 respuestas

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

git diff origin/master
poseid avatar Mar 27 '10 20:03 poseid
Aceptado

Echemos un vistazo a un ejemplo de diferenciación avanzada del historial de git (en la confirmación 1088261f en el repositorio 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] == '-') {

Analicemos este parche línea por línea.

  • la primera linea

    diferencia --git a/builtin-http-fetch.cb/http-fetch.c
    es un encabezado "git diff" en el formulario diff --git a/file1 b/file2. Los nombres de archivo a/y b/son los mismos a menos que se trate de cambiar el nombre/copiar (como en nuestro caso). Esto --gitquiere decir que diff está en el formato diff "git".

  • A continuación se encuentran una o más líneas de encabezado extendidas. Los primeros tres

    índice de similitud 95%
    cambiar el nombre desde incorporado-http-fetch.c
    cambiar el nombre a http-fetch.c
    díganos que se cambió el nombre del archivo de builtin-http-fetch.ca http-fetch.cy que esos dos archivos son 95% idénticos (que se usó para detectar este cambio de nombre).

    La última línea en el encabezado de diferencia extendida, que es
    índice f3e63d7..e8f44ba 100644
    cuéntenos sobre el modo del archivo dado ( 100644significa que es un archivo normal y no, por ejemplo, un enlace simbólico, y que no tiene un bit de permiso ejecutable), y sobre el hash abreviado de la preimagen (la versión del archivo antes del cambio dado) y la postimagen (la versión del archivo después del cambio). Esta línea se utiliza para git am --3wayintentar realizar una combinación de tres vías si el parche no se puede aplicar por sí solo.

  • El siguiente es el encabezado de diferenciación unificado de dos líneas.

    --- a/integrado-http-fetch.c
    +++b/http-fetch.c
    En comparación con diff -Uel resultado, no tiene desde el tiempo de modificación del archivo ni hasta el tiempo de modificación del archivo después de los nombres de los archivos de origen (preimagen) y destino (postimagen). Si se creó el archivo, la fuente es /dev/null; Si se eliminó el archivo, el destino es /dev/null.
    Si establece diff.mnemonicPrefixla variable de configuración en verdadero, en lugar de los prefijos a/y b/en este encabezado de dos líneas, puede tener en su lugar c/, y como prefijos, respectivamente i/, con lo que compara; ver git-config(1)w/o/

  • Luego vienen uno o más grupos de diferencias; cada trozo muestra un área donde los archivos difieren. Los trozos de formato unificado comienzan con una 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 desde el archivo tiene el formato -<start line>,<number of lines>y el rango hasta el archivo tiene el formato +<start line>,<number of lines>. Tanto la línea de inicio como el número de líneas se refieren a la posición y la longitud del fragmento en la preimagen y la postimagen, respectivamente. Si no se muestra el número de líneas, significa que es 1.

El encabezado opcional muestra la función C donde ocurre cada cambio, si es un archivo C (como -pla opción en GNU diff), o el equivalente, si corresponde, 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:

  • '+': aquí se agregó una línea al primer archivo.

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


Entonces, por ejemplo, el primer fragmento

     #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_fetchfue reemplazado por mainy esa const char *prefix;línea fue agregada.

En otras palabras, antes del cambio, el fragmento apropiado del archivo 'builtin-http-fetch.c' tenía este aspecto:

    #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 del archivo ahora 'http-fetch.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
    \ Sin nueva línea al final del archivo
    línea presente (no está en el ejemplo diff).

Como dijo Donal Fellows, es mejor practicar la lectura de diferencias en ejemplos de la vida real, donde sabes lo que has cambiado.

Referencias:

  • Página de manual de git-diff(1) , sección "Generación de parches con -p"
  • (diff.info) Nodo unificado detallado, "Descripción detallada del formato unificado".
Jakub Narębski avatar Mar 27 '2010 16:03 Jakub Narębski

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

Me tomó un tiempo entender esta parte, así que creé un ejemplo mínimo.

El formato es básicamente el mismo que el de la diff -udiferenciación unificada.

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

  • -1,6significa que esta parte del primer archivo comienza en la línea 1 y muestra un total de 6 líneas. Por lo tanto muestra las líneas 1 a 6.

    1
    2
    3
    4
    5
    6
    

    -significa "viejo", ya que normalmente lo invocamos como diff -u old new.

  • +1,4significa que esta parte del segundo archivo comienza en la línea 1 y muestra un total de 4 líneas. Por lo tanto muestra las líneas 1 a 4.

    +significa "nuevo".

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

    1
    4
    5
    6
    

@@ -11,6 +9,4 @@para el segundo trozo 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 11es la novena línea del nuevo archivo porque ya hemos eliminado 2 líneas en el fragmento anterior: 2 y 3.

encabezado de pedazo

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

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

Esta también se puede obtener con la -pbandera de llano diff.

Ejemplo: archivo antiguo:

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

Si eliminamos la 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ó líneas 1y 2.

Esta increíble característica a menudo indica exactamente a qué función o clase pertenece cada trozo, lo cual es muy útil para interpretar la diferencia.

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

Notación resumida de trozos de una línea

Esto es muy raro, pero considere:

diff -U0 <(seq -w 16) <(seq -w 16 | sed 's/10/hack/')

dónde:

  • -U0: usa 0 líneas de contexto
  • el segundo archivo se reemplaza 10conhack

La salida diff en ese caso es:

@@ -10 +10 @@
-10
+hack

Entonces entendemos que cuando hay un cambio de una sola línea, la notación se resume para mostrar solo un número en lugar del m,npar.

Este comportamiento está documentado en la documentación citada en la respuesta de Todd :

Si un trozo contiene solo una línea, solo aparece su número de línea inicial. De lo contrario, sus números de línea se verán como inicio, recuento. Se considera que un trozo vacío comienza en la línea que sigue al trozo.

Y la adición y eliminación de fragmentos de una sola línea se ve así, eliminación:

diff -U0  <(seq -w 16) <(seq -w 16 | grep -Ev '^(10)$')

producción:

@@ -10 +9,0 @@
-10

suma:

$ diff -U0 <(seq -w 16 | grep -Ev '^(10)$') <(seq -w 16)

producción:

@@ -9,0 +10 @@
+10

Probado en diff 3.8, Ubuntu 22.10.