Allgemeines Problem: Wie finde ich eine Liste von Commits, die all diese Commits als Vorfahren oder damit zusammenhängend die ersten Commits haben, die all diese Commits enthalten.
Ich kann Zweige (ähnlich Tags) finden, die die Commits enthalten, indem ich nach Zweigen suche, die von git branch --contains <commit>
für alle Commits in der Menge zurückgegeben werden, aber git rev-list
hat keine --contains
-Option. Effektiv suche ich nach einer Möglichkeit, die regulären Argumente --contains
mit git rev-list
zu kombinieren und die Ausgabe auf Commits zu beschränken, die alle die aufgelisteten Commits enthalten, keines davon (was wie --contains
normal funktioniert).
Spezifisches Beispiel: Gegebene commits a
, b
, c
, wie kann ich das erste Commit finden, das alle drei Commits in seiner Abstammung hat?
Wie finde ich zum Beispiel den Commit, der mit X markiert ist?
%Vor% Ich nehme an, ich kann etwas mit git rev-list
machen und möglicherweise mit der <commit1>...<commit2>
-Notation, aber ich kann nicht weiter arbeiten.
Ich denke, die Antwort auf diese Frage ist, dass git nicht dafür gemacht wurde. Git mag die Idee von "Kindern eines Commits" nicht wirklich, und dafür gibt es einen sehr guten Grund: Es ist nicht sehr gut definiert. Da ein Commit nichts über seine Kinder weiß, ist es ein sehr vages Set. Sie haben möglicherweise nicht alle Zweige in Ihrem Repo und so einige Kinder fehlen.
Gits interne Speicherstruktur macht auch das Finden der Kinder eines Commits zu einer ziemlich teuren Operation, da Sie das Revisionsdiagramm aller Köpfe entweder zu ihren entsprechenden Wurzeln gehen müssen oder bis Sie alle Commits gesehen haben, deren Kinder Sie kennenlernen wollen .
Das einzige Konzept dieser Art, das git unterstützt, ist die Idee eines commit mit anderem Commit. Aber diese Funktion wird nur von sehr wenigen Git-Befehlen unterstützt ( git branch
ist einer davon). Und wo git es unterstützt, unterstützt es es nicht für willkürliche Commits, sondern nur für Verzweigungsköpfe.
Das alles mag eine ziemlich harte Einschränkung von Git erscheinen, aber in der Praxis stellt sich heraus, dass Sie die "Kinder" eines Commits nicht brauchen, aber normalerweise nur wissen müssen, welche Zweige ein bestimmtes Commit enthalten.
> Das sagte alles: Wenn Sie wirklich die Antwort auf Ihre Frage bekommen wollen, müssen Sie Ihr eigenes Skript schreiben, das es findet. Der einfachste Weg ist, mit der Ausgabe von git rev-list --parents --reverse --all
zu beginnen. Wenn Sie diese Zeile für Zeile analysieren, erstellen Sie einen Baum und markieren für jeden Knoten, ob es sich um ein untergeordnetes Element der gesuchten Commits handelt. Sie tun dies, indem Sie die Commits selbst markieren, sobald Sie sie erfüllen, und dann diese Eigenschaft an alle ihre Kinder weiterleiten usw.
Sobald Sie einen Commit haben, der so markiert ist, dass er alle Commits enthält, fügen Sie ihn zu Ihrer "Lösungsliste" hinzu und markieren alle seine Kinder als tot - sie dürfen keine ersten Commits mehr enthalten . Diese Eigenschaft wird dann auch an alle Nachkommen weitergegeben.
Sie können hier ein wenig Speicher speichern, wenn Sie keine Teile des Baums speichern, die keine der verlangten Commits enthalten.
Bearbeiten Hacked einige Python-Code
%Vor%Eine mögliche Lösung:
Verwenden Sie 'git merge-base a b c', um das Commit als Ausgangspunkt für einen Aufruf der Rev-Liste zu verwenden. Wir nennen es $ MERGE_BASE.
Verwenden Sie den Aufruf 'git rev-list $ MERGE_BASE..HEAD', um alle Commits von ihrem gemeinsamen Vorfahren zu HEAD aufzulisten. Schleife durch diesen Ausgang (Pseudocode):
%Vor%Dies wird für Ihr Beispiel oben funktionieren, aber wird ein falsches positives Ergebnis liefern, wenn sie nie zusammengeführt wurden, nicht in das Commit unmittelbar nach dem jüngsten von a, b, c oder wenn mehrere Merge Commits zu bringen waren zusammen a, b und c (wenn sie jeweils in ihrem eigenen Zweig wohnten). Es gibt noch ein bisschen Arbeit, um den ältesten Nachkommen zu finden.
Sie sollten dann das oben beschriebene mit etwas beginnen, das mit $ OLDEST_DESCENDANT beginnt und in der DAG rückwärts in Richtung HEAD geht (rev-list --reverse $ OLDEST_DESCENDANT ~ ..HEAD), testen, um zu sehen, dass die Ausgabe von 'rev- Liste $ MERGE_BASE ~ .. $ OLDEST enthält alle gewünschten Commits a, b und c (vielleicht gibt es eine bessere Möglichkeit zu testen, dass sie erreichbar sind als rev-list).
Wie Twalberg erwähnt, scheint das Testen der einzelnen Commits nicht optimal und langsam, aber es ist ein Anfang. Dieser Ansatz hat den Vorteil gegenüber seiner Merge-Commit-List-Methode, dass er eine gültige Antwort liefert, wenn sich alle Eingabe-Commits in demselben Zweig befinden.
Die Leistung wird hauptsächlich durch die Abstände zwischen der Zusammenführungsbasis, dem Kopf, X und dem jüngsten des gewünschten Festschreibungssatzes (a, b und c) beeinflusst.
Wie wäre es mit:
%Vor%Angenommen, Sie haben nur 1 Zusammenführung. Selbst wenn Sie mehrere Zusammenführungen haben, ist der älteste der, der Änderungen von allen drei Commits enthält
Tags und Links git git-branch git-log git-rev-list