Git Rev-Liste verstehen

Oct 16 2020

Auf der Suche nach Beispielen für Git-Hooks bin ich auf folgenden Beitrag gestoßen: https://github.com/Movidone/git-hooks/blob/master/pre-receive und ich wollte den folgenden Befehl verstehen:

git rev-list $new_list --not --all 

wo new_list erhalten wird von:

NULL_SHA1="0000000000000000000000000000000000000000" # 40 0's
new_list=
any_deleted=false
while read oldsha newsha refname; do
    case $oldsha,$newsha in *,$NULL_SHA1) # it's a delete
            any_deleted=true;;
        $NULL_SHA1,*) # it's a create new_list="$new_list $newsha";; *,*) # it's an update new_list="$new_list $newsha";;
    esac
done

Ich dachte mir, dass die Rev-Liste Commits in umgekehrter chronologischer Reihenfolge anzeigt.

Aber kann jemand mehr Einblicke in das geben, wofür -notund welche -allOptionen vorgesehen sind?

Gemäß Dokumentation:

--not
Reverses the meaning of the ^ prefix (or lack thereof) for all following revision specifiers, up to the next --not.
--all
Pretend as if all the refs in refs/ are listed on the command line as <commit>. 

Ich kann diese Optionen nicht vollständig verstehen.

[Update] Nach ein paar Test Festschreibungen, dachte, wenn ich nicht verwenden --notund --allOptionen dann, git rev-listlistet alle Commits auf dem Zweig und nicht den eigenen , während ich pushen wollen.

Wollten Sie jedoch verstehen, warum die sha-Werte nicht auf dem Terminal gedruckt werden, wenn die --allOption übergeben wird?

Antworten

4 torek Oct 17 2020 at 13:18

Der git rev-listBefehl ist ein sehr komplizierter, sehr zentraler Befehl in Git, da er das Diagramm durchläuft . Das Wort Diagramm bezieht sich hier sowohl auf das Festschreibungsdiagramm selbst als auch in einigen Fällen auf die nächsthöhere Ebene (Git-Objekte, die über Festschreibungen erreichbar sind ).

Ich dachte mir, dass die Rev-Liste Commits in umgekehrter chronologischer Reihenfolge anzeigt.

Nicht genau, aber nah:

  • Die Reihenfolge ist änderbar. Die Standardeinstellung ist umgekehrt chronologisch.
  • Standardmäßig werden einige Commits ausgeführt. Sie können jedoch rev-listauch tiefer gehen, um Baum- und Blob-Objekte und sogar Tag-Objekte einzuschließen. Dies gilt für Programme wie git fetchund git push(die aufrufen git pack-objects) und git pack-objects. Ich habe vor, diese Möglichkeit hier völlig zu ignorieren, aber ich denke, ich sollte es zumindest erwähnen. 😀

Daher werden einige Commits standardmäßig in umgekehrter chronologischer Reihenfolge aufgelistet. Es ist sowohl wichtig als auch ein wenig schwierig, genau anzugeben, welche Teile des Diagramms ausgeführt werden sollen git rev-list: einige in einigen Commits .

Aber kann jemand mehr Einblicke in das geben, wofür --notund welche --allOptionen vorgesehen sind?

Wie VonC Notizen , die Wirkung ist hier , um die Liste Commits, die den empfangenden Verwahrungs neu sind. Dies hängt davon ab, dass dieser git rev-listBefehl in einem Pre-Receive-Hook ausgeführt wird . Außerhalb dieses speziellen Hakens macht es im Allgemeinen nichts Nützliches. Wie Sie sehen können, ist die Laufzeitumgebung eines Hooks in Git oft zumindest ein bisschen speziell. (Dies gilt nicht nur für den Pre-Receive-Hook: Man muss über den Aktivierungskontext jedes Hooks nachdenken.)

Mehr über --not --all

Die --allOption macht genau das, was Sie aus der Dokumentation zitiert haben:

Stellen Sie sich vor, als wären alle Refs in refs/der Befehlszeile aufgelistet ...

Dies entspricht also a git for-each-ref refs: Es durchläuft jede Referenz. Dazu gehört Zweignamen ( masteroder main, develop, feature/tall, und so weiter, alle sind wirklich in refs/heads/), Tag - Namen ( v1.2was wirklich ist refs/tags/v1.2), Fernverfolgung Namen ( origin/developwas wirklich ist refs/remotes/origin/develop), Ersatz Refs (in refs/replace/), der Stash ( refs/stash), Halbierungsreferenzen, Gerrit-Referenzen, wenn Sie Gerrit verwenden, und so weiter. Beachten Sie, dass keine Reflog-Einträge durchlaufen werden.

