git: ¿cómo puedo determinar mediante progtwigción si necesito un tirón o un empujón?

Estoy escribiendo un progtwig que verifica el estado de un par de repositorys git clonados.

¿Cómo puedo saber si mi repository necesita un "git pull" o "git push"?

Primero, pull es solo fetch seguido por merge (o rebase ). Esto es importante ya que lo que primero quiere es la respuesta a una pregunta más fácil y relacionada: ¿qué tan avanzado y / o detrás está su repository local, en comparación con algún repository remoto? (Para la respuesta TL; DR, salte a la primera sección encabezada debajo).

Existen complicaciones potenciales adicionales, pero lo más simple es verificar su (s) bifurcación (es) de sucursal (s) contra las de otros repositorys de git. Vamos a darles algunos nombres con fines ilustrativos. Podemos llamar a su repository "L" (para local) y digamos que hay dos repositorys adicionales "RA" (control remoto A) y "RB", que tienen URL almacenadas en su repository local L bajo nombres remotos RA y RB.

La forma más fácil de get todo lo que pueda necesitar de una vez es ejecutar git fetch (o git remote update ) en RA y RB. (Veremos una forma de diferir la búsqueda en un momento, aunque no estoy seguro de que tenga algún valor real).

Dada una configuration típica, la búsqueda de ambos controles remotos copyrá sus sucursales locales a sus propias "sucursales remotas". Supongamos que RA tiene master y dev y que RB tiene master y feature , de modo que una vez completadas tus fetches:

 L$ git rev-parse --short refs/remotes/RA/master feedbee L$ git rev-parse --short refs/remotes/RA/dev feedbee L$ git rev-parse --short refs/remotes/RB/master badf00d L$ git rev-parse --short refs/remotes/RB/feature c0ffee1 

(Estos numbers están compuestos, pero están destinados a la ilustración. Además, si está haciendo esto en código real, no querría --short . Y, puede abreviar los nombres de las twigs, --short refs/remotes/ -y debajo, los refs/heads/ -siempre que sean inequívocos. Sin embargo, si estás escribiendo, probablemente sea más prudente usar los nombres completos por las dudas).

Ahora revisemos los consejos de sus propias sucursales locales:

 L$ git rev-parse --short refs/heads/master feedbee 

Esto significa que su master está sincronizado con el master de RA, que está sincronizado con su desarrollador. Por lo tanto, no debe haber nada que empujar o search allí (aunque ya haya buscado de todos modos para descubrirlo) y, por lo tanto, no hay nada que fusionar o volver a establecer.

Por otro lado, el RB remoto no está sincronizado ya que su master apunta a cometer badf00d . ¿Esto significa que tienes algo para fusionar, o algo para presionar? Tal vez, quizás no: aquí es donde entran las complicaciones. Git no puede ayudar mucho si se necesita la fusión manual, pero puede averiguar si RB está "adelante", "detrás" o ambos, al ver cómo el compromiso los charts se astackn uno contra el otro.

Si el repo L está estrictamente "por delante" del repo RB, es decir, tiene cosas que puede empujar, entonces el fragment del gráfico debe verse más o less así:

 ... - o <-- RB/master: tip-most commit = badf00d \ o - o <-- master: tip commit = feedbee 

Aquí L es "2 adelante", en los términos que el git status le daría, de donde es el repository RB. Si hicieras un push a RB, git entregaría los dos últimos commits a RB y le diría que por favor configure su master para feedbee , lo que lo pondría al día.

Si repo L está estrictamente "detrás" de RB, entonces el fragment de gráfico tendrá el mismo aspecto, pero las tags se invertirán:

 ... - o <-- master \ o - o <-- RB/master 

En este caso, si realizó una fusión para llevar el RB/master al master , git vería un avance rápido y configuraría su master en badf00d . En este punto, el repo RA estaría atrás, y es posible que desee presionar allí.

Sin embargo, todavía hay dos posibilidades más. L podría estar adelante y atrás, por ejemplo:

 ... - o - o <-- master \ o - o <-- RB/master 

Esto requiere una rebase o una combinación real, cualquiera de las cuales podría necesitar una retención manual si git no puede combinar los diversos cambios por sí mismo (o incluso si puede, podría equivocarse).

Por último, es posible, aunque poco probable, que las dos puntas de las twigs no estén relacionadas por completo (no tienen un ancestro común en las ... partes):

 ... - o <-- master ... - o <-- RB/master 

