Git Pre-Push-Haken

115

Ich möchte vor jedem Git-Push einen Unit-Test durchführen. Wenn der Test fehlschlägt, brechen Sie den Push ab, aber ich kann nicht einmal einen Pre-Push-Hook finden. Es gibt nur Pre-Commit und Pre-Rebase.

Schafwanderer
quelle

Antworten:

14

Ich würde den Test lieber in einem Pre-Commit-Hook ausführen. Weil die Änderung bereits beim Festschreiben aufgezeichnet wird. Push and Pull tauschen nur Informationen über bereits aufgezeichnete Änderungen aus. Wenn ein Test fehlschlägt, haben Sie bereits eine "defekte" Revision in Ihrem Repository. Ob Sie es schieben oder nicht.

bestimmtewidrig
quelle
203
Ich stimme im Allgemeinen zu, obwohl dies unpraktisch sein kann, wenn Sie die Gewohnheit haben, später viele inkrementelle Zusagen zum Squash zu machen, und die Testsuite groß ist.
Cascabel
Aha. Daher würde ich vorschlagen, dass die Tests vor dem Zusammenführen mit dem Hauptzweig ausgeführt werden, aber es gibt auch keinen Hook vor dem Zusammenführen. Es gibt jedoch einen "Update" -Hook, mit dem verhindert werden kann, dass eine Referenz im Remote-Repository aktualisiert wird: "Kurz vor der Aktualisierung der Referenz im Remote-Repository wird der Update-Hook aufgerufen. Sein Exit-Status bestimmt den Erfolg oder Misserfolg der Referenz update. Der Hook wird einmal für jede zu aktualisierende Referenz ausgeführt und verwendet drei Parameter: den Namen der zu aktualisierenden Referenz, den in der Referenz gespeicherten alten Objektnamen und den in der Referenz zu speichernden neuen Objektnamen. "
Besitzwidrig
18
Abgestimmt, weil es - obwohl informativ - die Frage des OP völlig ignoriert.
Der Dembinski
1
@ TheDembinski Ich würde nicht sagen, dass es die OP-Frage ignoriert. Tatsächlich berücksichtigt es dies und sagt, dass es einen besseren Weg gibt, dies zu tun, als es das OP im Sinn hatte. Das ist im Allgemeinen die Art von Antwort, die ich gerne bekommen würde.
calder.ty
9
@ calder.ty - Nein. Manojlds spricht besser an, worauf es ankommt. In der Tat sind Pre-Commit-Hooks, die Tests ausführen, im Allgemeinen eine schlechte Idee. Es wird davon ausgegangen, dass alle Dinge, die festgeschrieben werden, Tests bestehen müssen. Das ist schlecht für gängige Arbeitsabläufe, die sich auf die Zusammenarbeit konzentrieren. Also ja ... ich bin anderer Meinung; Es ist weder ein besserer Weg, "es" zu tun, noch geht es auf die Frage ein.
Der Dembinski
209

Git hat den pre-pushHaken in der 1.8.2Veröffentlichung.

Beispielskript pre-push: https://github.com/git/git/blob/87c86dd14abe8db7d00b0df5661ef8cf147a72a3/templates/hooks--pre-push.sample

1.8.2 Versionshinweise zum neuen Pre-Push-Hook: https://github.com/git/git/blob/master/Documentation/RelNotes/1.8.2.txt

Manojlds
quelle
1
@manojlds Weißt du, wofür dieser Haken gedacht ist? Ich möchte es verwenden, um meine Binärdatei an meine Kunden zu senden, wenn sie in einen bestimmten Zweig verschoben wird (dh die nächtliche Version erstellen und mit Curl hochladen, bevor sie gesendet wird). Das Problem ist, dass das Erstellen und Hochladen eine Weile dauert und die Remote-Verbindung geschlossen wird. Am Ende habe ich meine Binärdatei erstellt und an Kunden hochgeladen, aber nicht in ein Repo verschoben, da das Remote-Repo die Verbindung schließt. Irgendeine Idee, wie man das umgehen kann? Oder vielleicht ist es eine schlechte Idee in seiner Wurzel.
Igrek
@igrek Haben Sie eine Lösung für das Problem beim Schließen der Verbindung gefunden?
Mario Estrada
1
@MarioEstrada, ja, ich weiß nicht genau, wie, aber ich habe es zweimal pushen lassen: Der erste git-Befehl führt Unit-Tests aus und wenn er nicht getrennt wird, pusht er und startet einen weiteren Push in einem anderen Thread, wenn der erste puscht raus, der zweite aus einem anderen Thread funktioniert für mich. Wenn entweder der erste oder der zweite erfolgreich ist, drückt der erste auf Änderungen und der zweite auf nichts. Der Trick ist, dass ich einige Argumente hinzugefügt habe, die Unit-Tests umgehen (die für den zweiten Git-Push verwendet wurden, so dass Unit-Tests nicht erneut gestartet wurden)
igrek
24

Git hat den Pre-Push-Hook in der Version 1.8.2 bekommen.

Pre-Push-Hooks waren genau das, was ich brauchte, zusammen mit Pre-Commit-Hooks. Neben dem Schutz eines Zweigs können sie auch zusätzliche Sicherheit in Kombination mit Pre-Commit-Hooks bieten.

