Hier ist die kurze Antwort: Blick auf den Wert, der durch:
git rev-list --count --max-parents=1 $oldrefid..$newrefid
du Null sein soll. Lesen Sie weiter für die Erklärung (und Vorbehalte).
Ihre Schleife hat das Recht Umriss:
- alle ref Updates lesen;
- für diejenigen, die die Branche (n) (oder andere Referenzen) aktualisieren, die Ihnen wichtig sind, führen Sie einige Überprüfungen durch.
Der Trick liegt in der Kontrolle durchgeführt wird. Betrachten Sie die beiden anderen Informationen, die Sie erhalten, nämlich die alte und die neue SHA-1-ID, und dass in diesen Hooks eine (aber nicht beide) dieser beiden SHA-1-IDs alle 0
s sein können (was bedeutet, dass der Verweis ist) erstellt oder gelöscht).
Darauf zu bestehen, dass die Änderung nicht ein Anlegen oder Löschen sein, sollte Ihr Test sicherstellen, dass weder SHA-1 All-Nullen sind. (Wenn Sie davon ausgehen, dass nur die Löschung überprüft werden muss, können Sie einfach überprüfen, ob die neue SHA-1 nicht alles null ist. Aber wenn die Erstellung möglich ist - was nur der Fall ist, wenn der Zweig master
danach gelöscht wird B. wenn sich jemand am Server anmeldet, der Push-Nachrichten empfängt, und sie manuell löscht - Sie müssen immer noch sicherstellen, dass der alte SHA-1 für den letzten Test nicht Null ist. Diese Art des Löschens ist eindeutig möglich, die Frage ist, ob Sie Code schreiben möchten, um den Fall zu behandeln.)
In jedem Fall aktualisiert der typische Push einfach die Referenz. Beachten Sie, dass all neu hat Commits bereits in das Repository geschrieben worden (sie werden Garbage Collection, wenn Sie die Push ablehnen), so dass Ihre Aufgabe an diesem Punkt an:
- finden und überprüfen jede dass die Referenz verpflichtet verwendet, um zu benennen, dass es nicht mehr benennen wird (das sind Commits, die durch einen Non-Fast-Forward-Push entfernt werden); und
- finden und überprüfen Sie alle Commits, die der Verweis jetzt nennen wird, die er nicht verwendet hat (dies sind die neuen Commits, die durch einen Push hinzugefügt werden, ob es sich um einen schnellen Vorlauf handelt oder nicht) push könnte ein oder mehrere Commits entfernen, während gleichzeitig ein oder mehrere hinzugefügt werden).
Um diese beiden Sätze von Commits zu finden Sie git rev-list
verwenden sollen, denn das ist genau seine Aufgabe ist: eine Liste von SHA-1 s durch einen Ausdruck angegeben herzustellen. Die beiden Ausdrücke, die Sie hier haben wollen, sind "alle Commit-IDs, die von einer Revision gefunden werden können, die von einer anderen ID nicht bereits gefunden werden können". In git rev-list
Begriffe sind dies git rev-list $r1 ^$r2
, oder äquivalent git rev-list $r2..$r1
, für zwei Revisions-Spezifizierer $r1
und $r2
. Die beiden Revspecs sind natürlich nur die alten und neuen IDs für den vorgeschlagenen push
.
Die Reihenfolge dieser beiden IDs bestimmt, welche Gruppe von Commits git rev-list
Listen enthält: die Listen, die entfernt werden würden - dieser Satz ist für einen Schnellvorlauf leer - und die, die hinzugefügt werden.
In diesem insbesondere Fall, Ihr Ziel ist nicht, diese Listen zu erzeugen, der sich verpflichtet (auch wenn das funktionieren würde), sondern wählen etwas aus dieser Listen.
Möglicherweise möchten Sie das Löschen von COMMIT verhindern (d. H. Die Schnellvorlauf-Funktion erzwingen, selbst wenn der Benutzer push
ein Force-Flag festgelegt hat). In diesem Fall genügt es zu überprüfen, ob die Liste "Zu entfernende" leer ist. Sie können das tun, indem Sie sicherstellen, dass die Liste tatsächlich leer ist, oder - einfacher in einem Shell-Skript - mit git rev-list
zählen Sie sie für Sie und überprüfen Sie, dass die resultierende Zahl Null ist.
Sie möchten auf jeden Fall verhindern, dass Ergänzungen, die sind nicht verschmolzen, aber erlauben Ergänzungen, die sind. In diesem Fall teilt --max-parents=1
(das auch --no-merges
buchstabiert werden kann) git rev-list
mit, Commits zu unterdrücken, die zwei oder mehr Eltern haben, d. H. Zusammenführungen. Durch das Hinzufügen von --count
erhalten Sie eine Anzahl von Commits, die dieser Einschränkung "keine Zusammenführung, weil null oder ein Elternteil" entsprechen. Wenn diese Anzahl Null ist, müssen alle hinzugefügten Commits per Definition Merges sein.
Daraus folgt:
n=$(git rev-list --count --max-parents=1 $oldrefid..$newrefid)
if [ $n -gt 0 ]; then
echo "disallowed: push adds $n non-merge commit(s)" 1>&2
exit 1
fi
zum Beispiel genügt diese besondere Einschränkung zu erzwingen.
Fast, aber nicht ganz gleichwertig, können Sie git rev-list $r1 --not $r2
schreiben: der Unterschied ist, dass die Wirkung von --not
verweilt, so dass, wenn Sie noch ID eine weitere Revision hinzuzufügen waren $r3
die --not
-r3
gelten. Das heißt git rev-list A ^B C
bedeutet yes-A, not-B, yes-C
aber A --not B C
bedeutet yes-A, not-B, not-C
. Man beachte, dass in rev-list
Syntax B..A
bedeutet A ^B
, d. H. Genau B
ist invertiert.
"Fast-Forward-Merges (die nur schwer von einer Reihe normaler Commits zu unterscheiden sind)" AFAIK gibt es keine Möglichkeit, eine Reihe von Commits von einer Fast-Forward-Merge zu unterscheiden. Es ist einfach eine Aktualisierung der Verzweigung, die wir in das fragliche Commit einbinden. –
@Zeeker Sie können bei einer Schnellvorlauf-Zusammenführung etwas raten, indem Sie prüfen, ob die gleichen Festlegungen in einem anderen Zweig existieren. Unter Umständen kann dies ein ausreichender Beweis sein. Es ist jedoch bei weitem nicht fehlerfrei. –
Ich sehe aber das wäre ziemlich weit hergeholt. Zumindest lokal könnte der Reflog einen klüger machen, aber das schließt offensichtlich jede Fernbedienung aus, auf die wir drücken. –