¿Se pueden volver a crear los files en un gancho git precompuesto?

He hecho un script de PowerShell que agrega un encabezado a ciertos files dentro de mi proyecto. Quiero ejecutarlo siempre que el código esté en process de ser enviado a GitHub para que todos los files en el repository de GitHub tengan el encabezado adjunto, eliminando la necesidad de ejecutar el script manualmente.

El problema es que cuando modifico los files en mi gancho de git precompuesto, esos cambios no son reestablecidos, así que esencialmente tengo que escenificar, comprometer y luego volver a escenar para que los encabezados se comprometan.

¿Hay una buena manera de lidiar con esto, o es mejor separar la ejecución de ese script de powershell del flujo de git?

TL; DR

La respuesta a la pregunta en el asunto es "sí, pero". 🙂 Lo voy a reformular un poco para que podamos hablar sobre todos los detalles técnicos. El "pero" es que hay una gran sorpresa si usas git commit --only . Me parece recordar que hubo un momento en que afectó a git commit -a mal también, pero ahora es mejor sobre eso. (No estoy seguro de qué versiones de Git se comportan mal con -a .)

Largo

Git realiza cada nueva confirmación a partir de un índice, o en la mayoría de los casos. El índice, o cualquier índice, es esencialmente una confirmación propuesta: consiste en una especie de tree aplanado, con numbers de hash blob y nombres de file. Está en una forma que lo hace especialmente conveniente para git mktree , que convierte el índice en una serie de objects de tree y devuelve la identificación de hash del object de tree de nivel superior, que luego entra en el object de confirmación.

La pregunta, entonces, toca estas tres partes:

  1. ¿Exactamente qué índice se está utilizando para build el nuevo compromiso?
  2. ¿Se puede modificar ese índice? Si es así, ¿qué ocurre con la nueva confirmación?
  3. ¿Cómo afecta esto el índice?

Aquí, el índice es el índice especial y distinguido que va con el tree de trabajo. Hay un índice de este tipo por tree de trabajo: si usa git worktree add obtendrá más treees de trabajo, y cada uno tendrá su propio índice privado, pero solo estará en un tree de trabajo a la vez, por lo que "esto índice del tree de trabajo "es el índice.

Sin embargo, cuando ejecuta git commit , puede instruirlo (usando --only y / u otros arguments de línea de command) para build su propio índice privado , separado del índice. Si lo ha hecho, ejecutará cada uno de los diversos enganches con una variable de entorno, GIT_INDEX_FILE , configurada con el nombre de ruta del índice temporal. (De lo contrario, GIT_INDEX_FILE contendrá .git/index , que es la ruta al file de índice).

Entonces la respuesta a Q1 es: $GIT_INDEX_FILE .

Ahora, de hecho, puede modificar cualquier índice que Git esté usando para comstackr la confirmación, porque Git volverá a leer este índice después de ejecutar sus enganches. Entonces, la respuesta a Q2 es: Sí, y esto hace que el siguiente compromiso utilice lo que esté en el índice que esté en uso.

Q3 es el más difícil. Si usa git commit --only <paths> , Git debe crear un índice temporal que:

  • es una copy de HEAD
  • a exception de los <paths> especificados

sin alterar el índice en absoluto. Si la confirmación continúa y tiene éxito, sin embargo, Git ahora debe modificar el índice para tener en count el hecho de que estas <paths> tienen nuevas manchas en ellas en el nuevo compromiso HEAD .

De hecho, Git necesita hacer dos índices temporales (¿índices?), Uno para la confirmación propuesta con sus --only files, y uno para el resultado del compromiso propuesto. El resultado de confirmación propuesto se convierte en index.lock , que actúa como el nuevo índice.

Si git add files mientras se ejecuta, entrarán en el índice temporal. Pero, ¿y el índice? Vamos a averiguar:

 $ cat .git/hooks/pre-commit #! /bin/sh echo pre-commit hook, GIT_INDEX_FILE = $GIT_INDEX_FILE git add sneaky $ echo this is the base version of sneaky > sneaky $ echo this is the base version of other > other $ git add other sneaky $ git commit -m 'create two files' pre-commit hook, GIT_INDEX_FILE = .git/index [master 5131b63] create two files 2 files changed, 2 insertions(+) create mode 100644 other create mode 100644 sneaky 

