Dies ist nicht gerade eine echte Antwort, aber ich brauche Zugriff auf die Formatierung und viel Platz. Ich werde versuchen, die Theorie hinter den beiden besten Antworten zu beschreiben: die akzeptierte und die (zumindest derzeit) bestplatzierte . Tatsächlich beantworten sie jedoch unterschiedliche Fragen .
Commits in Git werden sehr oft auf mehreren Zweigen gleichzeitig "ausgeführt". Genau darum geht es in der Frage. Gegeben:
...--F--G--H <-- master
\
I--J <-- develop
Wenn die Großbuchstaben für tatsächliche Git-Hash-IDs stehen, suchen wir häufig nur nach CommitH oder nur nach CommitsI-J in unserer git logAusgabe. Commits bis durch Gsind auf beiden Zweigen, daher möchten wir sie ausschließen.
(Beachten Sie, dass in so gezeichneten Diagrammen neuere Commits rechts liegen. Die Namen wählen das Commit ganz rechts in dieser Zeile aus. Jedes dieser Commits verfügt über ein übergeordnetes Commit, das das Commit zu ihrer Linken ist: das übergeordnete Commit von His Gund das Elternteil von Jist I. Das Elternteil von Iist Gwieder. Das Elternteil von istG ist Fund Fhat ein Elternteil, das hier einfach nicht angezeigt wird: Es ist Teil des ...Abschnitts.)
Für diesen besonders einfachen Fall können wir verwenden:
git log master..develop # note: two dots
sehen I-J , oder:
git log develop..master # note: two dots
anzuzeigen Hnur. Der Name auf der rechten Seite nach den beiden Punkten sagt Git: Ja, diese Commits . Der Name auf der linken Seite vor den beiden Punkten sagt Git: Nein, nicht diese Commits . Git beginnt am Ende - beim Festschreiben Hoder Festschreiben J- und arbeitet rückwärts . Weitere Informationen finden Sie unter Think Like (a) Git .
So wie die ursprüngliche Frage formuliert ist, besteht der Wunsch darin, Commits zu finden, die von einem bestimmten Namen aus erreichbar sind , jedoch nicht von einem anderen Namen in derselben allgemeinen Kategorie. Das heißt, wenn wir ein komplexeres Diagramm haben:
O--P <-- name5
/
N <-- name4
/
...--F--G--H--I---M <-- name1
\ /
J-----K <-- name2
\
L <-- name3
Wir könnten einen dieser Namen wie name4oder auswählen name3und fragen: Welche Commits können unter diesem Namen gefunden werden, aber nicht unter einem der anderen Namen? Wenn wir name3die Antwort auswählen , ist Commit L. Wenn wir auswählen name4, lautet die Antwort überhaupt keine Festschreibungen: Die Festschreibung, deren name4Name Festschreibung, Naber Festschreibung Nist , kann durch Beginnen bei gefunden werdenname5 und rückwärts arbeiten.
Die akzeptierte Antwort funktioniert mit Remote-Tracking-Namen anstelle von Zweigstellennamen und ermöglicht es Ihnen, einen - den buchstabierten origin/merge-only- als ausgewählten Namen zu bestimmen und alle anderen Namen in diesem Namespace anzuzeigen. Außerdem wird vermieden, dass Zusammenführungen angezeigt werden: Wenn wir name1als "interessanten Namen" auswählen und " Show me Commitsname1 " auswählen , die von einem anderen Namen aus erreichbar sind, aber keinen anderen Namen haben , werden sowohl Merge Commit Mals auch reguläres Commit angezeigtI .
Die beliebteste Antwort ist ganz anders. Es geht nur um die Grafik zu begehen durchqueren , ohne folgenden beiden Beine eines Merge, und ohne zu zeigen , eine der Commits , die sind verschmilzt. Wenn wir name1zum Beispiel mit beginnen, werden wir nicht anzeigen M(es ist eine Zusammenführung), aber wenn das erste übergeordnete Element der Zusammenführung MCommit ist I, werden wir uns nicht einmal Commits Jund ansehen K. Wir werden am Ende zeigt begehen I, und verpflichtet auch H, G, F, und so weiter-keines dieser sind Merge Commits und alle erreichbar sind , indem beim Start Mund nach hinten arbeiten, nur der Besuch zuerst Elternteil jeden merge zu begehen.
Die beliebteste Antwort ist ziemlich gut geeignet, um beispielsweise zu prüfen, masterwann masterein Zweig nur für Zusammenführungen gedacht ist. Wenn alle "echte Arbeit" an Seitenzweigen geleistet wurde, die anschließend zusammengeführt wurden master, haben wir ein Muster wie das folgende:
I---------M---------N <-- master
\ / \ /
o--o--o o--o--o
Dabei handelt es sich bei allen nicht mit Buchstaben versehenen oCommits um normale (nicht zusammengeführte) Commits Mund Num Zusammenführungs-Commits. Commit Iist die erste begehen: die allererste jemals gemacht begehen, und der einzige, der sollte auf Master sein , das kein merge begehen ist. Wenn das git log --first-parent --no-merges masterein anderes Commit als zeigtI , haben wir eine Situation wie diese:
I---------M----*----N <-- master
\ / \ /
o--o--o o--o--o
wo wir sehen wollen begehen * dass ein direkt ausgeführt wurde master, nicht durch Zusammenführen eines Feature-Zweigs.
Kurz gesagt, die beliebte Antwort ist großartig, um zu sehen, masterwannmaster nur Zusammenführen gedacht ist, ist aber für andere Situationen nicht so gut geeignet. Die akzeptierte Antwort funktioniert für diese anderen Situationen.
Sind Remote-Tracking-Namen wie origin/master Zweig Namen?
Einige Teile von Git sagen, dass sie nicht:
git checkout master
...
git status
sagt on branch master, aber:
git checkout origin/master
...
git status
sagt HEAD detached at origin/master. Ich stimme lieber mit git checkout/ git switch: überein, origin/masterist kein Filialname, weil man nicht "drauf" kommen kann.
Die akzeptierte Antwort verwendet Remote-Tracking-Namen origin/*als "Filialnamen":
git log --no-merges origin/merge-only \
--not $(git for-each-ref --format="%(refname)" refs/remotes/origin |
grep -Fv refs/remotes/origin/merge-only)
Die mittlere Zeile, die aufgerufen wird git for-each-ref, durchläuft die Namen der Fernverfolgung für die benannte Fernbedienungorigin .
Der Grund, warum dies eine gute Lösung für das ursprüngliche Problem ist, ist, dass wir hier eher an den Filialnamen anderer als an unseren Filialnamen interessiert sind . Das heißt aber, wir haben Branch als etwas anderes definiert als unsere Branchennamen definiert haben . Das ist in Ordnung: Sei dir nur bewusst, dass du das tust, wenn du es tust.
git log Durchläuft einige Teile des Festschreibungsdiagramms
Was wir hier wirklich suchen, sind Serien von dem, was ich Daglets genannt habe: siehe Was genau meinen wir mit "Zweig"? Das heißt, wir suchen nach Fragmenten innerhalb einer Teilmenge des gesamten Commit-Diagramms .
Immer wenn wir Git einen Zweignamen wie master, einen Tag-Namen wie v2.1oder einen Remote-Tracking-Namen wie ansehen origin/master, möchten wir, dass Git uns von diesem Commit erzählt und jedem Commit , das wir von diesem Commit erhalten können: von dort aus und rückwärts arbeiten.
In der Mathematik wird dies als Gehen eines Graphen bezeichnet . Gits Commit-Graph ist ein Directed Acyclic Graph oder DAG , und diese Art von Graph eignet sich besonders zum Gehen. Wenn Sie einen solchen Graphen durchlaufen, besuchen Sie jeden Graphenscheitelpunkt, der über den verwendeten Pfad erreichbar ist. Die Eckpunkte im Git-Diagramm sind die Commits, wobei die Kanten Bögen - Einwegverknüpfungen - sind, die von jedem Kind zu jedem Elternteil verlaufen. (Hier denken Sie wie (a) Git Spiel. Die Einbahnstraße von Bögen bedeutet, dass Git vom Kind zum Elternteil rückwärts arbeiten muss.)
Die beiden wichtigsten Git-Befehle für das Gehen mit Grafiken sind git logund git rev-list. Diese Befehle sind sehr ähnlich - tatsächlich werden sie meistens aus denselben Quelldateien erstellt -, aber ihre Ausgabe ist unterschiedlich: git logErzeugt eine Ausgabe für Menschen zum Lesen, während sie git rev-listeine Ausgabe für andere Git-Programme erzeugt. 1 Beide Befehle führen diese Art von Graph-Walk durch.
Der Graph Walk, den sie machen, ist speziell: gegeben eine Reihe von Startpunkt-Commits (möglicherweise nur ein Commit, möglicherweise eine Reihe von Hash-IDs, möglicherweise eine Reihe von Namen, die in Hash-IDs aufgelöst werden), gehen Sie durch das Diagramm und besuchen Sie Commits . Bestimmte Anweisungen, wie z. B. --notein Präfix ^, oder --ancestry-path, oder --first-parent, ändern den Diagrammlauf auf irgendeine Weise.
Während sie den Graph Walk machen, besuchen sie jedes Commit. Aber sie nur drucken jedoch eine ausgewählte Teilmenge der durchgeführten Commits. Anweisungen wie --no-mergesoder --before <date>teilen den Graph-Walking-Code mit, der zum Drucken verpflichtet ist .
Um diesen Besuch durchzuführen, verwenden diese beiden Befehle jeweils ein Commit Prioritätswarteschlange . Sie führengit logodergit rev-listund geben ihm einige Startpunkt-Commits. Sie stellen diese Commits in die Prioritätswarteschlange. Zum Beispiel ein einfaches:
git log master
wandelt den Namen masterin eine unformatierte Hash-ID um und stellt diese eine Hash-ID in die Warteschlange. Oder:
git log master develop
wandelt beide Namen in Hash-IDs um und stellt - vorausgesetzt, es handelt sich um zwei verschiedene Hash-IDs - beide in die Warteschlange.
Die Priorität der Commits in dieser Warteschlange wird durch noch mehr Argumente bestimmt. Zum Beispiel --author-date-ordersagt das Argument git logodergit rev-list die verwenden Autor Zeitstempel, anstatt die Committer Zeitstempel. Standardmäßig wird der Committer-Zeitstempel verwendet und das Commit nach dem neuesten Datum ausgewählt: das Commit mit dem höchsten numerischen Datum. Unter der master developAnnahme, dass diese Entschlossenheit zu zwei verschiedenen Commits führt, zeigt Git an, welche später kam zuerst kam, da diese ganz vorne in der Warteschlange steht.
In jedem Fall wird der Revisionslaufcode jetzt in einer Schleife ausgeführt:
- Während sich Commits in der Warteschlange befinden:
- Entfernen Sie den ersten Warteschlangeneintrag.
- Entscheiden Sie, ob dieses Commit überhaupt gedruckt werden soll. Beispiel
--no-merges: Drucken Sie nichts, wenn es sich um ein Zusammenführungs-Commit handelt. --before: Drucken Sie nichts aus, wenn das Datum nicht vor der angegebenen Zeit liegt. Wenn das Drucken nicht unterdrückt wird, drucken Sie das Commit: fürgit log , zeigen Sie das Protokoll an; zum git rev-listDrucken seiner Hash - ID.
- Stellen Sie einige oder alle übergeordneten Commits dieses Commits in die Warteschlange (solange es nicht vorhanden ist und noch nicht besucht wurde 2 ). Die normale Standardeinstellung ist, alle Eltern einzugeben. Mit wird
--first-parentalle bis auf das erste übergeordnete Element jeder Zusammenführung unterdrückt .
(Beides git logund git rev-listkann die Historie mit oder ohne Umschreiben der Eltern vereinfachen an dieser Stelle auch eine , aber das werden wir hier überspringen.)
Bei einer einfachen Kette, z. B. Start bei HEADund rückwärts arbeiten, wenn keine Zusammenführungs-Commits vorhanden sind, befindet sich in der Warteschlange immer ein Commit oben in der Schleife. Es gibt ein Commit, also entfernen wir es und drucken es aus, stellen sein (einzelnes) übergeordnetes Element in die Warteschlange und gehen erneut umher. Wir folgen der Kette rückwärts, bis wir das allererste Commit erreichen, oder der Benutzer hat die git logAusgabe satt und wird beendet das Programm. In diesem Fall spielt keine der Bestelloptionen eine Rolle: Es muss immer nur ein Commit angezeigt werden.
Wenn es Zusammenschlüsse gibt und wir beiden Elternteilen folgen - beide "Beine" des Zusammenschlusses - oder wenn Sie git logoder gebengit rev-list mehr als ein Start-Commit geben, sind die Sortieroptionen wichtig.
Betrachten Sie zuletzt die Auswirkung eines Commit-Spezifizierers --notoder ^vor diesem. Diese haben verschiedene Möglichkeiten, sie zu schreiben:
git log master --not develop
oder:
git log ^develop master
oder:
git log develop..master
alle bedeuten dasselbe. Das --notist wie das Präfix, ^außer dass es für mehr als einen Namen gilt:
git log ^branch1 ^branch2 branch3
bedeutet nicht Zweig1, nicht Zweig2, ja Zweig3; aber:
git log --not branch1 branch2 branch3
bedeutet nicht branch1, nicht branch2, nicht branch3, und Sie müssen eine Sekunde verwenden --not, um es auszuschalten:
git log --not branch1 branch2 --not branch3
das ist ein bisschen umständlich. Die beiden "Nicht" -Direktiven werden über XOR kombiniert. Wenn Sie also wirklich wollen, können Sie schreiben:
git log --not branch1 branch2 ^branch3
bedeutet nicht branch1, nicht branch2, ja branch3 , wenn Sie verschleiern wollen .
Dies alles wirkt sich auf den Graph Walk aus. Wie git logoder git rev-listdie Grafik geht, macht es sicher nicht in die Priorität zu setzen Warteschlange jede verpflichten , dass aus einem der erreichbar ist negierte Referenzen. (Tatsächlich wirken sie sich auch auf das Start-Setup aus: Negierte Commits können nicht direkt über die Befehlszeile in die Prioritätswarteschlange gestellt werden und git log master ^masterzeigen beispielsweise nichts an.)
Die gesamte ausgefallene Syntax, die in der Dokumentation zu gitrevisions beschrieben ist, nutzt dies, und Sie können dies mit einem einfachen Aufruf von verfügbar machen git rev-parse. Zum Beispiel:
$ git rev-parse origin/pu...origin/master # note: three dots
b34789c0b0d3b137f0bb516b417bd8d75e0cb306
fc307aa3771ece59e174157510c6db6f0d4b40ec
^b34789c0b0d3b137f0bb516b417bd8d75e0cb306
Die Dreipunktsyntax bedeutet, dass Commits von links oder rechts erreichbar sind, jedoch keine Commits, die von beiden Seiten erreichbar sind . In diesem Fall ist das origin/masterCommit b34789c0bselbst über origin/pu( fc307aa37...) erreichbar, sodass der origin/masterHash zweimal angezeigt wird, einmal mit einer Negation. Tatsächlich erreicht Git jedoch die Dreipunktsyntax, indem zwei positive Referenzen eingegeben werden - die beiden nicht negierten Hash-IDs - und eine negative, dargestellt durch die^ Präfix.
Ähnlich:
$ git rev-parse master^^@
2c42fb76531f4565b5434e46102e6d85a0861738
2f0a093dd640e0dad0b261dae2427f2541b5426c
Die ^@Syntax bedeutet, dass allemaster^ übergeordneten Elemente des angegebenen Commits und selbst - das erste übergeordnete Element des durch den Filialnamen ausgewählten Commits master- ein Zusammenführungs-Commit sind, sodass es zwei übergeordnete Elemente hat. Das sind die beiden Eltern. Und:
$ git rev-parse master^^!
0b07eecf6ed9334f09d6624732a4af2da03e38eb
^2c42fb76531f4565b5434e46102e6d85a0861738
^2f0a093dd640e0dad0b261dae2427f2541b5426c
Das ^!Suffix bedeutet das Commit selbst, aber keines seiner Eltern . In diesem Fall master^ist 0b07eecf6.... Wir haben bereits beide Eltern mit dem ^@Suffix gesehen; hier sind sie wieder, aber diesmal negiert.
1 Viele Git-Programme werden buchstäblich ausgeführtgit rev-list mit verschiedenen Optionen ausgeführt und lesen die Ausgabe, um zu wissen, welche Commits und / oder anderen Git-Objekte verwendet werden sollen.
2 Da das Diagramm azyklisch ist , kann garantiert werden, dass noch keines besucht wurde, wenn wir die Einschränkung hinzufügen, dass niemals ein Elternteil angezeigt wird, bevor alle untergeordneten Elemente der Priorität angezeigt werden. --date-order, --author-date-orderUnd --topo-orderdiese Einschränkung hinzufügen. Die Standardsortierreihenfolge - die keinen Namen hat - funktioniert nicht. Wenn die Festschreibungszeitstempel fehlerhaft sind - wenn beispielsweise einige Festschreibungen "in der Zukunft" von einem Computer vorgenommen wurden, dessen Uhr ausgeschaltet war -, kann dies in einigen Fällen zu einer seltsam aussehenden Ausgabe führen.
Wenn Sie es bis hierher geschafft haben, wissen Sie jetzt viel darüber git log
Zusammenfassung:
git log Es geht darum, einige ausgewählte Commits anzuzeigen, während ein Teil des Diagramms ganz oder teilweise ausgeführt wird.
- Das
--no-mergesArgument, das sowohl in den akzeptierten als auch in den derzeit am besten bewerteten Antworten enthalten ist, unterdrückt das Anzeigen einiger Commits, die ausgeführt werden .
- Das
--first-parentArgument aus der derzeit am höchsten bewerteten Antwort unterdrückt das Gehen einiger Teile des Diagramms während des Diagrammspaziergangs selbst.
- Das
--notPräfix für Befehlszeilenargumente, wie es in der akzeptierten Antwort verwendet wird, verhindert , dass von Anfang an einige Teile des Diagramms überhaupt besucht werden .
Mit diesen Funktionen erhalten wir die gewünschten Antworten auf zwei verschiedene Fragen.
git log ^branch1 ^branch2 merge-only-branchSyntax verwenden?