2015-03-16 4 views
7

Ich habe ein Problem, einen pre-receive Haken auf einem git remote Zweig zu schaffen, zu tun, was ich will.Git: pre-receive Haken nur verschmilzt zu ermöglichen, und nicht verpflichtet in Master direkt

Was ist das Problem?

Direkt zur Hauptzweig verpflichtet nicht erlaubt sein sollte. Nur Zusammenführungen in den Masterzweig sollten erlaubt sein.

Lösung

bisher Meine Lösung ist zu prüfen, ob es sich von einem Benutzer im Push sind, wo der Meister betroffen ist. Aber das Problem ist, dass ich nicht unterscheiden kann, ob die Änderung eine direkte Festschreibung oder eine Zusammenführung ist.

#!/bin/sh 
while read oldrefid newrefid refname 
do 
if [ "$refname" = "refs/heads/master" ]; then 
     echo $(git merge-base $oldrefid $newrefid) 
     echo "---- Direct commit to master branch is not allowed ----" 
     echo "Changes only with a merge from another branch" 
     exit 1 
fi 
done 

Hat jemand eine Idee, wie man überprüft, ob die Änderung eine Zusammenführung ist?

Vielen Dank!

Antwort

4

Was Sie in einem Pre-Receive-Hook bekommen, ist der vorherige und der neue Tipp der Verzweigung. Sie müssen also die Liste der Commits überprüfen, die hinzugefügt wurden, und sehen, ob einige von ihnen keine Merges sind :

nonmerges=$(git rev-list --no-merges --first-parent $oldrefid..$newrefid | wc -l) 
[ "$nonmerges" -eq 0 ] && exit 0 

--first-parent begrenzt die Ausgabe von der Hauptleitung verpflichtet, das heißt, die Festschreibungen in (erreichbar über zweite/dritte/... parent) verschmolzen wurde übersprungen.

Möglicherweise Komplikation: Vorspulen Merges (die aus einer Reihe von normalen Commits zu unterscheiden schwierig sind).

+0

"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. –

+0

@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. –

+0

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. –

6

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.

+0

Danke! Arbeitete auch! +1 –