ver tipos tag modificados log etiquetas crear archivos git rename mv

tipos - git ver archivos modificados



¿Es posible mover/renombrar archivos en Git y mantener su historial? (9)

Me gustaría cambiar el nombre / mover un subárbol de proyecto en Git moviéndolo desde

/project/xyz

a

/components/xyz

Si utilizo git mv project components formato, se pierde todo el historial de confirmaciones del xyz project . ¿Hay alguna forma de mover esto de manera que la historia se mantenga?


Me gustaría cambiar el nombre / mover un subárbol de proyecto en Git moviéndolo desde

/project/xyz

a

/ componentes / xyz

Si utilizo git mv project components formato, se pierde todo el historial de confirmaciones del proyecto xyz .

No (8 años después, Git 2.19, Q3 2018), porque Git detectará el cambio de nombre del directorio , y esto ahora está mejor documentado.

Ver commit b00bf1c , commit 1634688 , commit 0661e49 , commit 4d34dff , commit 983f464 , commit c840e1a , commit 9929430 (27 Jun 2018), y commit d4e8062 , commit 5dacd4a (25 Jun 2018) de Elijah Newren ( newren ) .
(Combinado por Junio ​​C Hamano - gitster - in commit 0ce5a69 , 24 de julio de 2018)

Eso ahora se explica en Documentation/technical/directory-rename-detection.txt :

Ejemplo:

Cuando todos los x/a , x/b y x/c han movido a z/a , z/b y z/c , es probable que x/d agregado mientras tanto también quiera moverse a z/d teniendo en cuenta que todo el directorio '' x '' se movió a '' z ''.

Pero son muchos otros casos, como:

un lado del historial renombra x -> z , y el otro renombra algún archivo a x/e , haciendo que la combinación haga un renombrado transitivo.

Para simplificar la detección del cambio de nombre del directorio, Git aplica esas reglas:

Un par de reglas básicas limitan cuando se aplica la detección de cambio de nombre de directorio:

  1. Si un directorio dado todavía existe en ambos lados de una combinación, no consideramos que haya sido renombrado.
  2. Si un subconjunto de archivos a ser renombrados tiene un archivo o directorio de la forma (o se interpondría entre ellos), "desactive" el cambio de nombre del directorio para esas subrutas específicas e informe el conflicto al usuario .
  3. Si el otro lado de la historia hizo un cambio de nombre de directorio a una ruta a la que su lado de historia cambió de nombre, entonces ignore ese cambio de nombre en particular desde el otro lado de la historia para cualquier cambio de nombre de directorio implícito (pero advierta al usuario).

Puedes ver muchas pruebas en t/t6043-merge-rename-directories.sh , que también indican que:

  • a) Si los nombres dividen un directorio en dos o más, el directorio con la mayoría de los nombres, "gana".
  • b) Evite la detección de cambio de nombre de directorio para una ruta, si esa ruta es la fuente de un cambio de nombre en cualquier lado de una combinación.
  • c) Solo aplique nombres de directorio implícitos a los directorios si el otro lado del historial es el que está haciendo el cambio de nombre.

No.

La respuesta corta es NO , no es posible cambiar el nombre de un archivo en Git y recordar el historial. Y es un dolor.

Se rumorea que git log --follow --find-copies-harder funcionará pero no funciona para mí, incluso si no hay cambios en el contenido del archivo, y los movimientos se han realizado con git mv .

(Inicialmente usé Eclipse para renombrar y actualizar paquetes en una operación, lo que puede haber confundido a git. Pero eso es algo muy común. --follow parece funcionar si solo se realiza un mv y luego un commit y the mv no está demasiado lejos.)

Linus dice que se supone que debes comprender de forma integral todo el contenido de un proyecto de software, sin necesidad de rastrear archivos individuales. Bueno, tristemente, mi pequeño cerebro no puede hacer eso.

Es realmente molesto que tantas personas hayan repetido sin pensar la afirmación de que git realiza un seguimiento automático de los movimientos. Han perdido mi tiempo. Git no hace tal cosa. Por diseño (!) Git no rastrea los movimientos en absoluto.

Mi solución es cambiar el nombre de los archivos a sus ubicaciones originales. Cambie el software para ajustar el control de la fuente. Con git parece que necesitas hacerlo bien la primera vez.

Desafortunadamente, eso rompe Eclipse, que parece usar --follow .
git log --follow A veces no muestra el historial completo de archivos con complicados renombrar historiales aunque git log haga. (No se por que.)

(Hay algunos hacks demasiado inteligentes que regresan y vuelven a comprometerse con el trabajo anterior, pero son bastante aterradores. Ver GitHub-Gist: emiller/git-mv-with-history )


Objetivo

  • Use git am (inspirado en Smar , tomado de Exherbo )
  • Agregar historial de confirmación de archivos copiados / movidos
  • De un directorio a otro
  • O de un repositorio a otro

Limitación

  • No se conservan etiquetas y ramas.
  • El historial se corta en el nombre de archivo de ruta (cambio de nombre de directorio)

