Mantener una copy de actualización automática de un repository de Git desnudo

Aquí esta lo que hice

cd /git git init --bare repo 

Lo que quiero hacer

Deseo que un clon no visible del repository que se actualiza automáticamente esté disponible en una location diferente, por ejemplo /srv/web/ . Lo que quiero decir es que cada vez que alguien hace un git push los contenidos en /srv/web/ debe actualizarse automáticamente. Del mismo modo, si el repository de git se revierte, los files en /srv/web también deberían volver a eso.

Lo que quiero decir es que cada vez que alguien hace un git push [al repository desnudo en /git/repo ,] los contenidos en [non-bare] /srv/web/ deberían actualizarse automáticamente. Del mismo modo, si el repository de git se revierte, los files en /srv/web también deberían volver a eso.

Tienes, en esencia, dos opciones:

  • Make /git/repo activamente update /srv/web . Esta ruta /git/repo -> /srv/web es una "actualización push" (no es lo mismo que git push , pero podría serlo): tiene el repository "masterización" actualizado el "esclavo" cada vez que hay una actualización disponible en el lado principal.

  • Make /srv/web activamente actualiza desde /git/repo . Esta ruta /git/repo <- /srv/web es una "actualización de extracción" (no es lo mismo que git pull , a less que lo implemente de esa manera, pero también podría serlo): tiene la actualización del repository esclavo desde la maestría en intervalos regulares.

Su segundo requisito ("si el repository git se revierte") es bastante misterioso. Un repository desnudo, por definición, no tiene tree de trabajo; entonces nadie puede hacer ningún trabajo en eso. Solo se puede actualizar al incorporar nuevos commits desde algún otro repository de Git. Si alguien quiere hacer un git revert , lo hace en otro repository, y luego lo hace. De modo que todas las actualizaciones del repository simple deben realizarse a través de git push y no debería necesitar este segundo requisito.

Por lo tanto, simplemente ignoraré por completo el segundo requisito.

Cuándo y por qué usar uno u otro

No hay una razón particularmente fuerte para favorecer ninguno de los enfoques, pero tenga en count que cada uno tiene un defecto diferente.

  • Si usa actualizaciones push y el receptor está inactivo, la actualización nunca ocurre. El maestro intenta pero no actualiza el esclavo. Cuando el esclavo vuelve a upload, el maestro simplemente se sienta hasta que hay una nueva actualización.

    (Si todo está en un único server, este problema desaparece, y este método se convierte en el claro ganador).

  • Si utiliza actualizaciones de extracción, hay un desfase temporal: sin importar cuán largo sea el intervalo de extracción, el esclavo puede permanecer desactualizado. Además, si el maestro se cae justo antes de una actualización, el esclavo puede permanecer desactualizado incluso más time que eso.

Making /srv/web activamente actualiza desde /git/repo (actualización de estilo de extracción)

Esto es conceptualmente más simple. Usted tiene su encuesta de /srv/web su /git/repo para cualquier actualización interesante. La frecuencia / intervalo de sondeo determina cuánto tarda la actualización en pasar del punto A al punto B. Para hacer esto más rápido, puede sondear con poca frecuencia, pero también puede tener un mecanismo desencadenante desde el que invoque, por ejemplo, un post posterior a la recepción script: "Acabo de recibir una actualización importante, por favor, consulta ahora". En otras palabras, usa un híbrido de tirar y empujar.

Por ejemplo, puedes simplemente ejecutar git pull desde una input de crontab (aunque recomiendo no usar git pull ever, incluso aquí: descomponerlo en git fetch seguido de otro command de Git).

Making /git/repo activamente update /srv/web (push style update)

[Editar: Me interrumpieron mientras escribía la respuesta original, y mezclé los ganchos de actualización y actualización posterior; esto ahora está arreglado.]

Esto es relativamente sencillo, usando un gancho post-recepción o post-actualización. También hay un gancho de update pero ese es el lugar equivocado para hacer esto. La diferencia entre todos es que creo que se ilustra mejor con un ejemplo: lo que sucede en /git/repo si, como alguien con acceso directo, lo hago desde mi propio clon de Git:

 git push origin 1234567:refs/heads/zorg 8888888:refs/tags/lucky 

