Git - Diferencia entre 'asumir-sin cambios' y 'saltar-árbol de trabajo'

450

Tengo cambios locales en un archivo que no quiero comprometer en mi repositorio. Es un archivo de configuración para compilar la aplicación en un servidor, pero quiero compilar localmente con diferentes configuraciones. Naturalmente, el archivo siempre aparece cuando hago 'git status' como algo para organizar. Me gustaría ocultar este cambio en particular y no comprometerlo. No haré ningún otro cambio en el archivo.

Después de investigar un poco, veo 2 opciones: 'asumir-sin cambios' y 'omitir-árbol de trabajo'. Una pregunta anterior aquí habla sobre ellos, pero en realidad no explica sus diferencias. Mi pregunta es esta: ¿en qué se diferencian los dos comandos? ¿Por qué alguien usaría uno u otro?

ckb
fuente
1
Por lo general, estoy usando .gitignorepara fines similares. ¿Esta solución funcionaría para usted?
samuil
45
samuil, .gitignore ignora agregar, no cambiar. Cuando el archivo ya está en git, se rastreará el evento si aparece en .gitignore
Grigory
pero ¿no se puede eliminar todo y agregar todo para "actualizar" como se explica aquí? stackoverflow.com/questions/7075923/… @Grigory
Daniel Springer
2
El archivo no debe ser ignorado, si entiendo correctamente la intención de OP. El archivo debe estar en el repositorio, pero estos cambios muy específicos que ha realizado no deben confirmarse, al menos no ahora.
Simone

Respuestas:

666

Que desea skip-worktree.

assume-unchangedestá diseñado para casos en los que es costoso verificar si se ha modificado un grupo de archivos; cuando configura el bit, git(por supuesto) supone que los archivos correspondientes a esa parte del índice no se han modificado en la copia de trabajo. Por lo tanto, evita un lío de statllamadas. Este bit se pierde cada vez que cambia la entrada del archivo en el índice (por lo tanto, cuando el archivo se cambia en sentido ascendente).

skip-worktreees más que eso: incluso cuando git sabe que el archivo ha sido modificado (o necesita ser modificado por algo reset --hardsimilar), fingirá que no lo ha sido, utilizando la versión del índice en su lugar. Esto persiste hasta que se descarta el índice.

Hay un buen resumen de las ramificaciones de esta diferencia y los casos de uso típicos aquí: http://fallengamer.livejournal.com/93321.html .

De ese artículo:

  • --assume-unchangedasume que un desarrollador no debería cambiar un archivo. Este indicador está destinado a mejorar el rendimiento de las carpetas que no cambian, como los SDK.
  • --skip-worktreees útil cuando le indica a git que no toque un archivo específico porque los desarrolladores deberían cambiarlo. Por ejemplo, si el repositorio principal en sentido ascendente aloja algunos archivos de configuración listos para producción y no desea confirmar accidentalmente los cambios en esos archivos, --skip-worktreeeso es exactamente lo que desea.
Borealid
fuente
3
Eso tiene sentido. skip-worktree realmente parece ser el camino a seguir. ¡Gracias!
ckb
100
Una pequeña nota para ahorrar unos segundos buscando y leyendo. Para cancelar los --skip-worktreeefectos y desactivar la bandera hay una --no-skip-worktreeopción. Funciona exactamente de la misma manera. Esto es útil en caso de que una mano se haya deslizado y se hayan marcado archivos incorrectos, o si las circunstancias hubieran cambiado y los archivos previamente omitidos ya no se deben ignorar.
drdaeman
18
Para responder a mi propia pregunta anterior, la diferencia entre usar --skip-worktreey el .git/info/excludearchivo es que el primero funcionará incluso para los archivos que se rastrean actualmente. .git/info/exclude, como .gitignore, solo evitará agregar accidentalmente archivos no rastreados al índice, pero no realizará cambios en los archivos que ya están rastreados.
LinusR
13
¿Puede esto ser empujado al control remoto y ser preservado por todos los clones?
CMCDragonkai
44
Solo el uso , señora:git update-index --skip-worktree <file_name>
ruffin
108

