Gancho pre-push de Git, enumerando todos los commits sin empujar

Quiero ejecutar un trabajo previo al envío en todas las confirmaciones locales sin enviar.

git rev-list BRANCH --not --remotes=origin funciona muy bien para todos los casos, excepto cuando el control remoto al que está presionando está vacío. Cuando ese es el caso, ese command no devolverá nada.

¿Es seguro suponer que si el sha arg remoto es 00000 y git rev-list BRANCH --not --remotes=origin vuelve vacío, entonces todos los commits están en para ser enumerados git rev-list BRANCH ?

¿Hay alguna forma mejor de get la información que busco que funcione en todos los casos?

Para mí no es completamente claro lo que pretendes lograr, pero cada vez que ejecutas git push :

  • tu git llama a su git (en el control remoto) y descubre lo que tiene;
  • le dices a tu git-a menudo implícitamente-qué nombres de twig (y / u otras references) debería ver de tu lado, y qué nombres de twig debería tratar de insert en "su" lado, usando "refspecs" (pares de nombres con dos puntos entre ellos).

Es decir, puede ejecutar:

 git push origin mybranch:master 

o:

 git push origin branch1:branch1 branch2:branch2 branch3:newname 

o incluso:

 git push origin 'refs/heads/*:refs/heads/*' 

También puede ejecutar:

 git push origin refs/tags/v1.2:refs/tags/v1.2 

