Wie kann ich feststellen, ob ein Commit ein Nachkomme eines anderen Commits ist?

146

Wie kann ich mit Git feststellen, ob ein Commit in meinem Zweig ein Nachkomme eines anderen Commits ist?

engie
quelle
2
Dieselbe
Chris Cleeland
11
Könnten Sie Ihre akzeptierte Antwort ändern? Die Mehrheit mag die --is-ancestorLösung.
Robert Siemer

Antworten:

51

Wenn Sie dies programmgesteuert überprüfen möchten (z. B. im Skript), können Sie überprüfen, ob git merge-base A Bes gleich ist git rev-parse --verify A(dann ist A von B aus erreichbar) oder ob dies der Fall ist git rev-parse --verify B(dann ist B von A aus erreichbar). git rev-parsewird hier benötigt, um vom Commit-Namen zum Commit SHA-1 / Commit-ID zu konvertieren.

Die Verwendung git rev-listwie in VonC Antwort ist ebenfalls möglich.

Bearbeiten: Im modernen Git gibt es explizite Unterstützung für diese Abfrage in Form von git merge-base --is-ancestor.


Wenn man von Commits über Sie fragen , ist ein Zweig Spitze , dann git branch --contains <commit>oder git branch --merged <commit>vielleicht besser nicht-programmatische Lösung sein.

Jakub Narębski
quelle
Möglicherweise wäre der schnellste Weg zu git checkout -b quickcheck <more-recent-commit-ID>und dann git branch --contains <older-commit-ID>(und dann git branch -D quickcheck, um den temporären Zweig loszuwerden).
Clee
2
Zwei mögliche Ansätze, die beide viel schlechter sind als der in der Antwort von @ MattR.
JWG
6
@jwg: MattR Antwort ist besser, aber diese Antwort (und wahrscheinlich wird sie akzeptiert) ist älter als Git 1.8.0 und git merge-base --is-ancestorum 2 Jahre.
Jakub Narębski
@ JakubNarębski Fair genug, sorry.
JWG
2
In einem großen Repository (2 Millionen Commits) verglich ich die Geschwindigkeit von git branch --contains <commit>und git merge-base --is-ancestor ...: 3m40s mit 0,14s
Hagello
259

Ab Git 1.8.0 wird dies als Option unterstützt für merge-base:

git merge-base --is-ancestor <maybe-ancestor-commit> <descendant-commit>

Von der Manpage:

--ist Vorfahr

Überprüfen Sie, ob der erste ein Vorfahr des zweiten ist, und beenden Sie ihn mit dem Status 0, wenn er wahr ist, oder mit dem Status 1, wenn nicht. Fehler werden durch einen Status ungleich Null angezeigt, der nicht 1 ist.

Beispielsweise:

git merge-base --is-ancestor origin/master master; echo $?
Matt R.
quelle
4
Nett! Hier ist ein Shell-Skript, das diese Antwort in etwas mit von Menschen realisierbarer Ausgabe zusammenfasst: gist.github.com/simonwhitaker/6354592
Simon Whitaker
1
Mit anderen Worten: git merge-base THING --is-ancestor OF_THING && echo yes || echo nozB:git merge-base my-feature-branch --is-ancestor master && echo yes || echo no
user1735594
2
@smarber git merge-base --is-ancestor -- commit commitarbeitet für Hashes an meiner Seite mit git2.1.4 (Debian / Devuan 7.10 Jessie) und 1.9.1 (Ubuntu 14.04 Trusty), die jetzt ziemlich alt sind. Es funktioniert sogar für Debian Wheezy, wenn Sie dies tun sudo apt-get install git/wheezy-backports.
Tino
15

Diese Art von Operationen beruht auf dem Begriff des Revisionsbereichs, der in der SO-Frage aufgeführt ist: " Unterschied zwischen 'Git-Log-Ursprung / Master' und 'Git-Log-Ursprung / Master'. "

git rev-list sollte in der Lage sein, von einem Commit bis zu einem anderen zurückzugehen, wenn dies erreichbar ist.

Also würde ich versuchen:

git rev-list --boundary 85e54e2408..0815fcf18a
0815fcf18a19441c1c26fc3495c4047cf59a06b9
8a1658147a460a0230fb1990f0bc61130ab624b2
-85e54e240836e6efb46978e4a1780f0b45516b20

(Boundary Commits werden vorangestellt -)

Wenn das zuletzt angezeigte Commit mit dem ersten Commit im git rev-listBefehl identisch ist , handelt es sich um ein Commit, das vom zweiten Commit aus erreichbar ist.

Wenn das erste Commit vom zweiten nicht erreichbar ist, git rev-listsollte nichts zurückgegeben werden.

git rev-list --boundary A..B

würde vorbei enden A, wenn Aerreichbar ist von B.
Es ist das gleiche wie:

git rev-list --boundary B --not A

mit Beiner positiven und Aeiner negativen Referenz .
Es beginnt bei Bund geht zurück durch das Diagramm, bis es auf eine Revision stößt, die von erreichbar ist A.
Ich würde argumentieren, dass wenn Aes direkt erreichbar ist B, es auf sich selbst trifft (und aufgrund der --boundaryOption angezeigt wird ) A.