Como puede ver, el gancho de precompromiso se disparó aquí, y agregó sneaky . Esto no es gran cosa, ya que lo que se copió en el índice para este compromiso, que es el índice, ya era la misma versión base.

Ahora, sin embargo, modifiquemos los contenidos actuales de sneaky , git add , y también modifiquemos los contenidos actuales de other , y lo git add , para que tengamos nuevos contenidos en el índice para ambos files …

 $ echo version 2 of other > other $ echo version 2 of sneaky > sneaky $ git add other sneaky 

En este punto, tanto el índice como el tree de trabajo tienen la "versión 2". Ahora actualicemos ambos files del tree de trabajo a la "versión 3" sin git add :

 $ echo version 3 of other > other $ echo version 3 of sneaky > sneaky $ git status --short MM other MM sneaky 

Esto nos dice que las versiones HEAD , index y work-tree son diferentes: la versión HEAD de cada una es la base, la versión de índice es la versión 2 y la versión de tree de trabajo es la versión 3.

Ahora ejecutamos git commit --only other :

 $ git commit --only other -m 'jump other straight to v3' pre-commit hook, GIT_INDEX_FILE = [path]/.git/next-index-72393.lock [master 91ec03b] jump other straight to v3 2 files changed, 2 insertions(+), 2 deletions(-) 

Veamos ahora qué tenemos en la confirmación, el índice y el tree de trabajo:

 $ git status --short MM sneaky 

OK, la versión de tree de trabajo de other coincide con la versión de índice de other que coincide con la versión HEAD de other , entonces:

 $ cat other version 3 of other 

todos ellos son versión 3. ¿Y si sneaky ? HEAD e index difieren, y el índice y el tree de trabajo difieren. Veamos qué hay en HEAD primero:

 $ git show HEAD:sneaky version 3 of sneaky 

Ahora veamos qué hay en el tree de trabajo:

 $ cat sneaky version 3 of sneaky 

Ajá, estos partidos! La parte difícil es ver la versión de índice, pero también podemos hacerlo con git show :

 $ git show :0:sneaky version 2 of sneaky 

Whoa, mira eso! ¡La versión de índice es la anterior!

Esta es la respuesta a Q3: el git add in the pre-commit hook actualiza cualquier índice que se use para build el siguiente commit, pero si se trata de un índice temporal , no actualiza lo que se convertirá en el índice real. Esto podría decirse que es un error: git add en el git add precompromiso también debe agregar al índice que se convertirá en el índice. Hacer que eso suceda es un poco complicado ( git commit podría leer el índice temporal antes y después del enganche y copyr cualquier actualización al file index.lock, quizás).

Tenga en count que salté los detalles de lo que sucede si confirma sin – --only . Afortunadamente, en este caso, el índice que agrega es el índice, por lo que todo funciona como era de esperar:

 $ git reset --hard 5131b63 HEAD is now at 5131b63 create two files $ echo v2 > other && echo v2 > sneaky && git add other $ git commit -m 'regular commit' pre-commit hook, GIT_INDEX_FILE = .git/index [master 10a8b20] regular commit 2 files changed, 2 insertions(+), 2 deletions(-) $ git status --short $ 

El git add reemplazó la versión de índice de sneaky , y se mantuvo reemplazado … lo cual ocurrirá incluso si falla la confirmación.

Tenga en count también que al usar git commit -a , obtenemos un índice temporal diferente:

 $ git commit -a -m test pre-commit hook, GIT_INDEX_FILE = [path]/.git/index.lock 

Aquí, lo que Git hace para git commit -a es crear el nuevo índice propuesto como el file index.lock . El git add procede de manera normal, agregando el file de la manera habitual, y luego, cuando la confirmación tiene éxito, Git renombra index.lock al index . Esto desbloquea el file de índice y, al mismo time, coloca el índice modificado en su lugar, de modo que la respuesta para git commit -a a Q3 es que el índice temporal se convierte en el índice. Esto es diferente del de git commit --only .

Esto es muy similar a esta publicación .

Desea utilizar .gitattributes y especificar scripts que se ejecutan en agregar time (es decir, git add ). De esta forma, cada vez que se agrega su file, la secuencia de commands se ejecuta y realiza los cambios, y luego lo confirma.

¡Espero que ayude!