Manejo de nombres de archivos en git

440

Leí que al cambiar el nombre de los archivos en git , debe confirmar cualquier cambio, realizar su cambio de nombre y luego organizar su archivo renombrado. Git reconocerá el archivo por el contenido, en lugar de verlo como un nuevo archivo sin seguimiento, y mantendrá el historial de cambios.

Sin embargo, haciendo esto esta noche, terminé volviendo a hacerlo git mv.

> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   index.html
#

Cambiar el nombre de mi hoja de estilo en Finder de iphone.cssamobile.css

> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   index.html
#
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   deleted:    css/iphone.css
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   css/mobile.css

Entonces git ahora piensa que eliminé un archivo CSS y agregué uno nuevo. No es lo que quiero, deshaga el cambio de nombre y deje que git haga el trabajo.

> $ git reset HEAD .
Unstaged changes after reset:
M   css/iphone.css
M   index.html

De vuelta a donde empecé.

> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   index.html
#

Vamos a usar git mven su lugar.

> $ git mv css/iphone.css css/mobile.css
> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   renamed:    css/iphone.css -> css/mobile.css
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   index.html
#

Parece que estamos bien. Entonces, ¿por qué git no reconoció el cambio de nombre la primera vez cuando usé Finder?

Greg K
fuente
29
Git rastrea el contenido, no los archivos, por lo que no importa cómo coloque su índice en el estado adecuado, add+rmo mvbien, produce el mismo resultado. Git luego usa su detección de cambio de nombre / copia para hacerle saber que fue un cambio de nombre. La fuente que citó también es inexacta. Realmente no importa si modifica + renombra en la misma confirmación o no. Cuando realiza una diferencia entre la modificación y el cambio de nombre, la detección de cambio de nombre lo verá como un cambio de nombre + modificación, o si la modificación es una reescritura total, se mostrará como agregado y eliminado, pero no importa cómo lo haya realizado. eso.
Cascabel
66
Si esto es cierto, ¿por qué no lo detectó con mi cambio de nombre usando Finder?
Greg K
26
git mv old newActualiza automáticamente el índice. Cuando cambie el nombre fuera de Git, tendrá que hacer git add newy git rm oldorganizar los cambios en el índice. Una vez que haya hecho esto git status, funcionará como espera.
Chris Johnsen
44
Acabo de mover un montón de archivos a un public_htmldirectorio, que se rastrean en git. Habiendo realizado git add .y git commit, todavía mostraba un montón de archivos 'eliminados' git status. Realicé una git commit -ay las eliminaciones se confirmaron, pero ahora no tengo historial en los archivos que viven public_htmlahora. Este flujo de trabajo no es tan suave como me gustaría.
Greg K

Respuestas:

352

Para git mvla página del manual dice

El índice se actualiza después de completar con éxito, […]

Entonces, al principio, debe actualizar el índice por su cuenta (mediante el uso git add mobile.css). Sin embargo
git status , aún mostrará dos archivos diferentes

$ git status
# On branch master
warning: LF will be replaced by CRLF in index.html
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   index.html
#       new file:   mobile.css
#
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       deleted:    iphone.css
#

Puede obtener un resultado diferente ejecutando git commit --dry-run -a, lo que da como resultado lo que espera:

Tanascius@H181 /d/temp/blo (master)
$ git commit --dry-run -a
# On branch master
warning: LF will be replaced by CRLF in index.html
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   index.html
#       renamed:    iphone.css -> mobile.css
#

No puedo decirte exactamente por qué vemos estas diferencias entre git statusy
git commit --dry-run -a, pero aquí hay una pista de Linus :

git realmente ni siquiera se preocupa por toda la "detección de cambio de nombre" internamente, y cualquier confirmación que haya realizado con los cambios de nombre es totalmente independiente de la heurística que luego usamos para mostrar los cambios de nombre.

A dry-runutiliza los mecanismos de cambio de nombre reales, mientras que git statusprobablemente no.

