tipos tener tag sirve qué proyecto podemos para nuestros nos mayor herramienta hacer existen etiquetas eliminar cuando crear creamos control git blame git-blame

tener - ¿Por qué la culpa de git no sigue a los cambios de nombre?



¿qué herramienta podemos hacer para tener un mayor control de nuestros proyecto en github? (2)

$ pwd /data/mdi2/classes $ git blame -L22,+1 -- utils.js 99b7a802 mdi2/utils.js (user 2015-03-26 21:54:57 +0200 22) #comment $ git blame -L22,+1 99b7a802^ -- utils.js fatal: no such path mdi2/classes/utils.js in 99b7a802^

Como habrás notado, el archivo estaba en un directorio diferente en ese commit

$ git blame -L22,+1 99b7a802^ -- ../utils.js c5105267 (user 2007-04-10 08:00:20 +0000 22) #comment 2

A pesar de doc

The origin of lines is automatically followed across whole-file renames (currently there is no option to turn the rename-following off)

la culpa no sigue a los cambios de nombre. ¿Por qué?

ACTUALIZACIÓN: respuesta corta

git blame follow renombra pero no para git blame COMMIT^ -- <filename>

Pero esto es demasiado difícil de rastrear los nombres de los archivos manualmente a través del grueso de los cambios de nombre y toneladas de historial. Creo que este comportamiento debe corregirse para seguir silenciosamente los git blame COMMIT^ -- <filename> . O, al menos, --follow debe implementar, así que puedo: git blame --follow COMMIT^ -- <filename>

ACTUALIZACIÓN2: Eso es imposible. Lee abajo.

RESPUESTA DE MAILLIST por Junio ​​C Hamano

git blame follow renombra pero no para git blame COMMIT^ -- <filename>

Supongamos que tiene el archivo A y el archivo B en su versión v1.0.

Seis meses después, el código fue muy refactorizado, y no necesita el contenido de estos dos archivos por separado. Has eliminado A y B y mucho de lo que tenían ahora está en el archivo C. Ese es el estado actual.

git blame -C HEAD -- C

puede seguir el contenido de ambos bien, pero si se le permitiera decir

git blame v1.0 -- C

¿Qué se supone que significa eso? C no existía v1.0 en absoluto. ¿Estás pidiendo seguir los contenidos de A en ese momento o B? ¿Cómo dijiste que significaba A y no B cuando le dijiste C en este comando?

"Git culpa" sigue los movimientos de contenido, y nunca trata "cambiar el nombre" de ninguna manera especial, ya que es una cosa estúpida que hacer para pensar que un cambio de nombre es de alguna manera especial ;-)

La forma de decir a qué contenido comenzar a buscar desde el comando desde su línea de comando es otorgar commit de punto de inicio (por defecto HEAD pero puede dar COMMIT ^ como su ejemplo) y la ruta en ese punto de inicio. Como no tiene sentido decirle C a Git y luego mágicamente adivinar que quisiste decir A en algunos casos y B en algún otro. Si v1.0 no tiene C, lo único sensato es salir en lugar de hacer una conjetura (y sin decirle al usuario cómo adivinó).


El último git tiene un comando interesante. Añádelo junto a tu configuración:

[alias] follow= "!sh -c ''git log --topo-order -u -L $2,${3:-$2}:"$1"''" -

Ahora usted puede:

$git follow <filename> <linefrom> [<lineto>]

Y verá cada confirmación que cambia las líneas especificadas en <filename> .

También puede estar interesado en --follow opción del comando de git log de git log :

Continúe enumerando el historial de un archivo más allá de cambiar el nombre (funciona solo para un solo archivo).

Si está interesado en la detección de copia, use -C :

Detecta copias y cambia el nombre. Ver también --find-copies-harder. Si se especifica n, tiene el mismo significado que para -M.

-C verá diferentes archivos en la misma confirmación. Si desea detectar que el código se tomó de un archivo diferente que no se modificó en este compromiso. Luego debe proporcionar la --find-copies-harder .

Por motivos de rendimiento, de forma predeterminada, la opción -C encuentra copias solo si el archivo original de la copia se modificó en el mismo conjunto de cambios. Este indicador hace que el comando inspeccione los archivos no modificados como candidatos para la fuente de copia. Esta es una operación muy costosa para proyectos grandes, así que ústela con precaución. Dar más de una opción -C tiene el mismo efecto.

UPD
Yo mejoro este alias:

[alias] follow = "!bash -c '' / if [[ $1 == /"//"* ]]; then / FILE=$1; / else / FILE=${GIT_PREFIX}$1; / fi; / echo /"git log --topo-order -u -L $2,${3:-$2}:///"$FILE///"/"; / git log --topo-order -u -L $2,${3:-$2}:/"$FILE/"; / '' --"

Ahora puede rastrear cómo se cambia el rango de líneas especificado:

git follow file_name.c 30 35