Resumen

  1. Extraer el historial en formato de correo electrónico usando
    git log --pretty=email -p --reverse --full-index --binary
  2. Reorganizar el árbol de archivos y actualizar los nombres de archivos
  3. Añadir nueva historia usando
    cat extracted-history | git am --committer-date-is-author-date

1. Extraer el historial en formato de correo electrónico.

Ejemplo: extraer el historial de file4 , file4 y file5

my_repo ├── dirA │   ├── file1 │   └── file2 ├── dirB ^ │ ├── subdir | To be moved │ │ ├── file3 | with history │ │ └── file4 | │   └── file5 v └── dirC ├── file6 └── file7

Establecer / limpiar el destino

export historydir=/tmp/mail/dir # Absolute path rm -rf "$historydir" # Caution when cleaning the folder

Extraer el historial de cada archivo en formato de correo electrónico.

cd my_repo/dirB find -name .git -prune -o -type d -o -exec bash -c ''mkdir -p "$historydir/${0%/*}" && git log --pretty=email -p --stat --reverse --full-index --binary -- "$0" > "$historydir/$0"'' {} '';''

Desafortunadamente, la opción --follow o --find-copies-harder no se puede combinar con --reverse . Esta es la razón por la cual el historial se corta cuando se cambia el nombre del archivo (o cuando se cambia el nombre de un directorio principal).

Historial temporal en formato de correo electrónico:

/tmp/mail/dir ├── subdir │ ├── file3 │ └── file4 └── file5

Dan Bonachea sugiere invertir los bucles del comando de generación de registro de git en este primer paso: en lugar de ejecutar el registro de git una vez por archivo, ejecútelo exactamente una vez con una lista de archivos en la línea de comandos y genere un solo registro unificado. De este modo, las confirmaciones que modifican varios archivos siguen siendo una única confirmación en el resultado, y todas las confirmaciones nuevas mantienen su orden relativo original. Tenga en cuenta que esto también requiere cambios en el segundo paso a continuación al volver a escribir los nombres de archivo en el registro (ahora unificado).

2. Reorganizar el árbol de archivos y actualizar los nombres de archivos

Supongamos que desea mover estos tres archivos en este otro repositorio (puede ser el mismo repositorio).

my_other_repo ├── dirF │ ├── file55 │ └── file56 ├── dirB # New tree │ ├── dirB1 # from subdir │ │ ├── file33 # from file3 │ │ └── file44 # from file4 │ └── dirB2 # new dir │ └── file5 # from file5 └── dirH └── file77

Por eso reorganiza tus archivos:

cd /tmp/mail/dir mkdir -p dirB/dirB1 mv subdir/file3 dirB/dirB1/file33 mv subdir/file4 dirB/dirB1/file44 mkdir -p dirB/dirB2 mv file5 dirB/dirB2

Tu historia temporal es ahora:

/tmp/mail/dir └── dirB ├── dirB1 │ ├── file33 │ └── file44 └── dirB2 └── file5

Cambie también los nombres de archivos dentro de la historia:

cd "$historydir" find * -type f -exec bash -c ''sed "/^diff --git a/|^--- a/|^+++ b/s:/( [ab]/)/[^ ]*:/1/$0:g" -i "$0"'' {} '';''

3. Aplicar nueva historia.

Tu otro repo es:

my_other_repo ├── dirF │ ├── file55 │ └── file56 └── dirH └── file77

Aplicar confirmaciones de archivos de historial temporal:

cd my_other_repo find "$historydir" -type f -exec cat {} + | git am --committer-date-is-author-date

--committer-date-is-author-date conserva los sellos de tiempo de confirmación originales (comentario de Dan Bonachea ).

Tu otro repo es ahora:

my_other_repo ├── dirF │ ├── file55 │ └── file56 ├── dirB │ ├── dirB1 │ │ ├── file33 │ │ └── file44 │ └── dirB2 │ └── file5 └── dirH └── file77

Use el git status para ver la cantidad de confirmaciones listas para ser empujadas :-)

Truco adicional: verifique los archivos renombrados / movidos dentro de su repositorio

Para listar los archivos que han sido renombrados:

find -name .git -prune -o -exec git log --pretty=tformat:'''' --numstat --follow {} '';'' | grep ''=>''

Más personalizaciones: puede completar el comando git log usando las opciones --find-copies-harder o --reverse . También puede eliminar las dos primeras columnas usando cut -f3- y grepping el patrón completo ''{. * =>. *}''.

find -name .git -prune -o -exec git log --pretty=tformat:'''' --numstat --follow --find-copies-harder --reverse {} '';'' | cut -f3- | grep ''{.* => .*}''


Es posible cambiar el nombre de un archivo y mantener el historial intacto, aunque hace que el nombre del archivo se cambie a lo largo de todo el historial del repositorio. Esto es probablemente solo para los obsesivos amantes de git-log, y tiene algunas implicaciones serias, incluyendo estas:

  • Podría estar reescribiendo un historial compartido, que es lo más importante que NO debe usar mientras usa Git. Si alguien más ha clonado el repositorio, lo romperás haciendo esto. Tendrán que volver a clonar para evitar dolores de cabeza. Esto podría estar bien si el cambio de nombre es lo suficientemente importante, pero deberá considerar esto con cuidado, ¡podría terminar molestando a toda una comunidad de código abierto!
  • Si ha hecho referencia al archivo usando su nombre anterior en el historial del repositorio, efectivamente está rompiendo las versiones anteriores. Para remediar esto, tendrás que hacer un poco más de salto de aro. No es imposible, solo tedioso y posiblemente no valga la pena.

