Comparar cambios con git diff

La comparación es una función mediante la cual se toman dos conjuntos de datos de entrada y se muestran los cambios entre estos. git diff es un comando multiusos de Git que, cuando se ejecuta, lleva a cabo una función para establecer las diferencias en las fuentes de datos de Git. Dichas fuentes de datos pueden ser confirmaciones, ramas y archivos, entre otras posibilidades. En el presente documento, examinaremos las invocaciones habituales de git diff y los patrones del flujo de trabajo de comparación. El comando git diff suele utilizarse junto con git status y git log para analizar el estado actual de un repositorio de Git.

Leer diferencias: resultados

Formato de salida sin procesar

Los siguientes ejemplos se ejecutarán en un solo repositorio, que se crea con los comandos indicados a continuación:

 $:> mkdir diff_test_repo $:> cd diff_test_repo $:> touch diff_test.txt $:> echo "this is a git diff test example" > diff_test.txt $:> git init . Initialized empty Git repository in /Users/kev/code/test/.git/ $:> git add diff_test.txt $:> git commit -am"add diff test file" [master (root-commit) 6f77fc3] add diff test file 1 file changed, 1 insertion(+) create mode 100644 diff_test.txt

Si ejecutamos git diff llegados a este punto, no habrá ningún resultado. Este es el comportamiento previsto, ya que no hay ningún cambio en el repositorio que permite mostrar las diferencias. Cuando se haya creado el repositorio y hayamos añadido el archivo diff_test.txt, podremos cambiar el contenido del archivo para empezar a experimentar con el resultado de la diferencia.

 $:> echo "this is a diff example" > diff_test.txt

Si ejecutas este comando, cambiarás el contenido del archivo diff_test.txt. Cuando se haya modificado, podremos ver una diferencia y analizar el resultado. Ahora, al ejecutar git diff, se producirá el resultado siguiente:

 diff --git a/diff_test.txt b/diff_test.txt index 6b0c6cf..b37e70a 100644 --- a/diff_test.txt +++ b/diff_test.txt @@ -1 +1 @@ -this is a git diff test example +this is a diff example

Pasemos ahora a examinar un desglose más detallado del resultado de la diferencia.

1. Introducción de la comparación

 diff --git a/diff_test.txt b/diff_test.txt

En esta línea se muestran las fuentes de entrada de la diferencia. Podemos observar que se ha utilizado a/diff_test.txt y b/diff_test.txt en la diferencia.

2. Metadatos

 index 6b0c6cf..b37e70a 100644

En esta línea se muestran unos cuantos metadatos internos de Git. Lo más probable es que no necesites esta información. Los números de este resultado se corresponden con los identificadores hash de la versión del objeto de Git.

3. Marcadores de los cambios

 --- a/diff_test.txt +++ b/diff_test.txt

Estas líneas conforman una leyenda que asigna símbolos a cada fuente de entrada de la diferencia. En este caso, los cambios de a/diff_test.txt se marcan con un --- y, los de b/diff_test.txt, con el símbolo +++.

4. Fragmentos de la diferencia

El producto restante de la diferencia es una lista de "fragmentos" de la diferencia. Una diferencia solo muestra las secciones del archivo con cambios. En el ejemplo en cuestión, solo hay un fragmento porque estamos trabajando con un caso sencillo. Los fragmentos poseen su propia semántica pormenorizada del resultado.

 @@ -1 +1 @@ -this is a git diff test example +this is a diff example

La primera línea es el encabezado del fragmento. Cada fragmento comienza con un encabezado delimitado por símbolos @@. El contenido de dicho encabezado es un resumen de los cambios efectuados en el archivo. En nuestro ejemplo simplificado pone "-1 +1", lo que significa que ha habido cambios en la primera línea. En una diferencia más realista, se podría ver un encabezado como el siguiente:

 @@ -34,6 +34,8 @@