Das --notPräfix ist eine einfache boolesche Operation. In der Gitrevisions-Syntax - siehe die Dokumentation zu Gitrevisions - können wir Dinge wie schreiben develop, dh ich sage Ihnen, Sie sollen von developvorne beginnen und rückwärts arbeiten und diese Commits einschließen , aber auch Dinge wie ^develop, das heißt, ich sage Ihnen, Sie sollen von developdiesen Commits ausgehen und rückwärts arbeiten und sie ausschließen . Also, wenn ich schreibe:

git rev-list feature1 feature2 ^main

Ich frage Git Commits zu Fuß von erreichbar Commits durch den Namen identifiziert feature1und feature2, aber ausschließen Festschreibungen von den Festschreibungen von identifiziert Unerreichbar main. Weitere Informationen zur allgemeinen Vorstellung von Erreichbarkeit und Graph-Walking finden Sie unter Think Like (a) Git .

Der --notBediener dreht effektiv die ^auf jeder Referenz:

git rev-list --not feature1 feature2 ^main

ist sozusagen eine Abkürzung für:

git rev-list ^feature1 ^feature2 main

Dies führt die Liste der Commits durch main, die von erreichbar sind, schließt jedoch diejenigen aus, die entweder von feature1oder erreichbar sind feature2.

Normalerweise sind alle Commits mit auffindbar--all

Wenn Sie Git im normalen Alltag verwenden und im Moment keinen "getrennten HEAD" haben - der getrennte HEAD-Modus ist nicht gerade abnormal, aber nicht die übliche Arbeitsweise - --all, können Sie git rev-listfestlegen, dass alle Commits eingeschlossen werden sollen , weil alle Commits von allen Referenzen aus erreichbar sind . 1 So --not --alleffektiv schließt alle Commits. Wenn Sie also --not --allzu einem Commit hinzufügen git rev-list, das ansonsten einige Commits auflistet, wird die Liste gesperrt. Die Ausgabe ist leer: Warum haben wir uns die Mühe gemacht?

Wenn Sie in frei stehende HEAD - Modus sind und mehrere neue gemacht haben Commits-dies kann passieren , wenn Sie in der Mitte eines interaktiven oder im Widerspruch Fütterungsmaterial, für sind instanz dann git rev-list HEAD --not --allwürden jene Commits aufzulisten , die sind aus erreichbar , HEADaber nicht von jedem Zweignamen. In dieser Rebase wären dies beispielsweise nur die Commits, die Sie bisher kopiert haben.

Der "losgelöste HEAD" -Modus wäre also einmal ein Ort, an dem er git rev-list --not --allüber die Befehlszeile nützlich sein könnte. Aber für die Situation, die Sie untersuchen - ein Pre-Receive-Hook - sind wir nicht wirklich in der Befehlszeile.

Haken vorab empfangen

Wenn jemand git pushCommits an Ihr eigenes Git sendet , wird Ihr Git:

  • richtet einen Quarantänebereich für neue Objekte ein (neue Commits und Blobs usw.); 1
  • verhandelt mit dem Absender, um zu entscheiden, was der Absender senden soll;
  • empfängt diese Objekte; und
  • Nimmt eine Liste der Ref-Update-Anforderungen . Diese Aktualisierungsanforderungen sagen im Wesentlichen nur, dass dieser Name diese Hash-ID enthält . 2

Bevor tatsächlich tut jeder der angeforderten Updates, Git:

  1. Führt die gesamte Liste an den Pre-Receive-Hook weiter. Dieser Haken kann "nein" sagen; In diesem Fall wird der gesamte Push als Ganzes abgelehnt.
  2. Wenn das "ok" lautet, wird die Liste nacheinander an den Update-Hook weitergeleitet. Wenn dieser Hook "ok" sagt, wird das Update durchgeführt. Wenn der Hook "Nein" sagt, lehnt Ihr Git das eine Update ab, untersucht aber andere.
  3. Nachdem alle Aktualisierungen in Schritt 2 akzeptiert oder abgelehnt wurden, wird die akzeptierte Liste dem Post-Receive-Hook zugeführt.

Erforderliche Objekte, die in Schritt 2 zu einer Referenz hinzugefügt wurden, werden aus der Quarantäne in die Objektdatenbank von Git verschoben. Diejenigen, die abgelehnt wurden, sind nicht.

Denken Sie jetzt an ein typisches git push. Wir erhalten einige neue Commits und eine Anfrage: Erstellen Sie einen neuen Zweigstellennamenfeature/short , oder wir erhalten einige neue Commits und eine Anfrage: Aktualisieren Sie den vorhandenen Filialnamen develop, um diese neuen Commits zusammen mit den alten einzuschließen .

In Schritt 1 oben haben wir eine einzelne neue Hash-ID. Wir liefen eine Schleife alle ref Namen zu lesen, und ihre aktuellen und vorgeschlagenen neuen Hash - IDs und die Schleife lief nur einmal, da nur ein Name wurde git push-ed. Diese Hash-ID bezieht sich auf das neue Commit oder die neuen Commits, die entweder zu diesem vorhandenen Zweig hinzugefügt werden, oder auf den Tipp und andere Commits, die ausschließlich für den neuen Zweig gelten.

