Zeigen Sie mit Git alle Commits an, die * nur * in einem bestimmten Zweig und nicht * in * anderen vorhanden sind

84

Bei einem Zweig möchte ich eine Liste der Commits sehen, die nur in diesem Zweig vorhanden sind. In dieser Frage diskutieren wir Möglichkeiten, um festzustellen, welche Commits sich in einem Zweig befinden, jedoch nicht in einem oder mehreren bestimmten anderen Zweigen.

Das ist etwas anders. Ich würde gerne sehen, welche Commits sich in einem Zweig befinden, aber nicht in einem anderen Zweig.

Der Anwendungsfall liegt in einer Verzweigungsstrategie, bei der einige Zweige nur zusammengeführt und niemals direkt festgeschrieben werden sollten. Dies würde verwendet, um zu überprüfen, ob Commits direkt in einem "Nur-Zusammenführen" -Zweig vorgenommen wurden.

BEARBEITEN: Im Folgenden finden Sie Schritte zum Einrichten eines Dummy-Git-Repos zum Testen:

git init
echo foo1 >> foo.txt
git add foo.txt
git commit -am "initial valid commit"
git checkout -b merge-only
echo bar >> bar.txt
git add bar.txt
git commit -am "bad commit directly on merge-only"
git checkout master
echo foo2 >> foo.txt 
git commit -am "2nd valid commit on master"
git checkout merge-only 
git merge master

Es sollte nur das Commit mit der Meldung "Bad Commit direkt bei Nur-Zusammenführung" angezeigt werden, das direkt im Nur-Zusammenführungs-Zweig ausgeführt wurde.

Jimmyorr
quelle
1
Diese Frage setzt voraus, dass alle zusammengeführten Zweige derzeit im Repo verfügbar sind, niemals gelöscht werden, wenn sie vollständig zusammengeführt wurden, und möglicherweise nie mit einem Schnellvorlauf zusammengeführt werden. Lassen Sie mich wissen, wenn mir etwas fehlt, aber es scheint mir, dass dies nur für einen relativ kleinen Satz zulässiger Zusammenführungszweige funktioniert. Warum also nicht einfach die git log ^branch1 ^branch2 merge-only-branchSyntax verwenden?
Karl Bielefeldt
1
Das git log ^branch1 ^branch2 merge-only-brancherfordert die Auflistung jedes einzelnen Zweigs. Das kann mit einer cleveren Verwendung von Bash / Grep vermieden werden (siehe meine Antwort unten), aber ich hoffe, dass Git eine eingebaute Unterstützung dafür hat. Sie haben Recht, dass davon ausgegangen wird, dass alle Merge-From-Zweige entfernt sind (nur lokal sind für andere Entwickler so gut wie nicht vorhanden). Bei der Verwendung werden --no-mergesalle Commits weggelassen, die zusammengeführt wurden und deren ursprünglicher Merge-From-Zweig gelöscht wurde. Dies setzt also voraus, dass die Merge-From-Zweige so lange beibehalten werden, bis sie zu einem Nicht-Merge-Only-Zweig (dh Master) zusammengeführt wurden.
Jimmyorr

Antworten:

73

Wir haben gerade diese elegante Lösung gefunden

git log --first-parent --no-merges

In Ihrem Beispiel wird das anfängliche Commit natürlich immer noch angezeigt.

Diese Antwort beantwortet die Frage nicht genau, da das anfängliche Commit immer noch angezeigt wird. Andererseits scheinen viele Leute, die hierher kommen, die Antwort zu finden, nach der sie suchen.

wrtsprt
quelle
1
Einfach, effektiv, am besten.
Zabba
1
Da das anfängliche Commit für den Master weiterhin angezeigt wird, wird die Frage nicht beantwortet.
Jimmyorr
6
Dies allein genügt nicht „verpflichtet , die nur existieren auf diesem Zweig“ zustands es die zeigt initial valid commit, welcher Teil sowohl der ist merge-onlyund masterZweige. Wenn man sich jedoch die Mühe macht, den aktuellen Zweignamen am Ende gefolgt von den mit ^ vorangestellten Zweignamen einzugeben, von denen man weiß, dass der aktuelle Zweig stammt, löst dies die Hälfte der Probleme (ausgenommen Dinge, die zusammengeführt wurden). Beispiel:git log --first-parent --no-merges merge-only ^master
Slipp D. Thompson
13
Ich bin mir nicht sicher, warum dies so positiv bewertet wird, es scheint für die Frage überhaupt nicht relevant zu sein. Es liefert sicherlich nicht die Informationen, nach denen das Poster gesucht hat.
Chris Rasys
2
Diese Antwort ist möglicherweise nicht perfekt. Aber es ist einfach und funktioniert sicherlich bis zu einem gewissen Grad. Ich finde es nützlich, den git log --first-parent --no-merges | grep <branch_name>
Filialnamen
29

