Cambiar el control remoto a una twig específica

Entonces entiendo el valor de la ramificación, pero una cosa me confunde. Tengo mi repository local. Presiono los cambios en un control remoto, que tiene un gancho de recepción de posts configurado para escribir files en un website. Entonces creo una twig (new-branch) para probar algo. Edito files, confirmo y presiono al control remoto. Estupendo. El problema es que, mientras el repository en mi control remoto está actualizado, creo que todavía está configurado como maestro, ya que los cambios en la nueva twig no se reflejan en el sitio. ¿Cómo configuro el control remoto para una twig en particular, de modo que esa bifurcación conduzca el gancho de recepción de posts, y no solo el default a la twig principal? Me gustaría fusionarme en el maestro pero no estoy listo ya que aún estoy resolviendo los problemas en la twig.

De acuerdo, según los comentarios, parece que también controlas el server, que es necesario aquí. (Es decir, donde escribo "ellos" a continuación, es usted mismo, pero llevaba un sombrero diferente, por así decirlo).

Comencemos con algunos antecedentes: git push y "bare"

Desde el punto de vista de git, trabajas en tu repository, y ellos (sean quienes sean) trabajan en su repository. Entonces, cuando haces un git push , que entrega confirmaciones que has hecho directamente a su repository, probablemente estén trabajando en su repository. Es probable que tengan alguna twig revisada y estén en el medio de editar su copy de xyz.html o lo que sea.

Si su impulso xyz.html su xyz.html , ¿no les molestaría? ¡Están en medio de la edición! Entonces, para un repository convencional, git rechazará un bash de enviarlo a cualquier twig que esté actualmente desprotegida (el git status un git status imprime como "# en la twig …", o mostrado por git symbolic-ref --short HEAD ) . 1 Solo puede presionar a otras twigs, que por definición, no están trabajando, para que no se molesten.

Ahora, de hecho, muchos de estos repositorys centralizados de "push here" no tienen a nadie trabajando en ellos. A menudo se configuran como repositorys "simples", lo que significa que no tienen tree de trabajo en absoluto. Esto a su vez significa que "ellos" -quienquiera que "ellos" puedan ser-no pueden estar trabajando en su copy de xyz.html , ya que no tienen una copy de trabajo de xyz.html en primer lugar. La clonación con --bare , o la conversión de un repository regular a uno --bare , desactiva la verificación de "no se puede enviar a la sucursal actual", que es lo que quieren aquí.


1 En particular, aparece el post "rechazo a actualizar la ramificación comprobada". Esto es realmente configurable, a través de receive.denyCurrentBranch , en el repository (no desnudo) en el server.


Despliegue automático

Hasta aquí todo bien; pero ahora pueden querer que su repository central de push-server también se deployment automáticamente en un website (o, para repositorys de código, en un sistema de testing como Jenkins, o lo que sea) -el punto key es que "ellos", sean quienes sean nuevamente, tiene un gancho post-recepción que implementa una twig en particular. Pero simplemente describimos el repository desnudo como que no tiene tree de trabajo. ¿Cómo funciona esta implementación automática? Vamos a ponernos nuestro otro sombrero y convertirnos en "ellos".

Debido a que esto es git, en realidad hay muchas maneras, pero el método usual (y tal vez el mejor) es con un script posterior a la recepción que verifica qué twigs se están actualizando y si el (los) interesado (s) es / se llevan a cabo, ¿la implementación?

Aquí hay un ejemplo de una secuencia de commands de shell de anzuelo post-receive ridículamente simple que tiene una function de deploy shell no deploy para desplegar los empujes al master :

 #! /bin/sh deploy() { local newref=$1 branch=$2 echo deploy invoked: $newref $branch } while read oldsha newsha refname; do case "$refname" in refs/heads/master) deploy $newsha ${refname#refs/heads/};; esac done 

Git ejecuta el enganche post-recepción con su stdin alimentado con una serie de líneas de input. Cada línea tiene tres elementos. El último es el nombre completo de la reference, por lo que el maestro de la twig es refs/heads/master . Los primeros dos son el "antiguo" SHA-1 y el "nuevo" SHA-1, respectivamente, para esa reference.

Como máximo, uno de los valores antiguos y nuevos de SHA-1 puede tener 40 0 caracteres: esto significa que la reference se está creando (antiguo-SHA-1 es todo ceros) o eliminado (nuevo es todo ceros). De lo contrario, la reference existe actualmente y simplemente se está actualizando (apuntando a una nueva confirmación, normalmente): solía apuntar a la ID anterior y ahora apunta a la nueva.

(En un enlace previo a la recepción, que obtiene exactamente lo mismo, puede rechazar el bash de actualizar la reference. En un enlace posterior a la recepción la actualización ya ha sucedido y lo único que puede hacer es informar sobre ella o utilizarla de alguna manera.)

En nuestro caso, realmente no nos importa el valor anterior. No importa si el master sucursal nunca existió antes, o qué se implementó entonces. (Bueno, podríamos quererlo, en cuyo caso podríamos agregarlo: después de todo, se trata de un guión de shell). Ni siquiera necesitamos el nuevo valor, porque podemos leerlo directamente del repository con un git command, pero es bueno tenerlo, especialmente si queremos verificar y quejarnos si la twig ha sido eliminada. (La twig probablemente se elimine si falla el bash de leer la reference de la twig master , pero eso podría suceder si el server se incendió y el repository está medio destruido también. Aunque en este caso puede que ya no nos importe): – )