AVISO: Desafortunadamente, git no tiene en cuenta los cambios en el directorio de trabajo. Por lo tanto, si realiza cambios locales en el archivo, debe ocultarlo antes de poder follow cambios.


git blame sigue a los cambios de nombre (como lo hace el git log si lo das --follow ). El problema radica en la forma en que sigue el cambio de nombre, que es un truco no muy completo: a medida que retrocede un compromiso a la vez (de cada niño a cada uno de los padres), se convierte en el mismo tipo de diferencia que puede hacer manualmente con:

git diff -M SHA1^ SHA1

-Y comprueba si esta diferencia detectó un cambio de nombre. 1

Eso está bien hasta donde llega, pero significa que para que la git blame detecte un cambio de nombre, (a) git diff -M tiene que poder detectarlo (afortunadamente ese es el caso aquí) y-aquí está lo que te está causando problemas -Debe pasar por el cambio de nombre .

Por ejemplo, supongamos que el gráfico de compromiso se parece un poco a esto:

A <-- B <-- ... Q <-- R <-- S <-- T

donde cada letra mayúscula representa un compromiso. Supongamos además que un archivo fue renombrado en commit R , de modo que en commits de R a T tiene nombre newname mientras que en commits de A a Q tiene nombre oldname .

Si ejecuta git blame -- newname , la secuencia comienza en T , compara S y T , compara R y S , y compara Q y R Cuando compara Q y R , git blame descubre el cambio de nombre y comienza a buscar oldname en commits Q y anteriores, por lo que cuando compara P y Q compara los archivos oldname y oldname en esos dos commits.

Si, por otro lado, ejecuta git blame R^ -- newname (o git blame Q -- newname ) para que la secuencia comience en commit Q , no hay ningún archivo newname en esa confirmación, y no hay cambio de nombre al comparar P y Q , y la git blame simplemente se rinde.

El truco está en que si comienzas una confirmación en la que el archivo tenía el nombre anterior, debes darle a git el nombre anterior:

git blame R^ -- oldname

y luego todo funciona de nuevo.

1 En la documentación de git diff , verá que hay una opción -M que controla cómo git diff detecta los cambios de nombre. El código de blame modifica esto un poco (y de hecho hace dos pasadas, una con -M apagado y una segunda con -M activada) y usa su propia opción (diferente) -M para fines algo diferentes, pero al final está usando esto mismo código

[ Editar para agregar respuesta al comentario (no encajaba como un comentario en sí)]:

Es cualquier herramienta que me puede mostrar renombra como: git renames <filename> SHA date oldname-> newname

No exactamente, pero git diff -M se acerca, y puede ser lo suficientemente cerca.

No estoy seguro de lo que quiere decir con "fecha de SHA" aquí, pero git diff -M permite suministrar dos SHA-1 y compara de izquierda a derecha. Agregue --name-status para obtener solo nombres de archivos y disposiciones. Por lo tanto, git diff -M --name-status HEAD oldsha1 puede informar que para convertir de HEAD a oldsha1 , git cree que debe cambiar el nombre de un archivo e informará el nombre antiguo como el "nuevo". Por ejemplo, en el repositorio de git, hay un archivo actualmente llamado Documentation/giteveryday.txt que solía tener un nombre ligeramente diferente:

$ git diff -M --name-status HEAD 992cb206 M .gitignore M .mailmap [...snip...] M Documentation/diff-options.txt R097 Documentation/giteveryday.txt Documentation/everyday.txt D Documentation/everyday.txto [...]

Si ese es el archivo que te importa, eres bueno. Los dos problemas aquí son:

  • encontrar un SHA1: ¿de 992cb206 viene 992cb206 ? Si ya tienes un SHA-1, es fácil; si no, git rev-list es la herramienta de búsqueda de SHA1; lee su documentación;
  • y el hecho de que seguir una serie de renombrados a través de cada commit uno por vez, como lo hace git blame , puede producir respuestas bastante diferentes que comparar un commit mucho más tarde ( HEAD ) contra un commit mucho más temprano ( 992cb206 o lo que sea). En este caso, sale igual, pero el "índice de similitud" aquí es 97 de 100. Si se hubiera modificado mucho más en algunos de los pasos intermedios, ese índice de similitud podría caer por debajo del 50% ... sin embargo, si tuviéramos que comparar una revisión solo un poco después de 992cb206 a 992cb206 (como lo haría la git blame ), tal vez el índice de similitud entre esos dos archivos podría ser mayor.

Lo que se necesita (y falta) es que git rev-list implemente --follow , para que todos los comandos que usen git rev-list internamente, es decir, la mayoría de los comandos que funcionan en más de una revisión, puedan hacer el truco. En el camino, sería bueno si funcionara en la otra dirección (actualmente - --follow es más nuevo a más antiguo solamente, es decir, funciona bien con git blame y funciona bien con git log siempre que no solicite el más antiguo la historia primero con --reverse ).