Aquí, le digo a mi Git que se ponga en contacto con su server Git (mi origin = su /git/repo ) y entregue mi compromiso 1234567 a su Git. My Git lo hace, junto con cualquier otro object requerido para que 1234567 útil. También le estoy diciendo a mi Git que entregue commit-or-tag 8888888 a su Git, por lo que mi Git lo hace, junto con cualquier otro object requerido para que 8888888 útil.

Una vez que tu Git tiene todos esos objects, mi Git pregunta a tu Git:

  1. Configure su twig zorg ( zorg refs/heads/zorg ) en 1234567 .
  2. Configure su label con lucky ( refs/tags/lucky ) en 8888888 .

En este punto, su Git invocará su gancho de pre-receive , si tiene uno. Ofrece los identificadores hash antiguos y nuevos para refs/heads/zorg y refs/heads/zorg refs/tags/lucky en la input estándar. El trabajo de su gancho previo a la recepción es examinarlos y decidir si o no: "permitir que todas estas actualizaciones continúen con el próximo paso" o "prohibir que ocurra alguna de estas actualizaciones".

Luego, su Git invocará su gancho de update dos veces (nuevamente, si tiene uno). Uno de ellos dirá "hey, alguien está pidiendo cambiar refs/heads/zorg , aquí están los viejos y nuevos valores hash, ¿deberíamos dejarlo?" El otro dirá "hey, alguien está pidiendo cambiar refs/tags/lucky , aquí están los viejos y nuevos valores de hash, ¿deberíamos dejarlo?" El trabajo de su hook es examinar esta actualización y decidir sí o no: permitir la actualización o rechazarla. Si permite uno y rechaza el otro, se produce una actualización y la otra falla.

Finalmente, después de que todas las actualizaciones hayan sido aceptadas o rechazadas, para las actualizaciones que realmente ocurrieron, su Git invocará sus ganchos post-receive y post-update (si los hay). Su Git entrega a su gancho post-recepción, en input estándar, una línea por cada actualización que se produjo, en la misma forma que se utilizó en el gancho pre-receive . Su gancho posterior a la recepción puede hacer lo que quiera con estas líneas de input, pero ya es demasiado tarde para evitar que las actualizaciones sucedan: ya terminaron. Su sucursal de zorg ahora apunta a cometer 1234567 y su label de la lucky apunta ahora a cometer 8888888 , suponiendo que sus ganchos de pre-recepción y actualización no los rechazaron. Su Git entrega a su gancho post-actualización, como arguments, un argumento para cada reference actualizada: refs/heads/zorg y refs/heads/zorg refs/tags/lucky .

Ahora puede realizar cualquier acción que desee.

La acción obvia a tomar, en el gancho post-recepción o posterior a la actualización, es activar /srv/web para recoger las nuevas confirmaciones en cualquier (s) twig (s) que desee que actualice. (El enganche de actualización no es adecuado ya que, en el momento del enganche, el cambio real aún no se ha producido, por lo que si su /srv/web es muy rápido, es posible que aún no pueda get los nuevos objects de /git/repo : todavía pueden estar en el process de ser cementadas en su lugar.)

La implementación real podría ser tan simple como: "Ditch $GIT_DIR variable de entorno, cd en el repository esclavo y ejecutar git pull ". La razón para desarmar a GIT_DIR es que cualquier gancho de Git se ejecuta con este set de variables, y contiene una ruta relativa al repository de Git, que interfiere con el uso de otros repositorys. Como antes, recomiendo evitar el git pull completo.

Además, tenga en count que la identificación de usuario (es decir, los privilegios) del usuario que ejecuta el script post-receive depende en primer lugar del método de authentication utilizado para hacer el git push . Esto afecta todos los methods de deployment, incluso si el script posterior a la recepción simplemente envía un post (por ejemplo, un package en un puerto de socket) a algún process independiente que haga la actualización del lado del esclavo, ya que los privilegios disponibles para enviar un post pueden depender de identidad de usuario.

Nota final: ¿realmente necesitas un repository de Git en el área de deployment?

Si su server es un server web típico, no necesita un repository Git. Simplemente puede actualizar el equivalente de un tree de trabajo. Si su server web está en un sistema diferente, usar un repository Git puede ser la forma más simple o más conveniente de lograr esto, pero si todo está en una máquina, simplemente puede ejecutar git --work-tree=/path/to/work-tree checkout ... del repository simple.

