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
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 formadiff --git a/file1 b/file2
. Los nombres de archivoa/
yb/
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 debuiltin-http-fetch.c
ahttp-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 esindex 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 porgit 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 condiff -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óndiff.mnemonicPrefix
a true, en lugar dea/
yb/
prefijos en este encabezado de dos líneas que puede tener en su lugarc/
,i/
,w/
yo/
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 pormain
, y que se agregó la líneaconst 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:
- git-diff(1) manpage , sección " Generar parches con - p "
- (diff.info) Nodo Unificado Detallado, "Descripción Detallada del Formato Unificado".
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 comodiff -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?
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
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.
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.
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.
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.
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.
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