En este ejemplo de encabezado, lo que pone es que se han extraído 6 líneas a contar desde la línea número 34. Además, se han añadido 8 líneas a partir de la línea número 34.

El resto del contenido del fragmento muestra los cambios recientes. Todas las líneas cambiadas comienzan por un símbolo + o -, que sirve para indicar de qué versión de la entrada de la diferencia proceden los cambios. Como ya hemos comentado antes, - indica cambios en a/diff_test.txt, mientras que "+" indica cambios en b/diff_test.txt.

Resaltar cambios

1. git diff --color-words

El comando git diff también tiene un modo especial para resaltar los cambios con un nivel de detalle muy superior: ‐‐color-words. Este modo tokeniza las líneas añadidas y eliminadas mediante espacios en blanco y luego muestra la diferencia.

 $:> git diff --color-words diff --git a/diff_test.txt b/diff_test.txt index 6b0c6cf..b37e70a 100644 --- a/diff_test.txt +++ b/diff_test.txt @@ -1 +1 @@ this is agit difftest example

Ahora, el resultado muestra solo las palabras coloreadas que han cambiado.

2. git diff-highlight

Si clonas el código fuente de git, verás un subdirectorio llamado "contrib", que contiene una serie de herramientas relacionadas con Git, así como otras interesantes cositas que todavía no se han promovido al núcleo de Git. Una de ellas es un script en Perl llamado "diff-highlight". Diff-highlight empareja líneas coincidentes del resultado de la diferenciación y resalta los fragmentos menores que una palabra que han cambiado.

 $:> git diff | /your/local/path/to/git-core/contrib/diff-highlight/diff-highlight diff --git a/diff_test.txt b/diff_test.txt index 6b0c6cf..b37e70a 100644 --- a/diff_test.txt +++ b/diff_test.txt @@ -1 +1 @@ -this is a git diff test example +this is a diff example

De este modo, se reduce la diferencia al mínimo cambio posible.

Diferenciación de archivos binarios

Aparte de las utilidades para archivos de textos que hemos mostrado hasta el momento, git diff también se puede ejecutar con archivos binarios. Por desgracia, el resultado predeterminado no resulta demasiado útil.

 $:> git diff Binary files a/script.pdf and b/script.pdf differ

Git tiene una función que te permite especificar un comando de shell para transformar el contenido de tus archivos binarios en texto antes de ejecutar el comando diff, pero para ellos hay que hacer unos pequeños ajustes. En primer lugar, tienes que especificar un filtro de textconv que exponga cómo convertir en texto un tipo concreto de archivo binario. Usaremos una sencilla utilidad llamada "pdftohtml" (disponible a través de Homebrew) para convertir unos PDF en HTML legible para humanos. Puedes configurar esto para un solo repositorio editando el archivo .git/config o bien globalmente editando ~ /.gitconfig.

 [diff "pdfconv"] textconv=pdftohtml -stdout

Acto seguido, lo único que tienes que hacer es asociar al menos un patrón de archivos a nuestro filtro pdfconv, para lo cual puedes crear un archivo .gitattributes en la raíz del repositorio.

 *.pdf diff=pdfconv

Una vez configurado, git diff ejecutará primero el archivo binario mediante el script de conversor configurado y producirá la diferencia del resultado del conversor. Esta misma técnica se puede aplicar para obtener diferencias útiles de todo tipo de archivos binarios como, por ejemplo, archivos .zip, .jar y otros comprimidos. Por ejemplo: si utilizas unzip -l (o semejante) en vez de "pdf2html", te mostrará las rutas que se han añadido o eliminado entre imágenes de confirmaciones. Por otro lado, se puede utilizar "exiv2" para mostrar cambios en los metadatos como, por ejemplo, documentos de dimensiones de imágenes. Asimismo, existen herramientas de conversión para transformar archivos .odf, .doc y otros formatos de documento a texto sin formato. Si no hay otra opción, las cadenas normalmente funcionarán con archivos binarios para los que no existe ningún conversor formal.

