¿Cómo REALMENTE mostrar registros de archivos renombrados con git?

132

Soy relativamente nuevo en git, utilicé Subversion antes.

Noté que la mayoría de los front-end gráficos de git y los complementos IDE no parecen ser capaces de mostrar el historial de un archivo si el archivo ha sido renombrado. Cuando uso

git log --follow

en la línea de comando, puedo ver todo el registro a través de los nuevos nombres.

Según Linus Torvalds, el interruptor --follow es un deleite "novato SVN", los usuarios serios de git no lo usan:

--follow es un truco total, destinado a satisfacer a los ex usuarios de SVN que nunca supieron nada sobre cosas como la paternidad o buenos gráficos de revisión de todos modos.

No es totalmente fundamental, pero la implementación actual de "--follow" es realmente un proceso rápido de preprocesamiento sujeto a la lógica de revisión de la revisión, en lugar de ser algo realmente integral.

Literalmente fue diseñado como un "SVN noob" complaciente, no como una "verdadera funcionalidad git". La idea era que te alejarías de la mentalidad (rota) de pensar que los cambios de nombre importan en el panorama general.

Mi pregunta : ¿Cómo obtienen los usuarios hardcore de git entre ustedes el historial de un archivo cuando fue renombrado? ¿Cuál es la forma 'real' de hacer esto?

Miguel
fuente
17
@ David Hall: git mv oldfile newfileno hace que el cambio de nombre a grabarse en absoluto - es lo mismo que borrar un archivo y añadir otro. git solo resuelve los cambios de nombre y copias del estado del árbol en cada confirmación después del hecho.
Mark Longair
20
@David Hall: si cambia el nombre del archivo con otra herramienta fuera de git (por ejemplo /bin/mv oldfile newfile), pero luego lo hace git add newfile; git rm oldfile, el resultado es indistinguible del de git mv oldfile newfile.
Mark Longair
1
Esta ideología se desmorona si alguna vez mueve un archivo a un nuevo repositorio, en cuyo caso la imposibilidad de mover todo su historial puede ser un problema importante. Aunque, por supuesto, hay límites para la cantidad de historia verdadera que realmente puede venir con un archivo en un proyecto complejo.
Roman Starkov
1
Nota: git log --followmejora un poco con git 2.9 (junio de 2016): vea mi respuesta a continuación
VonC
1
A partir de la versión 2.15, es posible que desee experimentar --color-movedcuando diff.
Michael

Respuestas:

71

Creo que el impulso general detrás del punto de Linus es que, y tome esto con una pizca de sal, a los usuarios de git hardcore nunca les importa la historia de un "archivo". Pones contenido en un repositorio git porque el contenido en su conjunto tiene un historial significativo.

Un cambio de nombre de archivo es un pequeño caso especial de "contenido" que se mueve entre rutas. Es posible que tenga una función que se mueve entre archivos que un usuario de git puede rastrear funcionalmente con "pico" (por ejemplo log -S).

Otros cambios de "ruta" incluyen la combinación y división de archivos; git realmente no le importa qué archivo considera renombrado y cuál considera copiado (o renombrado y eliminado) solo rastrea el contenido completo de su árbol.

git fomenta el pensamiento de "árbol completo" en el que muchos sistemas de control de versiones están muy centrados en los archivos. Es por eso que git se refiere a "rutas" con más frecuencia que a "nombres de archivo".