o (con --tags ) incluyen un par de refs/tags/* como la línea refs/heads/* .

En otras palabras, puede que no estés simplemente empujando una twig (puedes empujar varias), o que no estés empujando una twig, sino una label, o podrías estar empujando twigs y tags. (Para el caso, también hay "notas". Notas en vivo en refs/notes/ , que es un espacio de nombre algo nuevo que generalmente no se transfiere, pero tenga en count la palabra "generalmente").

En un gancho de pre-push, se supone que debe leer varias líneas de la input estándar. Habrá una línea para cada nombre de reference que proponga crear, eliminar o actualizar en el control remoto.

En cada línea, obtiene (como señala la documentation ) el nombre de reference local, 1 el SHA-1 local, el nombre de reference remoto y el SHA-1 remoto, todo en ese order. Puede ver si le ha pedido a su git que cree o elimine el nombre de reference remoto examinando los dos SHA-1. Como máximo uno de estos será 40 0 s. Para una actualización normal, ninguno será completamente cero.

Es posible que no haya nuevos commits, o incluso ningún object nuevo, 2 involucrados en la actualización del nombre de reference proporcionado. Por ejemplo, al crear una nueva label que apunta a una confirmación existente, no hay nada más que hacer: simplemente solicite al control remoto "crear esta nueva label, apuntando a la confirmación 1234567890123456789012345678901234567890 " o lo que sea. Sin embargo, si simplemente está eliminando algún historial de confirmación (con un empuje forzado), esto tampoco tiene compromisos nuevos: simplemente le está preguntando al control remoto "por favor cambie de branch para que apunte a este nuevo ID".

Para saber qué objects nuevos (si los hay) se enviarán, no debe mirar sus propios nombres , ya que pueden estar desactualizados. En su lugar, debe hacer lo mismo que hace git: concéntrese en los ID de SHA-1.

Sin embargo, hay un pequeño problema aquí. Digamos, por ejemplo, que le está pidiendo al control remoto que actualice ref-name refs/heads/branch de 1234567... a 9abcdef... , de modo que el SHA-1 remoto sea 1234567... y el SHA local- 1 es 9abcdef... Esto puede ser -de hecho, por lo general es- un movimiento "hacia adelante":

 ... <- 1234567... <- 5555555... <- 9abcdef... <-- refs/heads/branch 

(donde los numbers aquí son identidades SHA-1 de objects de compromiso reales, y usted simplemente le está pidiendo al control remoto que mueva su branch hacia adelante dos confirmaciones). Sin embargo, es posible que el control remoto ya tenga commits 5555555... y 9abcdef... , simplemente no en la branch :

 ... <- 1234567... <-- branch \ 5555555... <- 9abcdef... <-- develop 

En este caso, mientras actualiza su branch moviéndola hacia delante dos confirmaciones, esas son dos confirmaciones que ya estaban en algún lugar del repository (de hecho, en el develop sucursal).

Sin embargo, esos son dos commits que no estaban en branch antes, y lo serán después, si el push tiene éxito (su gancho pre-push puede detenerlo, pero también lo puede hacer el control remoto: puede ejecutar sus propios ganchos y decidir rechazar su push )

Para enumerar esos dos commits, simplemente use git rev-list con los valores raw SHA-1, como en este gancho de muestra que encontré en github.

Si está preguntando cómo puede evitar enumerar esas dos confirmaciones, la respuesta es que no hay un método 100% confiable. Puede acercarse bastante ejecutando git fetch 3 antes de ejecutar git push . Esto le permitirá encontrar todos los nombres de reference que el control remoto está dispuesto a exportar a usted, y cuáles son sus valores SHA-1. Cualquier object de commit que pueda encontrar por sus nombres de reference está necesariamente en el repository remoto.

Aquí, git rev-list ... --not --remotes=origin es, de hecho, la mayoría de las 4 cosas correctas: después de ejecutar git fetch para get su copy de sus references, puede usar el SHA-1 sin formatting para encontrar compromisos alcanzables, y también use todas esas copys para excluir commits accesibles desde cualquier twig remota. El error aquí no es solo el de la nota cuatro (tags), sino también el hecho de que no importa qué tan rápido sea su secuencia de fetch continuación, push , las references que copie pueden estar desactualizadas en el momento en push ejecuta. Puedes hacer que esta window sea muy pequeña, pero no (con solo git solo) eliminarla.


1 Aquí hay una advertencia, que también se menciona en la documentation : el SHA-1 local puede no tener un nombre. Este es obviamente el caso cuando le pide al control remoto que elimine una reference, ya que lo solicita con git push :ref-to-delete : no hay ningún nombre en el lado izquierdo del refspec. Sin embargo, también es cierto si presionas SHA-1 sin formatting o una reference relativa, como en gitrevisions . En general, este no es un gran problema ya que el nombre de reference local, si lo hay, no tiene ningún efecto en el control remoto: toda la acción se debe a los dos SHA-1 y al nombre de reference remoto.

2 Recuerde, git push empuja todos los objects necesarios, no solo confirma: un commit apunta a un tree, por lo que si hay un nuevo compromiso probablemente haya un nuevo tree; los treees apuntan a más treees y a manchas, por lo que puede haber treees y manchas adicionales; y una label anotada es su propio tipo de object. Todos estos pueden ser transferidos durante un empuje.

3 Puede usar git ls-remote para get las asignaciones actuales de nombres de reference, pero el problema aquí es que si su repository local carece de los objects correspondientes, no puede vincularlos con su propio historial de repositorys para encontrar con precisión qué objects tienes que no. La única manera de descubrir lo que tienen es usar git fetch para get no solo los objects a los que apuntan los refs, sino también los objects mismos, para build el gráfico de compromiso.

4 Esto, por supuesto, omite por completo las tags.

Los compromisos en el control remoto pueden ser alcanzados a través de las tags. Sin embargo, si traes el espacio de nombre de label, tú (y git) generalmente lo hacen al copyr todas esas tags en tu espacio de nombre. Estas tags no están labeldas en cuanto a su origen, por lo que no hay forma de saber si la label v1.2 es su label, o su label, o ambas. Si excluye confirmaciones accesibles por tags, puede excluir demasiadas confirmaciones.

Para distinguir correctamente las tags del control remoto de las suyas o de cualquier otro control remoto, necesita (re) devise "tags remotas" .