Wir möchten jetzt diese Commits überprüfen und nicht alle vorhandenen Commits, die von einer vorhandenen Niederlassung aus erreichbar sind. $new_listNehmen wir zur Vereinfachung an, anstatt in meiner anderen Antwort, nehmen wir nur die eine neue Hash-ID $newund die alte Hash-ID für den Zweignamen an $old: Alle Nullen, wenn der Zweig komplett neu ist, oder ein gültiges vorhandenes Commit, wenn dies der Fall ist ein vorhandener Filialname.

Wenn sich die neuen Commits in einem völlig neuen Zweig befinden, dann:

git rev-list $new ^master ^develop ^feature/short ^feature/tall

würde sie zum Beispiel abdecken, wenn wir wüssten, dass die einzigen vorhandenen Zweige diese vier sind (und dass es keine Tags usw. gibt, über die wir uns Sorgen machen müssen). Aber was ist, wenn sie beispielsweise hinzugefügt werden develop? Dann möchten wir die Commits auszuschließen, die zur Zeit auf develop. Wir könnten die $oldHash-ID verwenden, um das zu tun:

git rev-list $new ^master ^$old ^feature/short ^feature/tall

Das würde wieder nur die neuen Commits auflisten, die jeder, der läuft git push origin develop, zu unseren hinzufügen möchte develop.

Aber denk darüber nach $old. Dies ist eine Hash-ID. Woher hat Git es? Git hat diese Hash-ID vom Namen erhalten develop . Dies ist ein Pre-Receive-Hook . Der Name develop wurde noch nicht aktualisiert . Der Name develop ist also ein Name für die alte Hash-ID $old. Das bedeutet:

git rev-list $new ^master ^develop ^feature/short ^feature/tall

wird auch den Job machen.

Wenn git rev-list $newgefolgt von "und nicht allen vorhandenen" die Aufgabe erledigt, dann:

git rev-list $new --not --branches

wird den Job machen. Das ist fast das, was wir hier haben.

Der Fehler bei der Verwendung --branchesist, dass es keine Tags oder andere Refs bekommt. Wir könnten verwenden --not --branches --tags, --not --allist aber kürzer und bekommt auch alle anderen Refs.

Hier --not --allkommt also her: Es kommt auf den Sonderfall eines Pre-Receive-Hooks an. Wir listen die neuen Hash-IDs auf, wie von jedem, der a ausführt, vorgeschlagen git push, den unser Git als Liste von Zeilen an uns übergeben hat. Wir haben git rev-listdas vorgeschlagene zu aktualisierende Festschreibungsdiagramm durchlaufen und uns die neuen Festschreibungen im Quarantänebereich angesehen, jedoch alle Festschreibungen ausgeschlossen, die sich bereits in unserem Repository befinden. Der Befehl rev-list erzeugt diese Hash-IDs, eine pro Zeile, die wir dann in einer Shell-Schleife lesen und alles tun, um jedes Commit zu überprüfen .


1 Der Quarantänebereich war neu in Git 2.11. Zuvor konnten neue Objekte für eine Weile im Repository verbleiben, selbst wenn der Push abgelehnt wird. Der Quarantänebereich ist für die meisten Menschen keine große Sache, aber für große Server wie GitHub kann er viel Speicherplatz sparen .

2 Die Anforderung kann erzwungen oder nicht erzwungen werden, und wenn sie erzwungen wird, kann es sich um eine erzwungene Leasinganforderung handeln oder nicht. Diese Informationen sind weder im Pre-Receive-Hook (noch im Update-Hook) verfügbar, was, sagen wir mal, nicht so toll ist , aber es gibt Kompatibilitätsprobleme beim Hinzufügen. Meistens ist alles lebenswert. Der Hook kann erkennen, ob es sich um eine neue Referenz zum Erstellen oder Löschen einer vorhandenen Referenzanforderung handelt, da in diesem Fall eine der beiden Hash-IDs - alt oder neu - der Null-Hash "Null" ist (reserviert; keine Hash-ID zulässig All-Nullen sein).

5 VonC Oct 17 2020 at 01:59

Es bedeutet:

  • Listen Sie Commits auf, die erreichbar sind, indem Sie den übergeordneten Links der angegebenen Commits folgen, hier $new_listdie neuen, geänderten oder gelöschten Commits
  • aber schließen Sie Commits aus, die erreichbar sind, von denen, die mit einem ^vor ihnen angegeben sind, hier " all ", dh alle HEADS-Commits oder getaggten Commits.

Dies beschränkt die Rev-Liste nur auf die neu empfangenen Commits und nicht auf alle Commits (empfangen und bereits im empfangenden Repository vorhanden).