(Esto comenzó como una respuesta a una pregunta duplicada. He hecho un poco de edición ligera para limpiarlo).
Todas las flechas internas de Git son unidireccionales, apuntando hacia atrás. Por lo tanto, no hay una sintaxis corta y conveniente para avanzar: simplemente no es posible.
Que es posible "movimiento en contra de las flechas", pero la manera de hacerlo es sorprendente si no se ha visto antes, y luego obvia después. Digamos que tenemos:
A <-B <-C <-D <-E <-- last
^
|
\--------- middle
Usando middle~2
sigue las flechas dos veces de C
atrás a A
. Entonces, ¿cómo nos movemos de C
a D
? La respuesta es: comenzamos en E
, usando el nombre last
, y trabajamos hacia atrás hasta llegar middle
, registrando los puntos que visitamos en el camino . Luego nos movemos tan lejos como queremos en la dirección de last
: mover un paso a D
, o dos a E
.
Esto es particularmente importante cuando tenemos sucursales:
D--E <-- feature1
/
...--B--C <-- master
\
F--G <-- feature2
¿Qué commit es un paso después C
? No hay una respuesta correcta hasta que agregue a la pregunta: en la dirección de la función___ (complete el espacio en blanco).
Para enumerar las confirmaciones entre C
(excluyendo C
) en sí y, por ejemplo, G
usamos:
git rev-list --topo-order --ancestry-path master..feature2
Esto --topo-order
se asegura de que incluso en presencia de complejas ramificaciones y fusiones, los commits salgan en orden topológico. Esto solo es necesario si la cadena no es lineal. La --ancestry-path
restricción significa que cuando trabajamos desde atrás feature2
, solo enumeramos los commits que tienen commit C
como uno de sus propios antepasados. Es decir, si el gráfico, o el fragmento relevante de todos modos, en realidad se ve así:
A--B--C <-- master
\ \
\ F--G--J <-- feature2
\ /
H-------I <-- feature3
una simple solicitud del formulario feature2..master
enumera commits J
, G
y I
, y F
y H
en algún orden. Con --ancestry-path
noqueamos H
y I
: no son descendientes de C
, solo de A
. Con --topo-order
nos aseguramos de que el orden de enumeración real sea J
, entonces G
, entonces F
.
El git rev-list
comando derrama estas identificaciones hash en su salida estándar, una por línea. Para avanzar un paso en la dirección de feature2
, entonces, solo queremos la última línea.
Es posible (y tentador y puede ser útil) agregar --reverse
para que git rev-list
imprima las confirmaciones en orden inverso después de generarlas. Esto funciona, pero si lo usa en una tubería como esta:
git rev-list --topo-order --ancestry-path --reverse <id1>...<id2> | head -1
solo para obtener el "próximo commit en la dirección de id2", y hay una lista muy larga de commits, el git rev-list
comando puede obtener una tubería rota cuando intenta escribir en el head
que dejó de leer su entrada y salió. Dado que el shell normalmente ignora los errores de tubería rota, esto funciona principalmente. Solo asegúrate de que se ignoren en tu uso.
También es tentador agregar -n 1
al git rev-list
comando, junto con --reverse
. ¡No lo hagas! Eso hace que git rev-list
pare después de caminar un paso atrás y luego revierta la lista de confirmaciones visitadas (una entrada). Entonces esto solo produce <id2>
cada vez.
Nota al margen importante
Tenga en cuenta que con los fragmentos de gráfico "diamante" o "anillo de benceno":
I--J
/ \
...--H M--... <-- last
\ /
K--L
moviendo una cometen "hacia adelante" de H
hacia last
obtendrá ya sea I
o K
. No hay nada que pueda hacer al respecto: ¡ambas confirmaciones son un paso adelante! Si luego comienza desde la confirmación resultante y da otro paso, ahora está comprometido con cualquier ruta en la que comenzó.
La cura para esto es evitar moverse un paso a la vez y quedar encerrado en cadenas dependientes de la ruta. En cambio, si planea visitar una cadena completa de ruta de ascendencia, antes de hacer cualquier otra cosa , haga una lista completa de todas las confirmaciones en la cadena:
git rev-list --topo-order --reverse --ancestry-path A..B > /tmp/list-of-commits
Luego, visite cada confirmación en esta lista, una a la vez, y obtendrá toda la cadena. Se --topo-order
asegurará de que golpee I
-y- J
en ese orden, y K
-y- L
en ese orden (aunque no hay una manera fácil de predecir si hará el par IJ antes o después del par KL).