tanascio
fuente
1
No mencionaste el paso donde lo hiciste git add mobile.css. Sin él git status -a, solo habría "visto" la eliminación del iphone.cssarchivo rastreado anteriormente , pero no habría tocado el nuevo mobile.cssarchivo no rastreado . Además, git status -ano es válido con Git 1.7.0 y versiones posteriores. "" Git status "ya no es" git commit --dry-run "." en kernel.org/pub/software/scm/git/docs/RelNotes-1.7.0.txt . Úselo git commit --dry-run -asi desea esta funcionalidad. Como han dicho otros, solo actualice el índice y git statusfuncionará como lo espera el OP.
Chris Johnsen
3
Si lo hace normalmente git commit, no confirmará el archivo renombrado y el árbol de trabajo sigue siendo el mismo. git commit -aderrota prácticamente todos los aspectos del flujo de trabajo / modelo de pensamiento de git: cada cambio está comprometido. ¿Qué sucede si solo desea cambiar el nombre del archivo, pero confirmar los cambios index.htmlen otra confirmación?
knittl
@ Chris: sí, por supuesto que agregué mobile.csslo que debería haber mencionado. Pero ese es el punto de mi respuesta: la página del manual dice eso the index is updatedcuando lo usas git-mv. Gracias por la status -aaclaración, usé git 1.6.4
tanascius
44
¡Gran respuesta! Estaba golpeando mi cabeza contra la pared tratando de entender por qué git statusno estaba detectando el cambio de nombre. ¡La ejecución git commit -a --dry-rundespués de agregar mis "nuevos" archivos mostró los cambios de nombre y finalmente me dio confianza para comprometerme!
stephen.hanson
1
En git 1.9.1 git statusahora se comporta como git commit.
Jacques René Mesrine
77

Debe agregar los dos archivos modificados al índice antes de que git lo reconozca como un movimiento.

La única diferencia entre mv old newy git mv old newes que git mv también agrega los archivos al índice.

mv old newentonces git add -Ahabría funcionado también.

Tenga en cuenta que no puede usarlo simplemente git add .porque eso no agrega eliminaciones al índice.

Vea la diferencia entre "git add -A" y "git add".

no rectangular
fuente
3
Gracias por el git add -Aenlace, muy útil, ya que estaba buscando ese acceso directo.
PhiLho
55
Tenga en cuenta que con git 2, git add . no añadir el traslado al índice.
Nick McCurdy
19

Lo mejor es probarlo por ti mismo.

mkdir test
cd test
git init
touch aaa.txt
git add .
git commit -a -m "New file"
mv aaa.txt bbb.txt
git add .
git status
git commit --dry-run -a

Ahora git status y git commit --dry-run -a muestra dos resultados diferentes donde git status muestra bbb.txt cuando se elimina un nuevo archivo / aaa.txt, y los comandos --dry-run muestran el cambio de nombre real.

~/test$ git status

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   bbb.txt
#
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   deleted:    aaa.txt
#


/test$ git commit --dry-run -a

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   renamed:    aaa.txt -> bbb.txt
#

Ahora ve y haz el check-in.

git commit -a -m "Rename"

Ahora puede ver que el archivo ha cambiado de nombre y que lo que se muestra en estado de git es incorrecto.

Moraleja de la historia: si no está seguro de si se cambió el nombre de su archivo, emita un "git commit --dry-run -a". Si muestra que el archivo se renombra, está listo para comenzar.