CB Bailey
fuente
Hola Charles, gracias por tu respuesta. Parece que uso git de la misma manera que usé SVN. Aunque entiendo que git es muy diferente a otros sistemas de control de versiones, muchos conceptos en git me parecen extraños todavía ... Probablemente debería terminar ese libro de git que compré recientemente.
Mike
24
El punto de Linus era que una interfaz gráfica de usuario "adecuada" sería capaz de rastrear fragmentos de código a través de los archivos, y esperaba que tuviéramos esas herramientas en este momento. Desafortunadamente, todavía no tenemos ese lujo, y --lo sigue siendo útil.
Michael Parker
11
¿Git realmente te da una solución diferente a --followesta?
Griwes
13
Yo diría que el pensamiento de "árbol completo" se mejora al --followser el valor predeterminado. Lo que quiero decir es que cuando quiero ver el historial del código dentro de un archivo, realmente no me importa si el archivo fue renombrado o no, solo quiero ver el historial del código, independientemente de los cambios de nombre. Entonces, en mi opinión, tiene sentido --followque sea el predeterminado porque no me importan los archivos individuales; --followme ayuda a ignorar los cambios de nombre de archivos individuales, que generalmente son bastante intrascendentes.
Eddified
2
Entonces ... si decido preocuparme por el "contenido" en lugar del archivo, ¿cómo imprimo las confirmaciones relevantes para el contenido que está actualmente en este archivo? Estaría encantado si git lo rastreara en todos los archivos diferentes y me informara un registro de todos los cambios, simplemente no veo cómo obtenerlo.
Ed Avis
36

Tengo exactamente el mismo problema que estás enfrentando. Aunque no puedo darte una respuesta, creo que puedes leer este correo electrónico que Linus escribió en 2005, es muy pertinente y podría darte una pista sobre cómo manejar el problema:

... Estoy afirmando que cualquier SCM que intente rastrear cambios de nombre está fundamentalmente roto a menos que lo haga por razones internas (es decir, para permitir deltas eficientes), exactamente porque los cambios de nombre no importan. No te ayudan, y de todos modos no son lo que te interesaba .

Lo que importa es encontrar "de dónde vino esto", y la arquitectura git lo hace muy bien, mucho mejor que cualquier otra cosa. ...

Lo encontré referenciado en esta publicación de blog, que también podría ser útil para encontrar una solución viable:

En el mensaje, Linus describió cómo un sistema ideal de seguimiento de contenido puede permitirle encontrar cómo un bloque de código adquirió la forma actual. Comenzaría desde el bloque de código actual en un archivo, volvería al historial para encontrar la confirmación que cambió el archivo. Luego inspecciona el cambio de la confirmación para ver si el bloque de código que le interesa está modificado, ya que una confirmación que cambia el archivo puede no tocar el bloque de código que le interesa, sino solo algunas otras partes del código. expediente.

Cuando encuentre que antes de la confirmación, el bloque de código no existía en el archivo, inspecciona la confirmación más a fondo. Puede descubrir que es una de las muchas situaciones posibles, que incluyen:

  1. El commit realmente introdujo el bloque de código. El autor de la comisión fue el inventor de esa característica genial que estaba buscando su origen (o la parte culpable que introdujo el error); o
  2. El bloque de código no existía en el archivo, pero existían cinco copias idénticas en diferentes archivos, todos los cuales desaparecieron después de la confirmación. El autor de la confirmación refactorizó el código duplicado mediante la introducción de una única función auxiliar; o
  3. (como un caso especial) Antes de la confirmación, el archivo que actualmente contiene el bloque del código que le interesa no existía, pero sí existía otro archivo con contenidos casi idénticos, y el bloque del código que le interesa, junto con todos los otros contenidos en el archivo existían en ese entonces, existían en ese otro archivo. Se fue después del compromiso. El autor de la confirmación cambió el nombre del archivo y le dio una modificación menor.

En git, la última herramienta de seguimiento de contenido de Linus aún no existe de manera totalmente automatizada. Pero la mayoría de los ingredientes importantes ya están disponibles.

Por favor, manténganos informados sobre su progreso en esto.