Und für ein Beispiel zur Verwendung (aus diesem schönen Eintrag übernommen und übernommen und erweitert )

Einfaches Beispiel, um sich bei Vagrant anzumelden, Tests durchzuführen und dann zu pushen

#!/bin/bash
# Run the following command in the root of your project to install this pre-push hook:
# cp git-hooks/pre-push .git/hooks/pre-push; chmod 700 .git/hooks/pre-push

CMD="ssh [email protected] -i ~/.vagrant.d/insecure_private_key 'cd /vagrant/tests; /vagrant/vendor/bin/phpunit'"
protected_branch='master'

# Check if we actually have commits to push
commits=`git log @{u}..`
if [ -z "$commits" ]; then
    exit 0
fi

current_branch=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,')

if [[ $current_branch = $protected_branch ]]; then
    eval $CMD
    RESULT=$?
    if [ $RESULT -ne 0 ]; then
        echo "failed $CMD"
        exit 1
    fi
fi
exit 0

Wie Sie sehen können, verwendet das Beispiel einen geschützten Zweig, der Gegenstand des Pre-Push-Hakens ist.

Jimmy Kane
quelle
14

Wenn Sie die Befehlszeile verwenden, können Sie dies am einfachsten tun, indem Sie ein Push-Skript schreiben, das Ihre Komponententests ausführt und bei Erfolg den Push abschließt.

Bearbeiten

Ab Git 1.8.2 ist diese Antwort veraltet. Siehe die Antwort von manojlds oben.

kubi
quelle
Meinst du, überhaupt keine Haken zu benutzen? Ersetzen Sie einfach "git pull" durch "git uinttestspull". das ist nicht genau das, was ich brauche
Schafwanderer
1
@sheepwalker: s / pull / push / und benutze einen Alias, um es schön kurz zu machen.
Cascabel
@sheepwalker Ja, das ist nicht genau das, wonach Sie gefragt haben, aber wie @calmh sagte, gibt es keine Pre-Push-Haken.
Kubi
8

Es gibt keinen Hook dafür, da ein Push keine Operation ist, die Ihr Repository ändert.

Sie können die Überprüfungen jedoch auf der Empfangsseite im post-receiveHook durchführen. Dort lehnen Sie normalerweise einen eingehenden Push ab. Das Ausführen von Komponententests ist möglicherweise etwas intensiv, aber das liegt bei Ihnen.

Jakob Borg
quelle
6

Für die Aufzeichnung gibt es einen Patch zu Git 1.6, der einen Pre-Push-Hook hinzufügt . Ich weiß nicht, ob es gegen 1.7 funktioniert.

Anstatt sich damit herumzuschlagen, könnten Sie ein Push-Skript ausführen, wie es @kubi empfohlen hat. Sie können es stattdessen auch zu einer Rake-Aufgabe machen, damit es in Ihrem Repo ist. Ruby-Git könnte dabei helfen. Wenn Sie das Ziel-Repo überprüfen, können Sie Tests nur ausführen, wenn Sie zum Produktions-Repo wechseln.

Schließlich können Sie Ihre Tests in Ihrem pre-commitHook ausführen, aber prüfen, für welchen Zweig ein Commit ausgeführt wird. Dann könnten Sie beispielsweise einen productionZweig haben, für den alle Tests bestanden werden müssen, bevor ein Commit akzeptiert wird, aber es ist Ihnen masteregal. limerick_rake kann in diesem Szenario nützlich sein.

Turadg
quelle
danke, eigentlich habe ich schon die letzte Variante gewählt (Endlich könntest du deine Tests in deinem Pre-Commit-Hook
ausführen
1

Das Skript, das durch die hoch bewertete Antwort verknüpft ist, zeigt die Parameter usw. des pre-pushHooks ( $1ist Remote-Name, $2URL) und wie auf die Commits zugegriffen wird (Zeilen readvon stdin haben Struktur <local ref> <local sha1> <remote ref> <remote sha1>)

#!/bin/sh

# An example hook script to verify what is about to be pushed.  Called by "git
# push" after it has checked the remote status, but before anything has been
# pushed.  If this script exits with a non-zero status nothing will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
#   <local ref> <local sha1> <remote ref> <remote sha1>
#
# This sample shows how to prevent push of commits where the log message starts
# with "WIP" (work in progress).

remote="$1"
url="$2"

z40=0000000000000000000000000000000000000000

while read local_ref local_sha remote_ref remote_sha
do
    if [ "$local_sha" = $z40 ]
    then
        # Handle delete
        :
    else
        if [ "$remote_sha" = $z40 ]
        then
            # New branch, examine all commits
            range="$local_sha"
        else
            # Update to existing branch, examine new commits
            range="$remote_sha..$local_sha"
        fi

        # Check for WIP commit
        commit=`git rev-list -n 1 --grep '^WIP' "$range"`
        if [ -n "$commit" ]
        then
            echo >&2 "Found WIP commit in $local_ref, not pushing"
            exit 1
        fi
    fi
done

exit 0
serv-inc
quelle