Nota: fallengamer hizo algunas pruebas en 2011 (por lo que pueden estar desactualizadas), y aquí están sus hallazgos :

Operaciones

  • El archivo se cambia tanto en el repositorio local como en sentido ascendente
    git pull:
    Git conserva los cambios locales de todos modos.
    Por lo tanto, no perderá accidentalmente ningún dato que haya marcado con ninguna de las banderas.
    • Archivo con assume-unchangedbandera: Git no sobrescribiría el archivo local. En cambio, generaría conflictos y consejos sobre cómo resolverlos
    • Archivo con skip-worktreebandera: Git no sobrescribiría el archivo local. En cambio, generaría conflictos y consejos sobre cómo resolverlos

  • El archivo se cambia tanto en el repositorio local como en sentido ascendente, tratando de extraer de todos modos Usando los resultados en un trabajo manual adicional, pero al menos no perderías ningún dato si tuvieras algún cambio local.
    git stash
    git pull
    skip-worktree
    • Archivo con assume-unchangedbandera: descarta todos los cambios locales sin posibilidad de restaurarlos. El efecto es como ' git reset --hard'. ' git pull' la llamada tendrá éxito
    • Archivo con skip-worktreebandera: Stash no funcionaría en skip-worktreearchivos. ' git pull' fallará con el mismo error que el anterior. El desarrollador se ve obligado a restablecer manualmente el skip-worktreeindicador para poder ocultar y completar el error pull.

  • Sin cambios locales, se modificó el archivo ascendente Ambas banderas no le impedirán obtener cambios ascendentes. Git detecta que incumpliste la promesa y elige reflejar la realidad al restablecer la bandera.
    git pull
    assume-unchanged
    • Archivo con assume-unchangedbandera: el contenido se actualiza, la bandera se pierde.
      ' git ls-files -v' mostraría que la bandera se modifica a H(desde h).
    • Archivo con skip-worktreebandera: el contenido se actualiza, la bandera se conserva.
      ' git ls-files -v' mostraría la misma Sbandera que antes pull.

  • Con el archivo local cambiado, Git no toca el archivo y refleja la realidad (el archivo prometió que no se modificó realmente fue cambiado) para el archivo.
    git reset --hard
    skip-worktreeassume-unchanged
    • Archivo con assume-unchangedbandera: se revierte el contenido del archivo. La bandera se restablece a H(desde h).
    • Archivo con skip-worktreebandera: el contenido del archivo está intacto. La bandera sigue siendo la misma.

Agrega el siguiente análisis:

  • Parece que se skip-worktreeestá esforzando mucho por preservar sus datos locales . Pero no le impide obtener cambios aguas arriba si es seguro. Además, git no restablece la bandera pull.
    Pero ignorar el reset --hardcomando ' ' podría convertirse en una desagradable sorpresa para un desarrollador.

  • Assume-unchangedLa bandera podría perderse en la pulloperación y los cambios locales dentro de dichos archivos no parecen ser importantes para git.

Ver:

Él concluye:

En realidad, ninguna de las banderas es lo suficientemente intuitiva .

  • assume-unchangedasume que un desarrollador no debería cambiar un archivo. Si se modificó un archivo, ese cambio no es importante. Este indicador está destinado a mejorar el rendimiento de las carpetas que no cambian, como los SDK.
    Pero si la promesa se rompe y se cambia un archivo, git revierte la bandera para reflejar la realidad. Probablemente esté bien tener algunos indicadores inconsistentes en carpetas generalmente no destinadas a ser cambiadas.

  • Por otro lado, skip-worktreees útil cuando le indica a git que no toque nunca un archivo específico. Eso es útil para un archivo de configuración ya rastreado.
    El repositorio principal ascendente aloja algunas configuraciones listas para producción, pero le gustaría cambiar algunas configuraciones en la configuración para poder hacer algunas pruebas locales. Y no desea verificar accidentalmente los cambios en dicho archivo para afectar la configuración de producción. En ese caso skip-worktreehace escena perfecta.


