Crochets pré-push Git


115

Je voudrais exécuter des tests unitaires avant chaque push git et si les tests échouent, annulez le push, mais je ne trouve même pas de hook pré-push, il n'y a que pré-commit et pré-rebase.


Réponses:


14

Je préférerais exécuter le test dans un pré-commit-hook. Parce que le changement est déjà enregistré lors de la validation. Push and pull échange uniquement des informations sur les modifications déjà enregistrées Si un test échoue, vous auriez déjà une révision "cassée" dans votre référentiel. Que vous le poussiez ou non.


203
Je suis généralement d'accord, mais si vous avez l'habitude de faire beaucoup de commits incrémentiels pour squash plus tard, et que la suite de tests est grande, cela pourrait être peu pratique.
Cascabel

Je vois. Je suggérerais donc que les tests soient exécutés avant de fusionner avec la branche principale, mais il n'y a pas non plus de hook avant la fusion. Cependant, il existe un hook "update" qui peut être utilisé pour empêcher la mise à jour d'une ref dans le référentiel distant: "Juste avant de mettre à jour la ref sur le référentiel distant, le hook de mise à jour est appelé. Son état de sortie détermine le succès ou l'échec de la ref update. Le hook s'exécute une fois pour chaque ref à mettre à jour, et prend trois paramètres: le nom de la ref mise à jour, l'ancien nom d'objet stocké dans la ref et le nouveau objectname à stocker dans la ref. "
ordnungswidrig

18
Down a voté parce que - bien qu'informatif - il ignore complètement la question du PO.
The Dembinski

1
@TheDembinski Je ne dirais pas qu'il ignore la question du PO. En fait, il prend cela en considération et dit qu'il y a une meilleure façon de le faire que celle que le PO avait en tête. C'est en général le genre de réponse que j'aimerais obtenir.
calder.ty

9
@ calder.ty - Non. manojlds traite mieux ce qui compte. En fait, les hooks pré-commit qui exécutent des tests sont généralement une mauvaise idée imo. Cela suppose que toutes les choses qui sont validées doivent passer des tests. Ce qui est mauvais pour les flux de travail courants axés sur la collaboration. Alors oui ... Je ne suis pas d'accord; ce n'est pas une meilleure façon de faire "ça" ni ne répond à la question.
The Dembinski

209

Git a obtenu le pre-pushcrochet dans la 1.8.2version.

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

1.8.2 Notes de version parlant du nouveau hook pré-push: https://github.com/git/git/blob/master/Documentation/RelNotes/1.8.2.txt


1
@manojlds savez-vous à quoi sert ce hook? Je voudrais l'utiliser pour pousser mon binaire vers mes clients lorsque je pousse vers une branche spécifique (c'est-à-dire créer la version nocturne et la télécharger avec curl, avant de pousser). Le problème est que la construction et le téléchargement prennent un certain temps et la connexion à distance ferme. Je me retrouve donc avec mon binaire construit et téléchargé vers les clients, mais pas poussé vers un dépôt, car le dépôt distant ferme la connexion. Une idée comment contourner cela? Ou peut-être que c'est une mauvaise idée à la racine.
igrek

@igrek avez-vous trouvé une solution au problème de fermeture de connexion?
Mario Estrada

1
@MarioEstrada, oui, je ne me souviens pas exactement comment, mais je l'ai fait pousser deux fois: la première commande git exécute des tests unitaires, puis si elle ne se déconnecte pas, elle pousse et démarre une autre poussée dans un autre thread, si le premier pousse fois dehors, le second d'un autre fil fonctionne pour moi. Si le premier et le second réussissent, alors le premier pousse les changements et le second ne pousse rien. L'astuce est que j'ai eu un argument ajouté, qui contourne les tests unitaires (qui a été utilisé pour le deuxième push git, donc il n'a pas redémarré les tests unitaires)
igrek

24

Git a obtenu le hook pre-push dans la version 1.8.2.

Les hooks pré-push sont ce dont j'avais besoin avec les hooks pré-commit. Outre la protection d'une branche, ils peuvent également fournir une sécurité supplémentaire combinée à des hooks de pré-validation.

Et pour un exemple d'utilisation (repris et adopté et amélioré à partir de cette belle entrée )

Exemple simple pour se connecter à vagrant, exécuter des tests, puis pousser

#!/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 vagrant@192.168.33.10 -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

Comme vous pouvez le voir, l'exemple utilise une branche protégée, objet du crochet de pré-poussée.


14

Si vous utilisez la ligne de commande, le moyen le plus simple de le faire est d'écrire un script push qui exécute vos tests unitaires et, s'ils réussissent, termine le push.

Éditer

Depuis git 1.8.2, cette réponse est obsolète. Voir la réponse de manojlds ci-dessus.


voulez-vous dire ne pas utiliser du tout de crochets? remplacez simplement "git pull" par, par exemple, "git uinttestspull"? ce n'est pas exactement ce dont j'ai besoin
berger

1
@sheepwalker: s / pull / push /, et utilisez un alias pour le rendre joli et court.
Cascabel

@sheepwalker Oui, ce n'est pas exactement ce que vous avez demandé, mais comme @calmh l'a dit, il n'y a pas de crochets pré-push.
kubi

8

Il n'y a pas de hook pour cela, car un push n'est pas une opération qui modifie votre référentiel.

Vous pouvez cependant faire les vérifications du côté de la réception, dans le post-receivecrochet. C'est là que vous rejetteriez généralement une poussée entrante. L'exécution de tests unitaires peut être un peu intensive à faire dans un hook, mais c'est à vous de décider.


6

Pour mémoire, il existe un patch pour Git 1.6 qui ajoute un hook pré-push . Je ne sais pas si cela fonctionne contre 1.7.

Plutôt que de jouer avec ça, vous pouvez exécuter un script push comme @kubi recommandé. Vous pouvez également en faire une tâche Rake à la place pour qu'elle soit dans votre dépôt. ruby-git pourrait vous aider. Si vous vérifiez le dépôt cible, vous ne pouvez exécuter des tests que lors de la transmission vers le dépôt de production.

Enfin, vous pouvez exécuter vos tests dans votre pre-commithook mais vérifiez dans quelle branche est validée. Ensuite, vous pourriez avoir une, disons, une productionbranche qui exige que tous les tests réussissent avant d'accepter un commit, mais mastercela ne vous dérange pas. limerick_rake peut être utile dans ce scénario.


merci, en fait, j'ai déjà choisi la dernière variante (Enfin, vous pouvez exécuter vos tests dans votre hook pre-commit ..)
Shepherd

1

Le script lié par la réponse hautement votée montre les paramètres, etc. au pre-pushhook ( $1est le nom distant, l' $2URL) et comment accéder aux commits (les lignes readde stdin ont une structure <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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.