¿Cuál es la mejor forma de escribir un gancho de actualización de git que rechace confirmaciones de submodule no válidas?

Estoy intentando escribir un gancho de update para git que rebota si un submodule se está actualizando a una ID de confirmación que no existe en el repository de subida del submodule. Para decirlo de otra manera, quiero obligar a los usuarios a realizar cambios en los repositorys de submodules antes de insert cambios en los pointers de los submodules.

Una advertencia:

  • Solo quiero probar los submodules cuyos repositorys desnudos, ascendentes, existen en el mismo server que el repository principal. De lo contrario, comenzamos a tener que hacer cosas locas como llamar a 'git clone' o 'git fetch' desde dentro de un git hook, lo que no sería divertido.

He estado jugando con una idea, pero parece que debe haber una mejor manera de hacerlo. Esto es lo que estaba planeando hacer en el gancho de actualización:

  1. Compruebe el refname pasado al gancho para ver si estamos actualizando algo en refs/heads/ . Si no, salga temprano.
  2. Use git rev-list para get una list de revisiones que se están presionando.
  3. Para cada revisión:
    1. Llame a git show <revision_id> y use una expresión regular que busque si se actualizó un submodule (buscando `+ Subproject commit [0-9a-f] +).
    2. Si esta confirmación cambió un submodule, obtenga el contenido de los files .gitmodules como se ve en esa confirmación particular ( git show <revision_id>:.gitmodules ).
    3. Use los resultados de 3.1 y 3.2 para get una list de URL de submodule y sus ID de confirmación actualizados.
    4. Compruebe esta list creada en 3.3 en un file externo que correlaciona las URL de los submodules con los repositorys git locales en el sistema de files.
    5. cd a las routes que se encuentran en 3.4 y ejecuta git rev-parse --quiet --verify <updated_submodule_commit_id> para ver si ese commit existe en ese repository. Si no lo hace, salga con un estado distinto de cero.

(Nota: creo que los resultados de 3.2 pueden almacenarse en caching en todas las revisiones, siempre que la salida de git rev-parse --quiet --verify <revision_id>:.gitmodules no cambie de una revisión a la siguiente. esta parte para simplificar la solución.)

Así que sí, esto parece bastante complejo, y no puedo evitar preguntarme si hay algunos commands internos de GIT que pueden hacer mi vida mucho más fácil. ¿O tal vez hay una forma diferente de pensar sobre el problema?

Editar, mucho más tarde: a partir de Git 1.7.7, git-push ahora tiene una --recurse-submodules=check , que se niega a enviar el proyecto principal si no se ha --recurse-submodules=check ningún commit de submodule a sus controles remotos. No parece que se haya agregado aún un parámetro de configuration push.recurseSubmodules correspondiente. Esto, por supuesto, no resuelve el problema por completo: un usuario despistado podría seguir presionando sin el cheque, ¡pero es bastante relevante!

Creo que el mejor enfoque, en lugar de examinar cada compromiso individual, es mirar el diff en todos los commits: git diff <old> <new> . Sin embargo, no quieres ver todo el problema; podría ser enorme. Desafortunadamente, el command git-submodule de porcelana no funciona en repos sin formatting, pero aún así debería poder examinar .gitmodules rápidamente para get una list de routes (y tal vez direcciones URL). Para cada uno, puede git diff <old> <new> -- path , y si hay un diff, tome el nuevo commit del submodule. (Y si estás preocupado por una posibilidad de compromiso antiguo de 000000, puedes usar git show en el nuevo, creo).

Una vez que hayas solucionado todo eso, has networkingucido el problema para comprobar si existen confirmaciones dadas en repositorys remotos determinados. Desafortunadamente, como parece que te has dado count, no es sencillo, al less hasta donde yo sé . Mantener clones locales y actualizados es probablemente la mejor opción, y parece que eres bueno allí.

Por cierto, no creo que el almacenamiento en caching sea relevante aquí, ya que el gancho de actualización es una vez por ref. Sí, puedes hacer esto en un gancho de pre-recepción, que obtiene todos los refs en stdin, pero no veo por qué deberías molestarte en hacer más trabajo. No va a ser una operación costosa, y con un gancho de actualización, puede aceptar o rechazar individualmente las diversas twigs que se envían, en lugar de evitar que se actualicen todas ellas porque solo una era incorrecta.

Si quieres ahorrar algo de problemas, probablemente solo evitaría analizar el file gitmodules y codificar una list en el gancho. Dudo que su list de submodules cambie muy a menudo, por lo que probablemente sea más económico mantener eso que escribir algo automatizado.

Aquí está mi pequeño bash de un gancho de actualización de git. Documentarlo aquí para que pueda ser útil para otros. La advertencia conocida es que el caso especial '0000 …' no se maneja.

 #!/bin/bash REF=$1 OLD=$2 NEW=$3 # This update hook is based on the following information: # http://stackoverflow.com/questions/3418674/bash-shell-script-function-to-verify-git-tag-or-commit-exists-and-has-been-pushe # Get a list of submodules git config --file <(git show $NEW:.gitmodules) --get-regexp 'submodule..*.path' | while read key path do url=$(git config --file <(git show $NEW:.gitmodules) --get "${key/.path/.url}") git diff "$OLD..$NEW" -- "$path" | grep -e '^+Subproject commit ' | cut -f3 -d ' ' | while read new_rev do LINES=$(GIT_DIR="$url" git branch --quiet --contains "$new_rev" 2>/dev/null | wc -l) if [ $LINES == 0 ] then echo "Commit $new_rev not found in submodule $path ($url)" >&2 echo "Please push that submodule first" >&2 exit 1 fi done || exit 1 done || exit 1 exit 0