varios estados de git, comprensión de bajo nivel del estado de git (cabeza, índice, estados de trabajo)

Imagina que tienes un tree con un file. Supongamos que tenemos solo dos estados posibles para este file, a y b . Si falta o no existe, ø . Estoy intentando build una tabla para entender todos los posibles git-status es. Creo que lo que tengo tiene sentido, sin embargo, he marcado con ** las áreas de pregunta:

 head index working status aaa no changes** aab unstaged:modified** aa ø unstaged:deleted** aba staged:modified, unstaged:modified abb staged:modified ab ø staged:modified, unstaged:deleted a ø a no changes** a ø b unstaged:modified** a ø ø staged:deleted** ø aa staged:new file ø ab staged:new file, unstaged: modified ø a ø staged:new file, unstaged: deleted ø ø a untracked 

Para cualquiera de los *, ø, * casi siento que depende del tree padre, y si eso está o no en el índice … por ejemplo, a, ø, ø es como si hubieras eliminado el blob de el tree de trabajo, y también el índice. Pero, ¿cómo se ve una eliminación del índice? ¿Se acaba de agregar el tree padre al área de ensayo con la input del tree eliminada? Si ese es el caso, entonces tiene sentido que no haya ninguna input en el índice para el blob en sí.

Para cualquier logging donde index = head, ( a,a,a , a,a,b , a,a,ø ) supongo que este estado no puede ocurrir a less que estuvieras jugando con los commands de plomería.

¡Si ve errores en mi tabla, y / o cualquier luz arrojada sobre esto sería genial! Gracias por adelantado.

Piense en el índice como "el siguiente compromiso propuesto"

Los sistemas de control de versiones basados ​​en Commit como Mercurial y Git necesitan una forma de distinguir entre lo que está en el compromiso actual -que, como cualquier compromiso, nunca se puede cambiar- y lo que estará en el siguiente compromiso que hagamos , que por supuesto tiene que ser modificable hasta que hagamos la confirmación. Mercurial utiliza esencialmente el tree de trabajo para esto, pero Git agrega una capa adicional que llama índice . Git puede asignar algunas properties adicionales al índice: un file se rastrea si y solo si está en el índice, por ejemplo. Durante las fusiones, el índice adquiere properties adicionales (que ignoraremos aquí :-)). Hay una última complicación que dejaré para el final.

Pero, ¿cómo se ve una eliminación del índice?

Eliminar un file del índice equivale a (bastante literalmente) eliminar el file del índice. Intente ejecutar git ls-files --stage para ver lo que quiero decir: para su primera fila ( a, a, a = no changes ) encontrará que hay un file llamado a en el índice. Para su fila a, ø, a , el file a simplemente no está más en el índice (y por lo tanto no estará en un nuevo compromiso que realice ahora).

Como resultado, llamar a un file "en etapas" podría ser un poco engañoso. Si a no está en absoluto en el índice (pero está en HEAD ), el file se "organiza para su eliminación", pero es más simple decir "no en el índice". Una vez que un file no está en el índice, tampoco se rastrea , por lo que la versión del tree de trabajo se convierte en un file sin seguimiento .

Esto significa que su input a, ø, b también es incorrecta: aquí el file se organiza para su eliminación, y la variante del tree de trabajo con b es un file sin seguimiento.

La input a, a, ø es quizás la más difícil de nombrar. El file aún está en el índice, por lo que estará en cada confirmación que realice de aquí en adelante hasta que lo elimine del índice. Pero, el file no está en el tree de trabajo en absoluto, por lo que no puede ver que está pasando por commits. Si ejecuta git add file mientras está en este estado, Git copy la inexistencia del file de tree de trabajo en el índice eliminando la input de índice.

(Mercurial tiene un estado similar, ya que hay una estructura de datos interna oculta llamada manifiesto que juega parte del mismo rol que el índice de Git. Si el file falta en el tree de trabajo, pero está en el manifiesto, Mercurial llama al file que falta Mercurial intenta tratar el tree de trabajo como lo que pasa en el siguiente compromiso , por lo que se podría pensar que si el file simplemente se ha desvanecido así, también debería desaparecer del siguiente compromiso. Según la documentation, Mercurial se comportó de esta manera originalmente, pero se descubrió que era propenso a errores).

