(Cela a commencé comme une réponse à une question en double. J'ai fait un peu de retouche légère pour le nettoyer.)
Toutes les flèches internes de Git sont à sens unique, pointant vers l'arrière. Il n'y a donc pas de courte syntaxe pratique pour avancer: ce n'est tout simplement pas possible.
Il est possible de "bouger contre les flèches", mais la façon de le faire est surprenante si vous ne l'avez pas vue auparavant, puis évidente après. Disons que nous avons:
A <-B <-C <-D <-E <-- last
^
|
\--------- middle
L'utilisation middle~2
suit les flèches deux fois de l' C
arrière vers A
. Alors, comment pouvons-nous passer de C
à D
? La réponse est: nous commençons par E
, en utilisant le nom last
, et nous remontons jusqu'à ce que nous arrivions middle
, en enregistrant les points que nous visitons en cours de route . Ensuite, nous allons aussi loin que nous voulons dans le sens de last
: déplacer un pas vers D
, ou deux vers E
.
Ceci est particulièrement important lorsque nous avons des succursales:
D--E <-- feature1
/
...--B--C <-- master
\
F--G <-- feature2
Quel engagement est une étape après C
? Il n'y a pas de bonne réponse tant que vous n'ajoutez pas à la question: dans le sens de la fonction___ (remplissez le blanc).
Pour énumérer les commits entre C
(à l'exclusion C
) lui-même et, disons G
, nous utilisons:
git rev-list --topo-order --ancestry-path master..feature2
Le --topo-order
fait en sorte que même en présence de branchements et de fusions complexes, les commits sortent par ordre topologique. Ceci n'est requis que si la chaîne n'est pas linéaire. La --ancestry-path
contrainte signifie que lorsque nous travaillons en arrière à partir de feature2
, nous répertorions uniquement les validations qui ont été validées C
comme l'un de leurs propres ancêtres. Autrement dit, si le graphique - ou la partie pertinente de celui-ci de toute façon - ressemble réellement à ceci:
A--B--C <-- master
\ \
\ F--G--J <-- feature2
\ /
H-------I <-- feature3
une simple demande du formulaire feature2..master
énumère les commits J
, G
et I
, et F
et H
dans un certain ordre. Avec --ancestry-path
nous assommons H
et I
: ils ne sont pas des descendants de C
, seulement de A
. Avec --topo-order
nous nous assurons que l'ordre d'énumération est réelle J
, puis G
, alors F
.
La git rev-list
commande répand ces ID de hachage sur sa sortie standard, une par ligne. Pour avancer d'un pas dans la direction de feature2
, alors, nous voulons juste la dernière ligne.
Il est possible (et tentant et peut être utile) d'ajouter --reverse
afin d' git rev-list
imprimer les validations dans l'ordre inverse après les avoir générées. Cela fonctionne, mais si vous l'utilisez dans un pipeline comme celui-ci:
git rev-list --topo-order --ancestry-path --reverse <id1>...<id2> | head -1
pour obtenir juste le "prochain commit dans le sens de id2", et il y a une très longue liste de commits, la git rev-list
commande peut obtenir un tube cassé quand elle essaie d'écrire, head
ce qui a arrêté la lecture de son entrée et est sorti. Étant donné que les erreurs de canal cassé sont normalement ignorées par le shell, cela fonctionne principalement. Assurez-vous simplement qu'ils sont ignorés dans votre utilisation.
Il est également tentant d'ajouter -n 1
à la git rev-list
commande, avec --reverse
. Ne fais pas ça! Cela fait git rev-list
s'arrêter après avoir fait un pas en arrière , puis inverser la liste (une entrée) des commits visités. Donc, cela produit juste<id2>
chaque fois.
Remarque importante
Notez qu'avec des fragments de graphe "diamant" ou "anneau de benzène":
I--J
/ \
...--H M--... <-- last
\ /
K--L
déplacer un commit "en avant" de H
vers last
vous obtiendra soit I
ou K
. Vous ne pouvez rien y faire: les deux validations sont un pas en avant! Si vous commencez ensuite à partir de la validation résultante et passez à une autre étape, vous êtes maintenant engagé sur le chemin sur lequel vous avez commencé.
Le remède à cela est d'éviter de se déplacer une étape à la fois et de s'enfermer dans des chaînes dépendantes du chemin. Au lieu de cela, si vous prévoyez de visiter toute une chaîne de chemin d'ascendance, avant de faire quoi que ce soit d'autre , faites une liste complète de tous les commits de la chaîne:
git rev-list --topo-order --reverse --ancestry-path A..B > /tmp/list-of-commits
Ensuite, visitez chaque commit de cette liste, un par un, et vous obtiendrez toute la chaîne. Le --topo-order
fera en sorte que vous I
frappiez -et- J
dans cet ordre, et K
-et- L
dans cet ordre (bien qu'il n'y ait pas de moyen facile de prédire si vous ferez la paire IJ avant ou après la paire KL).