Con Git 2.25.1 (febrero de 2020), el "En realidad ninguna de las banderas es lo suficientemente intuitiva" mencionado anteriormente se aclara más:

Ver commit 7a2dc95 , commit 1b13e90 (22 de enero de 2020) por brian m. carlson ( bk2204) .
(Fusionada por Junio ​​C Hamano - gitster- en commit 53a8329 , 30 de enero de 2020)
( Lista de correo de Git )

doc: disuade a los usuarios de intentar ignorar los archivos rastreados

Firmado por: Jeff King
Firmado por: brian m. carlson

Es bastante común que los usuarios deseen ignorar los cambios en un archivo que Git rastrea.

Los escenarios comunes para este caso son las configuraciones IDE y los archivos de configuración, que generalmente no deben rastrearse y posiblemente generarse a partir de archivos rastreados utilizando un mecanismo de plantillas.

Sin embargo, los usuarios aprenden acerca de los bits de asumir-sin cambios y omitir-árbol de trabajo e intentan usarlos para hacer esto de todos modos.

Esto es problemático, porque cuando se establecen estos bits, muchas operaciones se comportan como el usuario espera, pero generalmente no ayudan cuando se git checkoutnecesita reemplazar un archivo.

No hay un comportamiento sensible en este caso, porque a veces los datos son preciosos, como ciertos archivos de configuración, y a veces son datos irrelevantes que el usuario estaría encantado de descartar.

Dado que esta no es una configuración compatible y los usuarios son propensos a hacer un mal uso de las funciones existentes para fines no deseados, causando tristeza y confusión general , documentemos el comportamiento existente y las trampas en la documentación para git update-indexque los usuarios sepan que deben explorar soluciones alternativas.

Además, proporcionemos una solución recomendada para tratar el caso común de los archivos de configuración, ya que existen enfoques bien conocidos que se utilizan con éxito en muchos entornos.

La git update-indexpágina del manual ahora incluye:

Los usuarios a menudo intentan usar los bits assume-unchangedy skip-worktreepara decirle a Git que ignore los cambios en los archivos que se rastrean. Esto no funciona como se esperaba, ya que Git aún puede verificar los archivos de árbol de trabajo contra el índice al realizar ciertas operaciones. En general, Git no proporciona una forma de ignorar los cambios en los archivos rastreados, por lo que se recomiendan soluciones alternativas.

Por ejemplo, si el archivo que desea cambiar es algún tipo de archivo de configuración, el repositorio puede incluir un archivo de configuración de muestra que luego puede copiarse en el nombre ignorado y modificarse. El repositorio puede incluso incluir un script para tratar el archivo de muestra como una plantilla, modificándolo y copiándolo automáticamente.

La última parte es lo que describo como un controlador de filtro de contenido típico basado en scripts de borrones / limpieza .

VonC
fuente
77
Si tiene omitir el árbol de trabajo en un archivo y los cambios ascendentes, obtiene "por favor confirme o oculte" cuando intenta extraer, aunque el estado de git no informa que el archivo ha cambiado. ¿Cómo puede evitar esto, para que los cambios locales puedan persistir mientras la gente está jugando con la configuración de producción en origen?
GreenAsJade
3
Sí, puedo confirmar que sí. Significa que todavía es muy difícil tener un archivo local que simplemente desea mantener de manera diferente al origen.
GreenAsJade
1
@GreenAsJade de lo que parece antiguo. ¿Hay alguna posibilidad de que pueda probarlo con un 2.2.x?
VonC
1
@VonC, el enlace al "comentario de Junio" no está en el historial de revisiones. ¿A esto te referías?
Michael - ¿Dónde está Clay Shirky?
1
@Michael Buena captura, gracias. He vuelto a poner ese enlace en la respuesta.
VonC