Herramientas de bajo nivel para hurgar

  • Usa git ls-tree -r HEAD para ver todo el tree de la confirmación actual (si solo hay un tree, no necesitas -r ).
  • Use git ls-files --stage para ver todo el tree del índice actual: el índice es como un tree plano, donde si tiene un subdirectory (subtree) llamado dir con los files d1 y d2 , obtendrá inputs de índice llamadas dir/d1 y dir/d2 (frente a la confirmación, donde el tree superior tendrá un subtree llamado dir y el subtree tendrá dos blobs llamados d1 y d2 ).
  • Use las herramientas comunes del sistema operativo ( ls por ejemplo) para ver su tree de trabajo. Su tree de trabajo en sí tiene muy poca importancia para el git commit , que simplemente convierte el índice existente, lo que está dentro, en uno o más objects de tree para almacenar en un nuevo compromiso. (Todos estos cambios ejecutan git commit con arguments de ruta de file o -a o similar. Aquí Git puede agregar files al índice, o incluso cambiar a un índice alternativo temporal que usa solo hasta que se complete la confirmación. Esto depende de si, cuando se proporcionan routes adicionales, use --include or --only .)

Una arruga más

Debido a que Git tiene, y expone, el índice, puede y lo hace exponer una característica más, de dos maneras diferentes. El índice tiene dos bits de indicador por input llamados assume-unchanged y skip-worktree . Para ver estos bits de git ls-files con git ls-files , debe agregar el argumento --debug , pero lo que hacen se puede describir de manera relativamente simple, un poco demasiado simple, resulta que:

  • Si las banderas de asumir-sin cambios u omitir-trabajo se establecen en una input de índice, Git simplemente debe "cerrar los ojos" a lo que está en el tree de trabajo al hacer operaciones como el git status .

  • Esto puede acelerar mucho a Git, pero tiene ciertos efectos secundarios. Los efectos secundarios pueden ser para lo que estamos usando los bits.

Cuando ejecutas el git status , Git ejecuta dos git diff s. Uno compara HEAD vs index, y el segundo compara index vs work-tree. Es la primera diferencia que determina la primera columna de un git status --short salida git status --short , y la segunda diferencia que determina la segunda columna.

Los bits asumidos, no cambiados y skip-worktree le dicen a Git que no se moleste en comparar el file durante la segunda diferencia. 1 Tenga en count que para que estos bits se establezcan, el índice debe tener una input para el file, es decir, el file debe rastrearse para omitirse así. Probablemente podamos suponer que la input del índice coincide con la input HEAD (si no lo hace, lo hará después de la próxima confirmación), por lo que el efecto de estos bits de marca es que nunca vemos el file como modificado, y git add generalmente se salta sobre el file también: no copy la versión del tree de trabajo de nuevo en el índice.

Nuestra suposition, que la input del índice coincide con el compromiso, nos lleva por mal path en algunos casos de esquina, y es la razón por la que hay dos bits. Para más información sobre esto, vea Git – Diferencia entre 'asumir-sin cambios' y 'skip-worktree'


1 La primera diferencia es muy rápida debido a los files de formularios especiales (blobs) cuando se almacenan en una confirmación o en el índice. Específicamente, Git puede decir si los contenidos de cualquier file coinciden con el contenido de cualquier otro file simplemente comparando sus identificadores de hash. Si las ID de hash coinciden, los files son los mismos; si no, los files son diferentes. Git no busca una diferencia completa en este punto, sino simplemente un diff de estilo de --name-status : "¿son los files iguales o no?"

La segunda diferencia es mucho más lenta porque Git debe, en el peor de los casos, abrir y leer todo el contenido de cada file. Incluso el solo hecho de preguntarle al sistema de files sobre el file (llamando a la llamada al sistema lstat ) es mucho más lento que el truco interno de ID de comparar-hash de Git.