VonC
quelle
Dies klingt nach einem häufig genug verwendeten Anwendungsfall, bei dem ich überrascht bin, dass git noch keinen "Porzellan" -Befehl veröffentlicht hat, der genau dies tut.
Lawrence I. Siden
1
@lsiden: wahr. Randnotiz: Vergessen Sie nicht, dass Sie zum programmgesteuerten Überprüfen nicht den Porzellanbefehl (wie in stackoverflow.com/questions/6976473/… ) verwenden, sondern die Installationsbefehle (wie in stackoverflow.com/questions dargestellt) / 3878624 /… )
VonC
Oh Mann, es sieht so aus, als müsste ich zurückgehen und an meinen Shell-Skripten arbeiten!
Lawrence I. Siden
1
Frage: Warum hat das -85e54e2...im Snippet ein Minus? auch ein möglicher Tippfehler: "... ist das gleiche wie das erste Commit ..."
sdaau
1
@sdaau -bedeutet, dass es sich um ein Boundary Commit handelt. Ich habe die Antwort bearbeitet, um dies klarer zu machen, sowie um Dokumentlinks zu aktualisieren und den Tippfehler für diese 5 Jahre alte Antwort zu korrigieren.
VonC
11

Eine andere Möglichkeit wäre, zu verwenden git logund grep.

git log --pretty=format:%H abc123 | grep def456

Dies erzeugt eine Ausgabezeile, wenn commit def456 ein Vorfahr von commit abc123 ist, oder keine andere Ausgabe.

Normalerweise können Sie das --prettyArgument weglassen , aber es ist erforderlich, wenn Sie sicherstellen möchten, dass Sie nur die tatsächlichen Commit-Hashes und nicht die Protokollkommentare usw. durchsuchen.

itub
quelle
Ich habe erwartet, dass diese Lösung langsam ist, aber sie ist tatsächlich ziemlich schnell, selbst für ein Projekt mit mehr als 20.000 Commits
Renato Zannon
2
anstatt --prettyich benutze --oneline: git log --oneline ce2ee3d | grep ec219ccfunktioniert super
gens
3

https://stackoverflow.com/a/13526591/895245 erwähnt es jetzt, um es menschlicher zu machen:

git-is-ancestor() (
  if git merge-base --is-ancestor "$1" "$2"; then
      echo 'ancestor'
  elif git merge-base --is-ancestor "$2" "$1"; then
      echo 'descendant'
  else
      echo 'unrelated'
  fi
)
alias giia='git-is-ancestor'
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
Dies gibt 'nicht verwandt' zurück, wenn beide Parameter auf dasselbe Commit verweisen
mik
1

git show-branch branch-sha1 commit-sha1

Wo:

  • branch-sha1: das sha1 in Ihrem Zweig, das Sie überprüfen möchten
  • commit-sha1: Das sha1 des Commits, gegen das Sie prüfen möchten
viq
quelle
0

Wenn Sie verwenden git merge-base --is-ancestor, stellen Sie sicher, dass Sie Git 2.28 (Q3 2020) verwenden.

Mit Git 2.28 (Q3 2020) wurden einige Felder in " struct commit", die nicht immer vorhanden sein müssen, verschoben, um Platten festzuschreiben.

Siehe Commit c752ad0 , Commit c49c82a , Commit 4844812 , Commit 6da43d9 (17. Juni 2020) von Abhishek Kumar ( abhishekkumar2718) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit d80bea4 , 06. Juli 2020)

commit-graph: vorstellen commit_graph_data_slab

Unterzeichnet von: Abhishek Kumar

Das struct commit wird in vielen Kontexten verwendet. Mitglieder generationund graph_poswerden jedoch nur für Commit-Graph-bezogene Operationen verwendet und verschwenden anderweitig Speicher.

Diese Verschwendung wäre beim Übergang zur Generierungsnummer v2 stärker ausgeprägt gewesen, bei der anstelle der aktuellen 32-Bit-Generierung die 64-Bit-Generierungsnummer verwendet wird.

Da häufig auf sie zugegriffen wird, führen wir struct ein commit_graph_dataund verschieben sie auf eine commit_graph_dataPlatte.

Während die gesamte Testsuite genauso schnell läuft wie master(Serie: 26m48s , master: 27m34s, schneller um 2,87%), wurden bestimmte Befehle wie git merge-base --is-ancestorum 40% verlangsamt, wie von Szeder Gábor entdeckt .
Nach Minimierung des Commit-Slab-Zugriffs bleibt die Verlangsamung bestehen, liegt jedoch näher bei 20%.

Derrick Stolee glaubt, dass die Verlangsamung eher auf den zugrunde liegenden Algorithmus als auf die Langsamkeit des Commit-Slab-Zugriffs zurückzuführen ist, und wir werden in einer späteren Reihe darauf eingehen.

VonC
quelle
-1

Aufbauend auf der Antwort von itub, falls Sie dies für alle Tags im Repository tun müssen:

for i in `git tag` ; do echo -ne $i "\t" ; git log --pretty=format:%H $i | (grep <commit to find> || echo ""); done
Okroquette
quelle