Ahora, como todavía estás conmigo, probablemente seas un desarrollador en solitario que cambia el nombre de un archivo completamente aislado. ¡Vamos a mover un archivo usando filter-tree !

Suponga que va a mover un archivo old a una carpeta dir y asígnele el nombre new

Esto se podría hacer con git mv old dir/new && git add -u dir/new , pero eso rompe el historial.

En lugar:

git filter-branch --tree-filter ''if [ -f old ]; then mkdir dir && mv old dir/new; fi'' HEAD

rehacerá cada confirmación en la rama, ejecutando el comando en los tics para cada iteración. Muchas cosas pueden salir mal cuando haces esto. Normalmente hago pruebas para ver si el archivo está presente (de lo contrario, todavía no se ha movido) y luego realizo los pasos necesarios para calzar el árbol a mi gusto. Aquí puede pasar por los archivos para modificar las referencias al archivo y así sucesivamente. ¡Tírate a ti mismo! :)

Cuando se completa, el archivo se mueve y el registro está intacto. Te sientes como un pirata ninja.

También; El directorio mkdir solo es necesario si mueve el archivo a una nueva carpeta, por supuesto. El si evitará la creación de esta carpeta antes en el historial que existe su archivo.


Git detecta los cambios de nombre en lugar de persistir en la operación con la confirmación, por lo que no importa si usa git mv o mv .

El comando de registro toma un argumento --follow que continúa el historial antes de una operación de cambio de nombre, es decir, busca contenido similar utilizando las heurísticas:

http://git-scm.com/docs/git-log

Para buscar el historial completo, use el siguiente comando:

git log --follow ./path/to/file


Hago mover los archivos y luego hago

git add -A

que puso en el área de sataging todos los archivos borrados / nuevos. Aquí git se da cuenta de que el archivo se mueve.

git commit -m "my message" git push

No sé por qué, pero esto funciona para mí.


Hago:

git mv {old} {new} git add -u {new}


Si bien es el núcleo de git, la plomería de git no realiza un seguimiento de los nombres, el historial que se muestra con el registro de git "porcelana" puede detectarlos si lo desea.

Para un git log dado use la opción -M:

git log -p -M

Con una versión actual de git.

Esto funciona para otros comandos como git diff también.

Hay opciones para hacer las comparaciones más o menos rigurosas. Si cambia el nombre de un archivo sin realizar cambios significativos en el archivo al mismo tiempo, facilita que el registro de git y sus amigos detecten el cambio de nombre. Por esta razón, algunas personas cambian el nombre de los archivos en una confirmación y los cambian en otra.

Hay un costo en el uso de la CPU siempre que le pida a git que busque dónde se ha cambiado el nombre de los archivos, así que si lo usa o no, y cuándo, depende de usted.

Si desea que su historial siempre se informe con detección de cambio de nombre en un repositorio particular, puede usar:

git config diff.renames 1

Se detectan archivos que se mueven de un directorio a otro. Aquí hay un ejemplo:

commit c3ee8dfb01e357eba1ab18003be1490a46325992 Author: John S. Gruber <[email protected]> Date: Wed Feb 22 22:20:19 2017 -0500 test rename again diff --git a/yyy/power.py b/zzz/power.py similarity index 100% rename from yyy/power.py rename to zzz/power.py commit ae181377154eca800832087500c258a20c95d1c3 Author: John S. Gruber <[email protected]> Date: Wed Feb 22 22:19:17 2017 -0500 rename test diff --git a/power.py b/yyy/power.py similarity index 100% rename from power.py rename to yyy/power.py

Tenga en cuenta que esto funciona siempre que use diff, no solo con git log . Por ejemplo:

$ git diff HEAD c3ee8df diff --git a/power.py b/zzz/power.py similarity index 100% rename from power.py rename to zzz/power.py

Como prueba, hice un pequeño cambio en un archivo en una rama de características y lo confirmé. Luego, en la rama maestra, cambié el nombre del archivo, lo confirmé, luego realicé un pequeño cambio en otra parte del archivo y lo comprometí. Cuando fui a la rama de características y fusioné desde el maestro, la combinación cambió el nombre del archivo y combinó los cambios. Aquí está la salida de la fusión:

$ git merge -v master Auto-merging single Merge made by the ''recursive'' strategy. one => single | 4 ++++ 1 file changed, 4 insertions(+) rename one => single (67%)

El resultado fue un directorio de trabajo con el nombre del archivo y ambos cambios de texto realizados. Por lo tanto, es posible que git haga lo correcto a pesar de que no hace un seguimiento explícito de los nombres.

Esta es una respuesta tardía a una pregunta anterior, por lo que las otras respuestas pueden haber sido correctas para la versión git en ese momento.


git log --follow [file]

le mostrará la historia a través de los nombres.