(Isso começou como uma resposta a uma pergunta duplicada. Fiz um pouco de edição leve para limpá-la.)
Todas as setas internas do Git são unidirecionais, apontando para trás. Portanto, não existe uma sintaxe curta e conveniente para avançar: simplesmente não é possível.
Ele é possível "movimento contra as flechas", mas a maneira de fazer isso é surpreendente, se você não tê-lo visto antes, e depois óbvio depois. Digamos que temos:
A <-B <-C <-D <-E <-- last
^
|
\--------- middle
O uso middle~2
segue as setas duas vezes de C
volta para A
. Então, como passamos C
para D
? A resposta é: começamos E
usando o nome last
e trabalhamos para trás até chegarmos middle
, registrando os pontos que visitamos ao longo do caminho . Depois, apenas avançamos o quanto queremos na direção de last
: mova um passo para D
, ou dois paraE
.
Isso é particularmente importante quando temos filiais:
D--E <-- feature1
/
...--B--C <-- master
\
F--G <-- feature2
Qual commit é um passo depois C
? Não há resposta correta até você adicionar à pergunta: na direção do recurso___ (preencha o espaço em branco).
Para enumerar os commits entre C
(excluindo C
) em si e, digamos, G
usamos:
git rev-list --topo-order --ancestry-path master..feature2
Os --topo-order
garante que, mesmo na presença do complexo ramificação-and-fusão, os commits sair, a fim topologicamente-ordenada. Isso é necessário apenas se a corrente não for linear. A --ancestry-path
restrição significa que, quando trabalhamos de trás para frente feature2
, listamos apenas confirmações que foram confirmadas C
como um de seus ancestrais. Ou seja, se o gráfico - ou a parte relevante dele de qualquer maneira - se parecer com isso:
A--B--C <-- master
\ \
\ F--G--J <-- feature2
\ /
H-------I <-- feature3
um simples pedido do formulário feature2..master
enumera commits J
, G
e I
, e F
e H
em alguma ordem. Com --ancestry-path
nós nocauteamos H
e I
: eles não são descendentes de C
, apenas de A
. Com --topo-order
, garantimos que a ordem de enumeração real seja J
, então G
, então F
.
O git rev-list
comando espalha esses IDs de hash em sua saída padrão, um por linha. Para avançar um passo na direção de feature2
, então, queremos apenas a última linha.
É possível (e tentador e pode ser útil) adicionar --reverse
para que git rev-list
imprima os commits em ordem inversa após gerá-los. Isso funciona, mas se você o usar em um pipeline como este:
git rev-list --topo-order --ancestry-path --reverse <id1>...<id2> | head -1
para obter o "próximo commit na direção do id2", e há uma lista muito longa de commits, o git rev-list
comando pode obter um pipe quebrado ao tentar gravar no head
qual parou de ler sua entrada e saiu. Como erros de tubos quebrados são normalmente ignorados pelo shell, isso geralmente funciona. Apenas verifique se eles são ignorados no seu uso.
Também é tentador adicionar -n 1
ao git rev-list
comando, juntamente com --reverse
. Não faça isso! Isso faz com que você git rev-list
pare depois de dar um passo para trás e inverta a lista de confirmações visitadas (uma entrada). Então, isso só produz <id2>
sempre.
Nota lateral importante
Observe que com os fragmentos do gráfico "diamante" ou "anel benzeno":
I--J
/ \
...--H M--... <-- last
\ /
K--L
movendo um commit "forward" de H
direção last
vai te quer I
ou K
. Não há nada que você possa fazer sobre isso: ambos os commits estão um passo à frente! Se você iniciar a partir do commit resultante e seguir outra etapa, agora estará comprometido com o caminho que iniciou.
A cura para isso é evitar dar um passo de cada vez e ficar preso a cadeias dependentes do caminho. Em vez disso, se você planeja visitar uma cadeia inteira de caminho de ancestralidade, antes de fazer qualquer outra coisa , faça uma lista completa de todos os commits na cadeia:
git rev-list --topo-order --reverse --ancestry-path A..B > /tmp/list-of-commits
Em seguida, visite cada confirmação nesta lista, uma de cada vez, e você obterá toda a cadeia. O --topo-order
fará com que você bateu I
-e- J
nessa ordem, e K
e- L
nessa ordem (embora não há nenhuma maneira fácil de prever se você vai fazer o par IJ antes ou depois de o par KL).