Principalmente, sin embargo, lo que tenemos que hacer es verificar todos los files de Branch master , pegándolos en un área de deployment. Como resultado, podemos hacer esto con un simple process de git checkout , incluso en un repository simple, especificando un tree de trabajo alternativo:

 NULL_SHA1=0000000000000000000000000000000000000000 # 40 0s deploy() { local newref=$1 branch=$2 if [ $newref = $NULL_SHA1 ]; then echo "deploy: branch $branch is deleted!" return 1 fi # next bit is stupid, hardcodes "master" even though # we have "$branch", but read on... git --work-tree=/deploy/master checkout master } 

(Nota al GIT_WORK_TREE=/deploy/master git checkout master : a veces verá esto como GIT_WORK_TREE=/deploy/master git checkout master o similar. Eso hace exactamente lo mismo. Si no especifica --work-tree git usa $GIT_WORK_TREE si está configurado).

Si queremos más de una sola twig ( master ) de deployment automático, podemos agregar más nombres de twig al set que invoque la deploy en nuestro script, y corregir el bit tonto: revise $branch a /deploy/$branch , por ejemplo .

Sin embargo, hay una serie de problemas técnicos potenciales:

  1. El directory de destino (aquí /deploy/master ) debe existir.
  2. Este tipo de git checkout de git checkout actualiza la noción de "twig actual" de git. Un repository desnudo todavía tiene tal cosa: todavía hay un file llamado HEAD que contiene el nombre de la twig actual. Si seleccionamos otra git checkout otherbranch cambiaremos; y esto afecta las operaciones de git clone que se clonan desde este repository simple.
  3. Git se asegurará de que no bloqueemos los files modificados. Dependiendo del directory de implementación y de lo que se haga allí, esto puede no ser un problema.
  4. A Git le gusta optimizar los pagos.

El ítem 1 es fácil de arreglar haciendo primero un mkdir -p .

El ítem 2 no es un problema si solo implementamos el master , o si no nos importan los nuevos clones que potencialmente pueden verificar la twig otherbranch automáticamente. Pero podemos solucionarlo usando una forma diferente de git checkout de git checkout . Podemos agregar -f también para arreglar el ítem 3.

 git --work-tree=/deploy/$branch checkout -f $branch -- . 

Todo esto introduce un nuevo problema, sin embargo, que va junto con el elemento 4.

El elemento 4 es el más complicado. Cuando haces un pago sin el -- . al final, para que cambie de twigs, git actualiza su file de índice (área de ensayo) para hacer un seguimiento de lo que ya está en el tree de trabajo. Luego, cuando haces un nuevo git checkout que reemplaza al anterior, puede decir qué files se pueden dejar solos, cuáles se deben eliminar si los hay, y cuáles se deben reescribir o agregar. Algunos scripts de implementación (que verifican una twig, como master ) dependen de este comportamiento.

Si cambiamos el código de deployment para usar el -- . form, git aún actualiza el índice, pero no eliminará los files por nosotros. Esto significa que tenemos que limpiar los files que se fueron. (También termina haciendo un índice muy desorderado, pero por lo general está bien: es un repository --bare que nadie trabaja de todos modos).

Limpiar solo los files correctos es complicado. Podemos usar el método $oldsha , comparando las confirmaciones antiguas y nuevas para determinar qué files eliminar. O simplemente podemos volar completamente el directory /deploy/$branch :

 rm -rf /deploy/$branch mkdir /deploy/$branch git --work-tree=/deploy/$branch checkout -f $branch -- . 

Esto es a menudo suficiente. Todavía tiene un error: hay un corto período cuando la versión implementada anterior se está eliminando, y la nueva versión implementada se está creando, cuando las cosas son un desastre en el directory de implementación. Pero a veces eso está bien.

En su mayoría, podemos arreglar el error cambiando el order: crear un nuevo directory vacío, rellenarlo, luego mv el nuevo directory en su lugar (eliminando el anterior) y solo luego rm -rf el (movido) fuera del path) viejo. Todavía hay una pequeña window durante la cual el directory de implementación no existe, pero es lo más pequeño posible. (Bueno, hay un potencial para cerrarlo completamente con enlaces simbólicos: ver comentarios a continuación).

Hay un último truco que podemos hacer, que en realidad nunca he probado: podemos hacer que git haga la mayor parte del trabajo, usando un file de índice diferente para cada directory de implementación. Git usa $GIT_INDEX_FILE , si está configurado, o $GIT_DIR/index si no es así. Entonces, si tuviéramos que establecer GIT_INDEX_FILE=$GIT_DIR/index.$branch , deberíamos get un file único para el nombre de la twig en particular. Luego, podemos volver al formulario "verificar una sucursal específica" de git checkout y dejar que elimine los files si es necesario.

Este último método abre la window de inconsistencia un poco más: si git tiene que eliminar una docena de files y actualizar o crear otros 100 files, lo que sea que use la versión desplegada tiene muchas más posibilidades de ver eliminaciones parciales y / o actualizaciones a medida que avanza la git checkout . Pero es mucho más simple y tiene less "movimiento perdido" en el tree de trabajo.

Una última reflexión irónica, por así decirlo

Tenga en count que nuestra function de deploy configura un tree de trabajo, o tal vez uno de varios, si tenemos múltiples twigs deploy , como un tree de trabajo literal de git, posiblemente incluso utilizando $GIT_WORK_TREE . Luego elimina lo que está en ese tree de trabajo, reemplazándolo con la última versión en la twig interesante. Esto es exactamente lo que dijimos que sería molesto, en la sección de "antecedentes". ¡No es nada molesto, es justo lo que queremos!

Especie de. Lo bueno de este tree de trabajo, en comparación con el repository desnudo, es que no se puede cd /deploy/master accidentalmente cd /deploy/master , ver un directory .git y comenzar a trabajar en él. No hay directory .git aquí. No obstante, la combinación de "directory de trabajo aquí" más "depósito vacío allí" realmente equivale a "repository regular, no desnudo", en casi todos los sentidos.