Mit freundlicher Genehmigung meines lieben Freundes Redmumba :

git log --no-merges origin/merge-only \
    --not $(git for-each-ref --format="%(refname)" refs/remotes/origin |
    grep -Fv refs/remotes/origin/merge-only)

... wo origin/merge-onlyist Ihr Filialname für die Remote-Zusammenführung? Bei Arbeiten an einem nur lokalen git Repo, Ersatz refs/remotes/originmit refs/headsund Ersatz Fernzweignamen origin/merge-onlymit lokalen Zweignamen merge-only, das heißt:

git log --no-merges merge-only \
    --not $(git for-each-ref --format="%(refname)" refs/heads |
    grep -Fv refs/heads/merge-only)
Jimmyorr
quelle
2
Ich hoffe, dass jemand anderes eine Lösung ohne Grep nur mit Git anbieten kann, aber wenn nicht, fühlt sich das ziemlich elegant an.
Jimmyorr
1
Ja, elegant. Verwenden Sie git for-each-refdiese Option, um jeden Referenznamen im Ursprung grep -vaufzulisten und den Nur-Zusammenführungs-Zweig wegzulassen. git lognimmt eine --notOption, an die wir unsere Liste aller Verweise übergeben (mit Ausnahme des Nur-Zusammenführungs-Zweigs). Wenn Sie eine elegantere Antwort auf das Problem haben, lassen Sie es uns hören.
Jimmyorr
2
Oh, ich bin sicher, es ist die eleganteste Antwort. Ich behaupte nur, es ist ein bisschen "wortreich / komplex" für wahre Eleganz. :-) Wollte Ihren Ansatz nicht herabsetzen, Sir!
Chris K
1
Die nacheilenden /*in den git for-each-refsBefehlen verlassen sich auf nicht einige vorhandene Datei passende und nicht mit failgloboder nullglobSatz ( bash - Optionen, andere Schalen variieren). Sie sollten entweder die Sternchen zitieren / entkommen oder einfach das Nachlassen /*weglassen ( git for-each-refMuster können „von Anfang an bis zu einem Schrägstrich“ übereinstimmen). Verwenden Sie grep -Fv refs/remotes/origin/foo( refs/heads/foo) möglicherweise , um genauer zu bestimmen, welche Refs entfernt werden.
Chris Johnsen
3
Es kann vereinfacht werden, wenn Sie nur Commits in einem Zweig und nicht in einem anderen sehen möchten: git log --no-merges B1 --not B2wobei B1 der Zweig ist, an dem Sie interessiert sind, und B2 der Zweig ist, mit dem Sie B1 vergleichen möchten. Sowohl B1 als auch B2 können lokale oder entfernte Zweige sein, daher können Sie git log --no-merges master --not origin/masterzwei entfernte Zweige angeben oder sogar angeben.
mr.b
20
git log origin/dev..HEAD

Dies zeigt Ihnen alle in Ihrer Niederlassung vorgenommenen Commits.

Prakash
quelle
2
@Prakash origin/branchNamezeigt auf den Kopf des Remote-Zweigs UND HEADauf das Commitid des letzten lokalen Commits in diesem Zweig. Dies funktioniert also nicht, wenn Sie Git Push verwendet haben.
Bharat
Sie können dies verwenden, um einen anderen lokalen Zweig zu vergleichen. Das Flag --no-merges kann auch nützlich sein, um die ursprüngliche Frage des OP zu beantworten.
Paul Whipp
15

@Prakash Antwort funktioniert. Nur zur Klarheit ...

git checkout feature-branch
git log master..HEAD

listet die Commits für den Feature-Zweig auf, nicht jedoch für den Upstream-Zweig (normalerweise Ihr Master).

Bryan
quelle
7

Versuche dies:

git rev-list --all --not $(git rev-list --all ^branch)

Grundsätzlich werden git rev-list --all ^branchalle Revisionen nicht im Zweig und dann alle Revisionen im Repo und subtrahieren die vorherige Liste, die die Revisionen nur im Zweig sind.

Nach @ Brians Kommentaren:

Aus der Dokumentation der Git Rev-Liste:

List commits that are reachable by following the parent links from the given commit(s)

Ein Befehl wie git rev-list AA ist also ein Commit, der Commits auflistet, die von A einschließlich A erreichbar sind.

In diesem Sinne so etwas wie

git rev-list --all ^A

listet Commits auf, die von A nicht erreichbar sind

So git rev-list --all ^branchwerden alle Commits aufgelistet, die von der Spitze des Zweigs nicht erreichbar sind. Dadurch werden alle Commits in der Verzweigung entfernt, oder mit anderen Worten, Festschreibungen, die sich nur in anderen Verzweigungen befinden.

Nun kommen wir zu git rev-list --all --not $(git rev-list --all ^branch)

Das wird so sein git rev-list --all --not {commits only in other branches}

Also wollen wir auflisten all also auflisten, von wo aus nicht erreichbar sindall commits only in other branches

Welches ist die Menge der Commits, die nur in der Verzweigung sind. Nehmen wir ein einfaches Beispiel:

             master

             |

A------------B

  \

   \

    C--------D--------E

                      |

                      branch

Hier ist das Ziel, D und E zu bekommen, die Commits in keinem anderen Zweig.

git rev-list --all ^branch gib nur B.

Darauf git rev-list --all --not Bkommen wir jetzt an. Was auch istgit rev-list -all ^B - wir wollen, dass alle Commits nicht von B aus erreichbar sind. In unserem Fall sind es D und E. Was wollen wir?

Hoffe das erklärt, wie der Befehl richtig funktioniert.

Nach Kommentar bearbeiten:

git init
echo foo1 >> foo.txt
git add foo.txt
git commit -am "initial valid commit"
git checkout -b merge-only
echo bar >> bar.txt
git add bar.txt
git commit -am "bad commit directly on merge-only"
git checkout master
echo foo2 >> foo.txt 
git commit -am "2nd valid commit on master"

Wenn Sie nach den oben genannten Schritten ein git rev-list --all --not $(git rev-list --all ^merge-only)Commit ausführen, erhalten Sie das gesuchte Commit - das Commit "bad commit directly on merge-only".

Sobald Sie jedoch den letzten Schritt in Ihren Schritten ausgeführt haben, gibt git merge masterder Befehl nicht die erwartete Ausgabe aus. Denn ab sofort gibt es kein Commit, das nicht nur beim Zusammenführen vorhanden ist, da das eine zusätzliche Commit im Master ebenfalls zum Nur-Zusammenführen zusammengeführt wurde. So git rev-list --all ^branchgibt leeres Ergebnis und damit git rev-list -all --not $(git rev-list --all ^branch)gibt alle Commits in Merge-only.

Manojlds
quelle
1
Hm ... ich weiß nicht warum, aber das funktioniert nicht ganz. Wenn Sie die Ausgabe Ihres Befehls so weiterleiten, xargs -L 1 -t git branch -a --containsdass viele Fehlalarme angezeigt werden (Commits, die sich tatsächlich in anderen Zweigen befinden). Ich habe es mit und ohne versucht --no-merges. Vielen Dank für Ihre Antwort!
Jimmyorr
Scheint gut für mich zu funktionieren, soweit ich in einem Dummy-Git-Repo sehen kann.
Manojlds
Ich habe Schritte hinzugefügt, um ein Dummy-Git-Repo zu erstellen, um das Problem mit Ihrer Antwort zu demonstrieren.
Jimmyorr
Ah, hoppla. Ich habe das positiv bewertet, bevor ich es mir durchdacht hatte. git rev-list --all ^branchgibt Ihnen alle Commits, die nicht in sind branch. Sie subtrahieren das dann von der Liste, die sich in der Liste befindetbranch . Aber per Definition sind alle Commits, die nicht in branchsind, nicht in branch, also subtrahieren Sie nichts. Was Jimmyorr sucht, sind Commits, die sich in einem anderen Zweig befinden, branchaber nicht master. Sie möchten die Commits, die nicht enthalten sind, nicht subtrahieren branch. Sie möchten die Commits subtrahieren, die sich in anderen Zweigen befinden.
Brian Campbell
1
@manojlds "(alle Revisionen) - (alle Revisionen nicht im Zweig) = Revision im Zweig." Ja, das funktioniert, um alle Revisionen zu erhalten branch, aber auch git rev-list branch. Sie schreiben nur git rev-list branchkomplizierter (und langsamer). Es funktioniert nicht, die Frage zu beantworten, wie man alle Commits findet branch , die sich in keinem anderen Zweig befinden .
Brian Campbell
2

Eine weitere Variation der akzeptierten Antworten, mit denen verwendet werden kann master

git log origin/master --not $(git branch -a | grep -Fv master)

Filtern Sie alle Commits, die in einem anderen Zweig als Master ausgeführt werden.

JaimeJorge
quelle
0

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.

torek
quelle