dimuthu
fuente
3
Por lo que importa para Git, ambos son correctos . Sin embargo, esto último es estar más cerca de cómo usted, como comisionado, probablemente lo vea. La verdadera diferencia entre cambiar el nombre y eliminar + crear es solo en el nivel del sistema operativo / sistema de archivos (por ejemplo, el mismo # de inodo frente al nuevo # de inodo), que a Git realmente no le importa mucho.
Alois Mahdal
17

Para git 1.7.x, los siguientes comandos me funcionaron:

git mv css/iphone.css css/mobile.css
git commit -m 'Rename folder.' 

No había necesidad de git add, ya que el archivo original (es decir, css / mobile.css) ya estaba en los archivos confirmados anteriormente.

GrigorisG
fuente
66
Esta. Todas las otras respuestas son ridícula e innecesariamente complejas. Esto mantiene el historial de archivos entre confirmaciones para que las fusiones antes / después del cambio de nombre del archivo no se rompan.
Phlucious
10

tienes que acceder al git add css/mobile.cssnuevo archivo y git rm css/iphone.css, por lo tanto, git lo sabe. entonces mostrará la misma salida engit status

puede verlo claramente en la salida de estado (el nuevo nombre del archivo):

# Untracked files:
#   (use "git add <file>..." to include in what will be committed)

y (el antiguo nombre):

# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)

Creo que detrás de escena git mvno es más que un script de envoltura que hace exactamente eso: eliminar el archivo del índice y agregarlo con un nombre diferente

knittl
fuente
No pensé que tenía que hacerlo git rm css/iphone.cssporque pensé que esto eliminaría la historia existente. Tal vez estoy malinterpretando el flujo de trabajo en git.
Greg K
44
@ Greg K: git rmno eliminará el historial. Solo elimina una entrada del índice para que la próxima confirmación no tenga la entrada. Sin embargo, seguirá existiendo en los compromisos ancestrales. De lo que puede estar confundido es que (por ejemplo) git log -- newse detendrá en el punto donde se comprometió git mv old new. Si desea seguir los cambios de nombre, use git log --follow -- new.
Chris Johnsen
9

Pensemos en sus archivos desde la perspectiva de git.

Tenga en cuenta que git no rastrea ningún metadato sobre sus archivos

Tu repositorio tiene (entre otros)

$ cd repo
$ ls
...
iphone.css
...

y está bajo control de git:

$ git ls-files --error-unmatch iphone.css &>/dev/null && echo file is tracked
file is tracked

Prueba esto con:

$ touch newfile
$ git ls-files --error-unmatch newfile &>/dev/null && echo file is tracked
(no output, it is not tracked)
$ rm newfile

Cuando tu lo hagas

$ mv iphone.css mobile.css

Desde la perspectiva de git,

  • no hay iphone.css (se elimina -git advierte sobre eso-).
  • hay un nuevo archivo mobile.css .
  • Esos archivos no tienen ninguna relación.

Entonces, git aconseja sobre los archivos que ya conoce ( iphone.css ) y los nuevos archivos que detecta ( mobile.css ), pero solo cuando los archivos están en el índice o HEAD git comienza a verificar su contenido.

En este momento, ni "iphone.css deletion" ni mobile.css están en el índice.

Agregue la eliminación de iphone.css al índice

$ git rm iphone.css

git te dice exactamente lo que sucedió: ( iphone.css se elimina. No pasó nada más)

luego agregue un nuevo archivo mobile.css

$ git add mobile.css

Esta vez, tanto la eliminación como el nuevo archivo están en el índice. Ahora git detecta que el contexto es el mismo y lo expone como un cambio de nombre. De hecho, si los archivos son 50% similares, lo detectará como un cambio de nombre, lo que le permite cambiar un poco mobile.css mientras mantiene la operación como un cambio de nombre.

Ver esto es reproducible en git diff. Ahora que sus archivos están en el índice, debe usarlos --cached. Edite mobile.css un poco, agréguelo al índice y vea la diferencia entre:

$ git diff --cached 

y

$ git diff --cached -M

-Mes la opción "detectar cambios de nombre" para git diff. -Msignifica -M50%(50% o más de similitud hará que git lo exprese como un cambio de nombre) pero puede reducir esto a -M20%(20%) si edita mucho mobile.css.

albfan
fuente
8

Paso 1: cambie el nombre del archivo de oldfile a newfile

git mv #oldfile #newfile

Paso 2: git commit y agrega comentarios

git commit -m "rename oldfile to newfile"

Paso 3: empuje este cambio a servidor remoto

git push origin #localbranch:#remotebranch
Haimei
fuente
1
Agregue algún comentario para que sea útil para OP
Devrath,
El paso 2 es innecesario. Después git mv, el nuevo archivo ya está en el índice.
Alois Mahdal
7

Git reconocerá el archivo por el contenido, en lugar de verlo como un nuevo archivo sin seguimiento

Ahí es donde te equivocaste.

Solo después de agregar el archivo, git lo reconocerá por el contenido.

Hasen
fuente
Exactamente. Cuando se organiza, git mostrará el cambio de nombre correctamente.
Max MacLeod
3

No diste los resultados de tu movimiento de búsqueda. Creo que si hiciste el movimiento a través del Finder y luego lo hiciste git add css/mobile.css ; git rm css/iphone.css, git calcularía el hash del nuevo archivo y solo entonces te darías cuenta de que los hash de los archivos coinciden (y, por lo tanto, es un cambio de nombre).

Mike Seplowitz
fuente
2

En casos en los que realmente tiene que cambiar el nombre de los archivos manualmente, por ejemplo. usando una secuencia de comandos para cambiar el nombre de un lote de archivos por lotes, luego usando git add -A .funcionó para mí.

pckben
fuente
2

Para usuarios de Xcode: si cambia el nombre de su archivo en Xcode, verá que el icono de la insignia cambia para agregarse. Si realiza una confirmación utilizando XCode, en realidad creará un nuevo archivo y perderá el historial.

Una solución alternativa es fácil, pero debe hacerlo antes de comprometerse a usar Xcode:

  1. Haz un git Status en tu carpeta. Debería ver que los cambios por etapas son correctos:

renombrado: Project / OldName.h -> Project / NewName.h renombrado: Project / OldName.m -> Project / NewName.m

  1. hacer commit -m 'cambio de nombre'

Luego regrese a XCode y verá que la insignia cambió de A a M y se guarda para confirmar cambios adicionales al usar xcode ahora.

doozHombres
fuente