Comparar archivos: archivo git diff

En el comando git diff se puede utilizar una opción de ruta de archivo explícita. Cuando se utiliza una ruta de archivo en git diff, la operación de diferencia limitará su alcance al archivo especificado. Los siguientes ejemplos ilustran este uso.

 git diff HEAD ./path/to/file

En este ejemplo, el alcance se restringe a ./path/to/file y, al invocar el comando, este comparará los campos concretos efectuados en el directorio de trabajo con respecto al índice, y mostrará los cambios que todavía no se han preparado. De forma predeterminada, git diff ejecutará la comparación con respecto a HEAD. Si se omite HEAD en el ejemplo anterior, git diff ./path/to/file surtirá el mismo efecto.

 git diff --cached ./path/to/file

Al invocar git diff con la opción --cached, el comando comparará los cambios preparados con el repositorio local. La opción --cached equivale a --staged.

Comparar todos los cambios

Al invocar git diff sin ninguna ruta de archivo, se compararán los cambios efectuados en todo el repositorio. Los ejemplos anteriores con archivos concretos se pueden invocar sin el argumento ./path/to/file y generar los mismos resultados en todos los archivos del repositorio local.

Cambios desde la última confirmación

De forma predeterminada, git diff te mostrará todos los cambios sin confirmar desde la última confirmación.

 git diff

Comparar archivos entre dos confirmaciones distintas

En el comando git diff se pueden utilizar referencias de Git a confirmaciones para mostrar las diferencias. Por ejemplo, algunas de estas referencias son HEAD, etiquetas y nombres de ramas. En Git, todas las confirmaciones tienen su correspondiente ID y, para obtenerlo, puedes ejecutar GIT LOG. También puedes utilizar este ID de confirmación en git diff.

 git log --prety=oneline 957fbc92b123030c389bf8b4b874522bdf2db72c add feature ce489262a1ee34340440e55a0b99ea6918e19e7a rename some classes 6b539f280d8b0ec4874671bae9c6bed80b788006 refactor some code for feature 646e7863348a427e1ed9163a9a96fa759112f102 add some copy to body $:> git diff 957fbc92b123030c389bf8b4b874522bdf2db72c ce489262a1ee34340440e55a0b99ea6918e19e7a

Comparar ramas

Comparar dos ramas

Las ramas se comparan igual que el resto de las entradas de referencia en git diff.

 git diff branch1..other-feature-branch

Este ejemplo introduce el punto como operador. Los dos puntos de este ejemplo indican que la entrada de diferencia son los extremos de ambas ramas. Si se omiten estos puntos y se utiliza un espacio entre las ramas, el efecto que se produce es el mismo. Además, está el operador de tres puntos:

 git diff branch1...other-feature-branch

El operador de tres puntos inicia la diferencia cambiando el primer parámetro de entrada, branch1, ya que convierte branch1 en una referencia de la confirmación antecesora común compartida entre las dos entradas de diferencia, el antecesor compartido de branch1 y other-feature-branch. El último parámetro de entrada permanece inmutable como extremo de other-feature-branch.

Comparar archivos de dos ramas

Para comparar un archivo concreto entre ramas, hay que utilizar en el comando la ruta del archivo como tercer argumento de git diff.

 git diff master new_branch ./diff_test.txt

Resumen

En esta página se han analizado el proceso de comparación de Git y el comando git diff. Hemos explorado cómo leer los resultados de git diff y los distintos datos que estos incluyen. Se han dado ejemplos de cómo alterar el resultado de git diff con resaltado y colores. Hemos examinado las estrategias de comparación como, por ejemplo, cómo ver las diferencias entre los archivos de ramas distintas y confirmaciones concretas. Además del comando git diff, también hemos utilizado git log y git checkout.

¿Listo para aprender a usar Git?

Prueba este tutorial interactivo.

Comienza ahora