git diff renombrado archivo

105

Tengo un archivo a.txt.

cat a.txt
> hello

El contenido de a.txtes "hola".

Me comprometo.

git add a.txt
git commit -m "first commit"

Luego me muevo a.txta un testdirectorio.

mkdir test
mv a.txt test

Luego hago mi segundo compromiso.

git add -A
git commit -m "second commit"

Finalmente, edito a.txtpara decir "adiós" en su lugar.

cat a.txt
> goodbye

Hago mi último compromiso.

git add a.txt
git commit -m "final commit"

Ahora aquí está mi pregunta:

¿Cómo difiero el contenido de a.txtentre mi última confirmación y mi primera confirmación?

Lo intenté git diff HEAD^^..HEAD -M a.txt, pero no funcionó. git log --follow a.txtdetecta correctamente el cambio de nombre, pero no puedo encontrar un equivalente para git diff. Hay uno

Ken Hirakawa
fuente
¿Supongo que sería lo mismo si hicieras una 'prueba git mv a.txt'? Es decir, acaba de cambiar el nombre del archivo en lugar de moverlo a un subdirectorio.
Max Waterman

Respuestas:

107

El problema con la diferencia entre HEAD^^y HEADes que tienes una a.txten ambas confirmaciones, por lo que solo considerando esas dos confirmaciones (que es lo que hace diff), no hay cambio de nombre, hay una copia y un cambio.

Para detectar copias, puede utilizar -C:

git diff -C HEAD^^ HEAD

Resultado:

index ce01362..dd7e1c6 100644
--- a/a.txt
+++ b/a.txt
@@ -1 +1 @@
-hello
+goodbye
diff --git a/a.txt b/test/a.txt
similarity index 100%
copy from a.txt
copy to test/a.txt

Por cierto, si restringe su diff a solo una ruta (como lo hace en git diff HEAD^^ HEAD a.txt, nunca verá los cambios de nombre o copias porque ha excluido todo, aparte de una sola ruta y los cambios de nombre o copias, por definición, involucran dos caminos.

CB Bailey
fuente
2
Digamos que la confirmación contiene varios cambios de archivo. ¿Cómo lo reduciría a una diferencia de un solo archivo?
Ken Hirakawa
3
@KenHirakawa use -- <old-path> <new-path>... vea mi respuesta.
Nolan Amy
En mi caso, quería mostrar los detalles de una confirmación en la que se cambió el nombre del archivo, pero solo me mostraba que se eliminó un archivo y se agregó un archivo ... Ejecuté git show -C [commit]y reconoció el cambio de nombre del archivo y me mostró el diff entre los archivos. Perfecto.
Jason
83

Para diferenciar un cambio de nombre de un archivo específico, use -M -- <old-path> <new-path>( -Ctambién funciona).

Entonces, si cambió el nombre y cambió un archivo en la última confirmación, puede ver los cambios con:

git diff HEAD^ HEAD -M -- a.txt test/a.txt

Esto produce:

diff --git a/a.txt b/test/a.txt
similarity index 55%
rename from a.txt
rename to test/a.txt
index 3f855b5..949dd15 100644
--- a/a.txt
+++ b/test/a.txt
@@ -1,3 +1,3 @@
 // a.txt

-hello
+goodbye

( // a.txtlíneas agregadas para ayudar a git a detectar el cambio de nombre)


Si git no detecta el cambio de nombre, puede especificar un umbral de similitud bajo con -M[=n], digamos, 1%:

git diff HEAD^ HEAD -M01 -- a.txt test/a.txt

De los documentos de git diff :

-M [<n>] --find-rename [= <n>]

Detectar cambios de nombre. Si nse especifica, es un umbral en el índice de similitud (es decir, la cantidad de adiciones / eliminaciones en comparación con el tamaño del archivo). Por ejemplo, -M90%significa que Git debería considerar un par de eliminar / agregar como un cambio de nombre si más del 90% del archivo no ha cambiado. Sin %signo, el número debe leerse como una fracción, con un punto decimal antes. Es decir, se -M5convierte en 0,5 y, por tanto, es lo mismo que -M50%. Del mismo modo, -M05es lo mismo que -M5%. Para limitar la detección a cambios de nombre exactos, utilice -M100%. El índice de similitud predeterminado es 50%.

Nolan Amy
fuente
... Por supuesto, también funciona cuando el cambio y el cambio de nombre están en confirmaciones separadas como en el ejemplo original:git diff HEAD^^ HEAD -M -- a.txt test/a.txt
Nolan Amy
1
Solo cuando el contenido de los archivos está lo suficientemente cerca como para que diff genere un índice de similitud. El uso de ambas opciones (-M y -C) muestra la diferencia a / dev / null y desde / dev / null en un archivo que se renombró y ha cambiado por completo (incluida la sangría), ni siquiera lo detecta cuando se ignora espacio en blanco.
DavidG
37

También puedes hacer:

git diff rev1:file1 rev2:file2

que, para tu ejemplo, sería

git diff HEAD^^:./a.txt HEAD:./test/a.txt

Tenga en cuenta lo explícito ./: de lo contrario, este formato asume que las rutas son relativas a la raíz del repositorio. (Si está en la raíz del repositorio, por supuesto, puede omitirlo).

Esto no depende en absoluto de la detección de cambio de nombre, ya que el usuario indica explícitamente exactamente qué comparar. (Por lo tanto, también resulta útil en otras circunstancias, como comparar archivos entre diferentes ramas svn en un entorno git-svn).

benkc
fuente
1
¡Oooh, esto es lindo!
Nolan Amy
Para un cambio de nombre con cambios de archivo adicionales traídos a través de una fusión, esta es la única respuesta que funcionó para mí. ¡Gracias!
Lane Rettig
1

Si su confirmación de cambio de nombre está preparada pero aún no se ha confirmado, puede usar:

git diff --cached -M -- file.txt renamed_file.txt
Mr-IDE
fuente