Este es el más complicado de tratar ya que lo que describo a continuación da una count de adelante / atrás falso. (También puede suceder solo con un repository clonado si alguien se ha ido y reescrito todo el historial de uno de los clones. Sería razonable agregar una verificación de paranoia para este caso, usando git merge-base por ejemplo. Pero yo ' Simplemente lo ignoraré de aquí en más).

Encontrar por delante y por detrás count

Con todo eso fuera del path, esta es la forma más rápida de encontrar el recuento de "adelante" y "detrás" para dos twigs (una local, una remota) que usted sabe que están relacionadas. Cambiaré a "origen" remoto, que es el nombre habitual para una fuente de clonación (única), y usaré la forma abreviada de los nombres de las twigs:

 $ ahead=$(git rev-list --count origin/master..master) $ behind=$(git rev-list --count master..origin/master) 

Estos utilizan la syntax del range de gitrevisions para seleccionar revisiones accesibles desde la sugerencia de la sucursal local (la confirmación identificada por el master ) pero no la sugerencia de la sucursal remota (identificada por origin/master ).

Si estás adelante pero no detrás, puedes git push security. Si estás detrás pero no avanzas puedes git merge manera segura para get un avance rápido (no tiene sentido, en este punto, usar el pull ya que has hecho el paso de fetch ). Si ambos van por delante y por detrás, por lo que ambos recuentos son distintos de cero, debe decidir entre fusión o rebase, y qué hacer si fallan. Y, por supuesto, si ambos recuentos son cero, las dos puntas de las twigs identifican el mismo SHA-1 y no hay necesidad de hacer nada.

¿Qué pasa si no quieres usar git fetch ?

Finalmente, debes usar git fetch . Sin embargo, si lo desea, puede comenzar con git ls-remote , que arroja una list de SHA-1 y renombra:

 $ git ls-remote From [url networkingacted] a17c56c056d5fea0843b429132904c429a900229 HEAD ca00f80b58d679e59fc271650f68cd25cdb72b09 refs/heads/maint a17c56c056d5fea0843b429132904c429a900229 refs/heads/master 0029c496ce1b91f10b75ade16604b8e9f5d8d20b refs/heads/next fcd56459647e0c41f2ea9c5b7e2ed827f701fc95 refs/heads/pu e8f6847178db882bd42d5572439333ca4cb3222e refs/heads/todo d5aef6e4d58cfe1549adef5b436f3ace984e8c86 refs/tags/gitgui-0.10.0 3d654be48f65545c4d3e35f5d3bbed5489820930 refs/tags/gitgui-0.10.0^{} [mass snippage] 

Estos SHA-1 no incluyen toda la información gráfica que necesita si los SHA-1 del control remoto difieren de los SHA-1 locales, pero si los SHA-1 que le interesan coinciden, entonces puede ver que no hay nada que search o empujar. Además, si difieren, puede ver si tiene los SHA-1 correspondientes. Por ejemplo, lo anterior muestra que el master del remoto apunta a cometer a17c56c056d5fea0843b429132904c429a900229 . Si lo tuviera (no lo hago) podría usarlo como uno de los especificadores en la git rev-list --count para descubrir qué tan atrás estaba el control remoto. Como no lo tengo, estoy casi seguro detrás: no sé cuánto, pero tengo que search, y luego tal vez fusionar o rebase (no sabré si estoy adelante también hasta que ha podido recuperar).

¿Cómo sabes qué twigs mirar?

Esto puede determinarse mejor de antemano, en lugar de simplemente iterar sobre todas las twigs posibles. Sin embargo, si desea iterar sobre las twigs, la herramienta para esto es git for-each-ref , que toma bastantes arguments. Para encontrar sus propias sucursales locales, por ejemplo:

 $ git for-each-ref --format='%(refname)' refs/heads refs/heads/master refs/heads/precious refs/heads/stash-exp 

Para encontrar twigs de origin remoto:

 $ git for-each-ref --format='%(refname)' refs/remotes/origin refs/remotes/origin/maint refs/remotes/origin/master refs/remotes/origin/next refs/remotes/origin/pu refs/remotes/origin/todo 

Es fácil quitar suficientes cadenas y hacer coincidir los nombres de las twigs, para poder compararlos. Usando otras opciones puede get nombres de twig y SHA-1, si desea verificar (y omitir) la coincidencia de SHA-1, lo que obviamente dará ceros para los recuentos de adelante y de atrás.

En realidad, desde Git 2.5 (lanzado hoy), puede ver rápidamente qué twig está adelante (necesita empujar) o atrás (necesita tirar)

Consulte " Mostrar la información de git delante y detrás de todas las sucursales, incluidos los controles remotos "

 git for-each-ref --format="%(push:track)" refs/heads 

Esto se debe a que <branch>@{push} es un nuevo atajo que hace reference específicamente a la twig ascendente usada para empujar (no siempre es la utilizada para tirar).