Alberto Bacchelli
fuente
Gracias por publicar esos artículos. ¡No fue hasta que los leí que comprendí completamente la idea del historial de contenido! ¡He estado pensando en esto de la manera incorrecta!
DavidG
Ese correo electrónico de Linus es genial, gracias por publicar esto.
mik01aj
Dato curioso--color-moved , Git v2.15 agrega un movimiento hacia ese "sistema de seguimiento ideal". Estaba jugando con él para verlo rastrear líneas movidas dentro de un archivo, pero accidentalmente me di cuenta de que rastrea líneas movidas en todo el diff.
Michael
2
Linus explica una situación compleja. Pero aquí tenemos una situación simple: simplemente se cambió el nombre de un archivo (o se movió a otro directorio). Por lo tanto, debería haber una solución simple. Creo que el problema proviene del hecho que es contrario a Subversion, el usuario no puede instruir a Git en el momento de la confirmación de dónde proviene un archivo, y eso --followpuede ser incorrecto (por ejemplo, si 2 archivos tienen el mismo contenido, o si hay una modificación además del movimiento de archivo).
vinc17
13

Noté que la mayoría de los front-end gráficos de git y los complementos IDE no parecen poder mostrar el historial de un archivo si el archivo ha sido renombrado

Te alegrará saber que algunas herramientas populares de Git UI ahora lo admiten. Hay docenas de herramientas de Git UI disponibles, por lo que no las enumeraré todas, pero por ejemplo:

  • SourceTree, cuando ve un registro de archivo, tiene una casilla de verificación "Seguir archivos renombrados" en la parte inferior izquierda
  • TortoiseGit tiene una casilla de verificación "seguir renombrados" en la ventana de registro en la parte inferior izquierda.

Más información sobre las herramientas de Git UI:

Michael Parker
fuente
Source funciona muy bien cuando se renombra una vez, cuando se renombra dos veces, los detalles de cambio no están disponibles para confirmaciones antes de renombrar. Informé
Intel
gitk funciona muy bien incluso cuando se cambia el nombre de un archivo dos veces en la historia. El comando se ve así: "gitk --follow path / to / file"
Intel
6

Nota: git 2.9 (junio de 2016) mejorará bastante la naturaleza "con errores" de git log --follow:

Ver commit ca4e3ca (30 mar 2016) por SZEDER Gábor ( szeder) .
(Fusionada por Junio ​​C Hamano - gitster- en commit 26effb8 , 13 abr 2016)

diffcore: arregla el orden de iteración de archivos idénticos durante la detección de cambio de nombre

Si las dos rutas ' dir/A/file' y ' dir/B/file' tienen contenido idéntico y se cambia el nombre del directorio principal, por ejemplo ' git mv dir other-dir', diffcoreinforma los siguientes cambios de nombre exactos:

renamed:    dir/B/file -> other-dir/A/file
renamed:    dir/A/file -> other-dir/B/file

(tenga en cuenta la inversión aquí: B/file -> A/filey A/file -> B/file)

Aunque técnicamente no está mal, esto es confuso no solo para el usuario, sino también para los comandos git que toman decisiones basadas en la información de cambio de nombre, por ejemplo, ' git log --follow other-dir/A/file' sigue ' dir/B/file' más allá del cambio de nombre.

Este comportamiento es un efecto secundario de commit v2.0.0-rc4 ~ 8 ^ 2 ~ 14 ( diffcore-rename.c: simplificar la búsqueda de cambios de nombre exactos, 14/11/2013): el hashmap que almacena las fuentes devuelve entradas del mismo depósito, es decir, las fuentes que coinciden con el destino actual , en orden LIFO.
Por lo tanto, la iteración primero examina ' other-dir/A/file' y ' dir/B/file' y, al encontrar contenido idéntico y nombre base, informa un cambio de nombre exacto.

VonC
fuente
2

En Linux, he verificado que SmartGit y GitEye pueden seguir los cambios de nombre al seguir el historial de un archivo en particular. Sin embargo, a diferencia de gitk y GitEye, SmartGit muestra una vista de archivo y una vista de repositorio separadas (que contiene la estructura de directorios pero no la lista de archivos que contiene)

Prusswan
fuente