Grabe la operación de copia de archivos con Git

141

Cuando muevo un archivo en git usando git-mv, el estado muestra que el archivo ha sido renombrado e incluso si modifico algunas partes, todavía considera que es casi lo mismo (lo cual es bueno porque me permite seguir el historial del mismo) .

Cuando copio un archivo, el archivo original tiene algo de historial que me gustaría asociar con la nueva copia.

He intentado mover el archivo y luego intentar volver a pagar en la ubicación original; una vez movido, git no me deja pagar la ubicación original.

He intentado hacer una copia del sistema de archivos y luego agregar el archivo: git lo enumera como un archivo nuevo.

¿Hay alguna manera de hacer que git grabe una operación de copia de archivo de una manera similar a cómo registra un cambio de nombre / movimiento de archivo donde el historial se puede rastrear hasta el archivo original?

Hexdoll
fuente

Respuestas:

112

Git no realiza el seguimiento de cambio de nombre ni de copia, lo que significa que no registra cambios de nombre o copias. En cambio, lo que hace es cambiar el nombre y la detección de copias . Puede solicitar la detección de cambio de nombre en git diff(y git show) utilizando la -Mopción, puede solicitar una detección de copia adicional en archivos modificados utilizando la -Copción ( -Cimplica -M), y puede solicitar una detección de copia más costosa entre todos los archivos con --find-copies-hardero -C -C(lo que implica -C, lo que implica -M) Vea la página de manual de git-diff .

También puede configurar git para que siempre realice la detección de cambio de nombre estableciendo diff.renamesun valor booleano verdadero (p. Ej. trueO 1), y puede solicitar a git que realice una detección de copia también configurándolo en copyo copies. Vea la página de manual de git-config .

Compruebe también la -lopción git diffay la variable de configuración relacionada diff.renameLimit.


Tenga en cuenta que git log <pathspec>funciona de manera diferente en Git: aquí <pathspec>hay un conjunto de delimitadores de ruta, donde la ruta puede ser un nombre de (sub) directorio. Filtra y simplifica el historial antes de que la detección de cambio de nombre y copia entre en juego. Si desea seguir los cambios de nombre y copias, use git log --follow <filename>(que actualmente es un poco limitado y funciona solo para un solo archivo).

Jakub Narębski
fuente
1
@allyourcode: ¿De qué estás confundido? Para activar la detección de copia de forma predeterminada, se establece diff.renamesen copies(por ejemplo, ' git config diff.renames copies'). Estoy de acuerdo en que es un poco contradictorio.
Jakub Narębski
Parece que una sección que no puedo analizar es "y puede solicitar que, de forma predeterminada, también cambie el nombre de detección". ¿Estás diciendo que hay cuatro valores que pueden usar diff.renames (verdadero, 1, copia, copias) y que todos hacen lo mismo?
allyourcode
1
@allyourcode: Lo siento, no me he dado cuenta de esto. Solucionado ahora, gracias.
Jakub Narębski
44
@ peschü: Git utiliza una base de datos de objetos con contenido como un almacenamiento de repositorio. El contenido del archivo se almacena en el contenido 'blob' bajo la dirección que es el hash de contenido SHA-1 (bueno, tipo + longitud + contenido). Esto significa que los contenidos dados se almacenan solo una vez. Nótese bien. Esta deduplicación automática fue la razón detrás de la creación del sistema de copia de seguridad "bup", utilizando el formato git pack.
Jakub Narębski
1
A diferencia de la solución a continuación, esto no funciona con el seguimiento de cambios en un rango. Git log permite un argumento de rango ( git log -L123,456:file.xyz) que sigue correctamente los cambios de nombre, pero no las copias, y no puede pasar - siga en ese caso; Además, AFAICT, esto no funciona con culpa git.
Clément
57

2020-05-19: La siguiente solución tiene las ventajas de no cambiar el registro del archivo original, no crear un conflicto de fusión y ser más corto.

Puede forzar a Git a detectar el historial del archivo copiado en tres confirmaciones:

  • En lugar de copiar, cambie a una nueva rama y mueva el archivo a su nueva ubicación allí.
  • Vuelva a agregar el archivo original allí.
  • Combine la nueva rama con la rama original con la opción sin avance rápido --no-ff.

(Los créditos van a Raymond Chen ).


La solución anterior tenía cuatro confirmaciones:

  • En lugar de copiar, cambie a una nueva rama y mueva el archivo a su nueva ubicación allí.
  • Cambie a la rama original y cambie el nombre del archivo.
  • Combine la nueva rama en la rama original, resolviendo el conflicto trivial manteniendo ambos archivos.
  • Restaurar el nombre de archivo original en una confirmación por separado.

(Solución tomada de https://stackoverflow.com/a/44036771/1389680 .)

Robert Pollak
fuente
77
Sencillez, brevedad, 100% ... Esta respuesta es servicio público ... votando todo a la vista
ptim
1
¿Cuál es la diferencia entre movey rename?
vovan
@vovan ¿Te refieres al hecho de que en bash lo usarías mvpara ambas operaciones? Estaba usando 'mover' para el caso que puede implicar cambiar el directorio del archivo, y 'cambiar el nombre' para el caso donde no lo hace.
Robert Pollak
1
Traté de seguir esta receta (nueva) y no funcionó. Podría ayudar si mostraras los comandos reales.
Greg Lindahl
@RobertPollak He probado varias versiones de esto pero no funcionaron. Por "mover el archivo", ¿quieres decir git mv orig new? Por "leído el original", ¿quieres decir cp new orig && git add orig?
ᆼ ᆺ ᆼ