(Tenga en count que lo que se verifica y cómo ocurre la actualización depende de qué hay en el índice y de HEAD en el repository real y de cómo se compara el índice con el tree de trabajo suministrado. Los arguments adicionales para el git checkout pueden cambiar qué twig es para ser desprotegido, lo que actualizará HEAD correspondientemente).

Usar git no es perfecto para el escenario que imaginas por un par de razones.

Primero estás invirtiendo completamente el uso normal de git. Un repository git es en realidad una image lógica de su proyecto. Puede haber twigs en el proyecto, por lo que esta image lógica es mucho más compleja que la versión más reciente . Debe get la twig real que desea para una copy de trabajo y trabajar en ella. Esto es lo que hacen los repositorys no simples. Son un repository y una copy de trabajo . No es el uso previsto de git para enviar la última versión a una copy de trabajo.

En segundo lugar, hay dificultades técnicas para empujar a un repository no simple. Como comportamiento pnetworkingeterminado, git negaría empujar a un repository no desnudo. Sin embargo, hay forms de configurar su no medio para eso. Pero esa configuration solo es factible si nunca modificará su copy de trabajo no desnuda. Si comienzas a modificar la copy de trabajo en un espacio no desnudo, definitivamente comenzarás a tener problemas.

Tercero, si está dispuesto a publicar su copy de trabajo en la web, tenga en count que también se servirá el directory .git . Esto podría causar vulnerabilidades. Si haces esto, al less recomiendo que sirvas una subcarpeta de tu proyecto si es posible. De esta manera .git queda fuera.

Sin embargo, te recomendaré otro método para hacer todo esto. En lugar de inicializar un directory en el tree web como un repository, puede copyr automáticamente toda la copy de trabajo (sin repository – carpeta .git) al directory deseado. Ya que solo está interesado en servir los files que serían un método más adecuado.

En su repository /git/repo , hay una carpeta llamada hooks . Crear file /git/repo/hooks/post-receive en este directory con el contenido

 #!/bin/bash rm -rf /srv/web/* git archive master | tar -x -C /srv/web 

También necesita dar permiso de ejecución a este file.

 chmod +x /git/repo/hooks/post-receive 

Luego, después de cada pulsación a este repository simple, HEAD del branch master se copyrá en el directory de su elección sin ningún tipo de información del repository.

Actualización: creo que la solución inicial en la respuesta no era válida. Así que lo eliminé, la solución alternativa aún está bien.

Actualización 2: Como @torek notó que esta solución causa una pequeña window de contenido no válido en el directory web. Como indicó que servirá el contenido web en la networking local, supongo que eso no es un problema. Además, esto es básicamente una especie de escenario de implementación de un hombre pobre y no debe utilizarse ningún deployment de producción. Sin embargo, esto se puede mejorar con un directory temporal provisional.

Reemplace el gancho post-recepción con la secuencia de commands siguiente. Este script networkinguce el time que su directory /srv/web permanece vacío. Como rm -rf y mv son bastante rápidos (si su directory temporal está en la misma unidad de disco) y dado que el tamaño del repository no afecta a ambos commands, la window de contenido no válido será más pequeña.

 #!/bin/bash STAGING=`mktemp -d` git archive master | tar -x -C $STAGING rm -rf /srv/web mv $STAGING /srv/web 

O puede usar un intercambio en lugar de eliminar la carpeta primero como @torek sugirió.

 #!/bin/bash STAGING=`mktemp -d` SWAP=`mktemp -d` git archive master | tar -x -C $STAGING mv /srv/web $SWAP mv $STAGING /srv/web rm -rf $SWAP 

Sin embargo, tenga en count que está eliminando o intercambiando /srv/web y perderá la propiedad, el permiso o la información ACL de la carpeta si sigue este método.

Alternativamente, puede usar rsync que aún copyrá los files, pero dado que funcionará selectivamente, no se eliminará todo el contenido en ningún momento. También rsync se puede ajustar para preservar la propiedad, los permissions, etc.

 #!/bin/bash STAGING=`mktemp -d` git archive master | tar -x -C $STAGING rsync -a --delete --remove-source-files $STAGING /srv/web