Ich versuche, einen serverseitigen Pre-Receive-Git-Hook zu schreiben, um Commits zu bewerten, wenn sie gepusht werden.
Nach den Antworten hier , dies sollte leicht möglich sein, indem ich git log suchen und herausfiltern, was ich mit 'format:' will.
Ich habe das folgende vorbereitende Skript erstellt.
%Vor% Ich finde, wenn ich git show
oder git log
auf dem Server starte, sind die Ergebnisse für den aktuellen HEAD, während ich das eingehende Commit abfragen möchte .
Wie kann ich dieses Skript ändern, um git log
oder git show
für den noch nicht erhaltenen Commit auszuführen?
Sie müssen die SHA-1-IDs verwenden, die an der Standardeingabe bereitgestellt werden:
%Vor%Der "Testcode" muss dann mindestens einige und vielleicht alle drei Elemente anzeigen, abhängig von den auszuführenden Tests.
Der Wert in $oldsha
ist 40 0
s, wenn vorgeschlagen wird, dass der Referenzname $refname
erstellt wird. Das heißt, $refname
(normalerweise so etwas wie refs/heads/master
oder refs/tags/v1.2
, aber jeder Name in refs/
kann erscheinen: refs/notes/commits
zum Beispiel) existiert jetzt nicht im empfangenden Repository, wird aber existieren und zeigen zu $newsha
, wenn Sie die Änderung zulassen.
Der Wert in $newsha
ist 40 0
s, wenn vorgeschlagen wird, den Referenznamen $refname
zu löschen. Das heißt, $refname
existiert jetzt und zeigt auf das Objekt $oldsha
; Wenn Sie die Änderung zulassen, wird dieser Referenzname gelöscht.
Die Werte beider Werte sind ungleich Null, wenn der Referenzname $refname
zur Aktualisierung vorgeschlagen wird, dh er zeigt momentan auf das Objekt $oldsha
. Wenn Sie die Änderung zulassen, wird auf das neue Objekt% verwiesen. stattdessen co_de%.
Wenn Sie einfach $newsha
oder git log
ausführen, verwendet git den gefundenen SHA-1, indem Sie git show
ausführen. In einem typischen Empfangs-Repository ist git rev-parse HEAD
eine symbolische Referenz, die auf HEAD
verweist (die Datei refs/heads/master
enthält buchstäblich die Zeichenkette HEAD
), sodass Sie das oberste Commit auf dem Zweig ref: refs/heads/master
(wie Sie) sehen beobachtet).
Sie müssen sich genau anschauen, welche neuen Objekte hereinkommen. Woher wissen Sie, welche neuen Objekte hereinkommen? Das hängt davon ab, was mit den angegebenen master
und möglicherweise anderen ref-Namen passiert.
Wenn der refname gelöscht werden soll, kommt nichts neues hinzu. Ob irgendwelche zugrundeliegenden git Objekte gelöscht werden (Garbage collected) hängt davon ab, ob dieser Refname der "letzte" Verweis auf diese Objekte ist . Nehmen Sie zum Beispiel an, dass die gesamte Standard-Eingabesequenz aus zwei Direktiven besteht:
$refname
refs/heads/foo
Nehmen wir weiter an, dass refs/tags/v1.1
(Zweig refs/heads/foo
) auf commit foo
in diesem Grafikdiagramm verweist und% F
auf das mit Anmerkungen versehene Tag v1.1
:
Das Löschen der Verzweigung G
ist "sicher", da keine Commits weggehen, weil das mit Anmerkungen versehene Tag foo
sie über das G
-Tag behalten wird.
Löschen des Tags v1.1
ist "sicher" (ish), da keine Commits weggehen, weil die Verzweigung v1.1
sie über den Verweis foo
behält. (Das annotierte Tag-Objekt selbst wird verschwinden. Es liegt an Ihnen, ob Sie dies zulassen)
Das Löschen von beide ist jedoch nicht sicher: commits refs/heads/foo
und E
werden nicht erreichbar und werden gesammelt. (Es liegt an Ihnen, ob Sie das trotzdem zulassen.)
Andererseits ist es möglich, dass stdin neben diesen beiden Direktiven eine dritte Direktive enthält:
F
zeigt auf commit refs/heads/foo2
, mit commit H
zeigt auf commit H
als übergeordnetem [ Bearbeiten: Beim nochmaligen Lesen bemerke ich die grelle Annahme, dass G
ist ein Commit-Objekt und kein Tag-Objekt. Wenn wir annehmen, dass G
ein Commit-Objekt ist, ist der Rest des Folgenden korrekt, aber das obige wird zumindest ein bisschen falsch. Die allgemeine Idee, dass die DAG durch externe Referenzen geschützt wird, ist jedoch immer noch richtig, und das sollte meist sinnvoll sein. ] In diesem Fall ist die Löschung von G
sicher, da der neue Zweig foo
weiterhin commit foo2
beibehält, wodurch commit H
beibehalten wird.
Eine vollständige Analyse ist schwierig; es ist oft besser, nur eine stückweise Analyse durchzuführen, die "sichere" Operationen erlaubt (was auch immer Sie sich entscheiden) und Benutzer dazu zwingen, die Aktualisierungen stückweise auf "sichere" Weise zu pushen (Zweig G
zuerst erstellen und dann Zweig% löschen co_de% zum Beispiel als separaten Push).
Wenn Sie nur neue Commits anzeigen möchten, dann für jedes Referenzupdate:
In den meisten "normalen" Pre-Receive-Hooks würden Sie die unten beschriebenen Methoden verwenden, aber wir haben eine Alternative für diese spezielle Aufgabe.
Es gibt eine Abkürzung für Änderungen, die die häufigsten und meist interessantesten Fälle behandelt. Angenommen jemand schlägt vor, foo2
von foo
auf refs/heads/foo
zu aktualisieren. Es ist möglich, dass einige Objekte in dem Bereich bereits vorhanden waren, z. B. 1234567...
ist die ID von commit 9876543...
und 1234567
ist die ID von commit C
:
In diesem Fall werden die Objekte D und E untersucht. Dies gilt auch, wenn die Commits 9876543
und E
gerade hochgeladen wurden, aber no Referenzen haben, dh das vorgeschlagene Update ist um D
und E
hinzuzufügen und das Diagramm sieht jetzt so aus:
In jedem Fall ein einfaches:
%Vor%erzeugt die Objekt-IDs, die Sie betrachten sollten.
Für neue Referenzen gibt es keine Abkürzung. Angenommen, wir haben die gleichen fünf Commits, die oben gezeigt wurden, mit demselben D
, aber keinem E
, und der tatsächliche Vorschlag lautet "create refs/heads/foo
zeigt auf refs/heads/bar
". In diesem Fall sollten wir uns erneut die Commits refs/heads/bar
und E
ansehen, aber es gibt keine offensichtliche Möglichkeit, über D
zu erfahren.
Der nicht naheliegende Weg, der nur in einigen Fällen funktioniert, ist das Finden von Objekten, die bei der vorgeschlagenen Erstellung erreichbar sind, die derzeit überhaupt nicht erreichbar sind:
%Vor% In diesem speziellen Fall werden die IDs für E
und D
erneut erzeugt.
Sehen wir uns jetzt Ihren speziellen Fall an, in dem Sie sich alle Commits anschauen möchten, die vorgeschlagen werden. Hier ist eine Möglichkeit, dies zu bewältigen.
Für alle vorgeschlagenen Updates:
Wenn wir einige Löschungen haben und haben wir einige SHAs angesammelt, lehnen den Versuch ab: es ist zu schwer. Lassen Sie den Benutzer die Vorgänge trennen.
Andernfalls, wenn wir keine angesammelten SHAs haben, müssen wir nur löschen (oder vielleicht gar nichts - sollte nicht passieren, aber harmlos); erlaube dies (exit 0).
Ansonsten müssen wir einige neue SHA-1-Werte haben.
Unter Verwendung der vorgeschlagenen neuen SHAs als Startpunkte finden Sie alle git-Objekte, die erreichbar wären, ausgenommen alle Objekte, die derzeit unter einem beliebigen Namen erreichbar sind. Dies sind alle neuen Objekte.
Untersuchen Sie für jeden, der ein Commit ist, ob es verboten ist. Wenn dies der Fall ist, lehnen Sie die gesamte Operation ab (auch wenn einige Teile erfolgreich sein könnten); Wie zuvor ist es zu schwer, dies herauszufinden, also lassen Sie den Benutzer die "guten" Operationen von den "schlechten" unterscheiden.
Wenn wir so weit kommen, ist alles in Ordnung; Erlaube das gesamte Update.
In Codeform:
%Vor%Tags und Links git bash pre-commit-hook githooks