Ganchos pré-push Git

115

Gostaria de executar um teste de unidade antes de cada git push e se os testes falharem, cancele o push, mas não consigo nem encontrar o gancho de pré-push, há apenas pré-commit e pré-rebase.

caminhante de ovelhas
fonte

Respostas:

14

Eu prefiro executar o teste em um gancho de pré-confirmação. Porque a mudança já está registrada no momento da confirmação. Push e pull apenas trocam informações sobre as alterações já registradas. Se um teste falhar, você já terá uma revisão "quebrada" em seu repositório. Quer você esteja forçando ou não.

ordnungswidrig
fonte
203
Eu geralmente concordo, embora se você tiver o hábito de fazer muitos commits incrementais para esmagar mais tarde, e o conjunto de testes for grande, isso pode ser impraticável.
Cascabel
Entendo. Então, eu sugeriria que os testes fossem executados antes de mesclar com o branch principal, mas também não há gancho de pré-mesclagem. No entanto, existe um gancho de "atualização" que pode ser usado para evitar a atualização de um ref no repositório remoto: "Pouco antes de atualizar o ref no repositório remoto, o gancho de atualização é invocado. Seu status de saída determina o sucesso ou falha do ref update. O gancho é executado uma vez para cada ref a ser atualizado e leva três parâmetros: o nome do ref sendo atualizado, o nome do objeto antigo armazenado no ref e o novo nome do objeto a ser armazenado no ref. "
ordnungswidrig
18
Votação negativa porque - embora informativa - ignora completamente a pergunta do OP.
The Dembinski
1
@TheDembinski Eu não diria que ignora a questão do OP. Na verdade, leva isso em consideração e diz que existe uma maneira melhor de fazer isso do que a que o OP tinha em mente. Em geral, esse é o tipo de resposta que gostaria de obter.
calder.ty
9
@ calder.ty - Nah. manojlds aborda melhor o que importa. Na verdade, ganchos de pré-confirmação que executam testes geralmente são uma má ideia. Ele pressupõe que todas as coisas que são confirmadas devem passar nos testes. O que é ruim para fluxos de trabalho comuns que se concentram na colaboração. Então sim ... eu discordo; não é a melhor maneira de fazer "isso" nem aborda a questão.
O Dembinski
209

Git ganhou o pre-pushgancho no 1.8.2lançamento.

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

1.8.2 notas de versão falando sobre o novo gancho de pré-push: https://github.com/git/git/blob/master/Documentation/RelNotes/1.8.2.txt

manojlds
fonte
1
@manojlds, você sabe para que esse gancho foi projetado? Eu gostaria de usá-lo para enviar meu binário aos meus clientes ao enviar para um branch específico (ou seja, construir a versão noturna e carregá-la com curl, antes de enviar). O problema é que demora um pouco para construir e fazer upload, e o remoto fecha a conexão. Então, acabo com meu binário criado e carregado para os clientes, mas não enviado para um repo, porque o repo remoto fecha a conexão. Alguma ideia de como contornar isso? Ou talvez seja uma má ideia em sua raiz.
igrek
@igrek você encontrou uma solução para o problema de fechamento da conexão?
Mario Estrada
1
@MarioEstrada, sim, não me lembro exatamente como, mas fiz push duas vezes: primeiro o comando git executa testes de unidade e, em seguida, se não desconectar, ele empurra e inicia outro push em outro thread, se o primeiro push vezes fora, o segundo de outro segmento funciona para mim. Se o primeiro e o segundo forem bem-sucedidos, o primeiro empurra as mudanças e o segundo não empurra nada. O truque é que adicionei um argumento que ignora os testes de unidade (que foram usados ​​para o segundo push do git, então não
reiniciou os
24

O Git ganhou o gancho pré-push na versão 1.8.2.

Pre-push hooks são o que eu precisava junto com os hooks pré-commit. Além de proteger um branch, eles também podem fornecer segurança extra combinada com ganchos pré-commit.

E para um exemplo de como usar (tomado, adotado e aprimorado a partir desta bela entrada )

Exemplo simples para fazer login no vagrant, executar testes e enviar

#!/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

Como você pode ver, o exemplo usa um galho protegido, assunto do gancho pré-push.

Jimmy Kane
fonte
14

Se você estiver usando a linha de comando, a maneira mais fácil de fazer isso é escrever um script de push que execute seus testes de unidade e, se eles forem bem-sucedidos, conclua o push.

Editar

No git 1.8.2, essa resposta está desatualizada. Veja a resposta de manojlds acima.

kubi
fonte
você quer dizer não usar ganchos? basta substituir "git pull" por, por exemplo, "git uinttestspull"? isso não é exatamente o que eu preciso
sheepwalker de
1
@sheepwalker: s / pull / push / e use um apelido para torná-lo agradável e curto.
Cascabel
@sheepwalker Sim, não foi exatamente isso que você pediu, mas, como disse @calmh, não há ganchos pré-push.
kubi
8

Não há um gancho para isso, porque um push não é uma operação que modifica seu repositório.

Você pode fazer as verificações no lado receptor, no entanto, no post-receivegancho. É aí que você geralmente rejeitaria um push recebido. A execução de testes de unidade pode ser um pouco trabalhosa para fazer em um gancho, mas isso é com você.

Jakob Borg
fonte
6

Para constar, há um patch para Git 1.6 que adiciona um gancho pré-push . Não sei se funciona contra 1,7.

Em vez de mexer com isso, você pode executar um script push como o recomendado pelo @kubi. Você também pode torná-la uma tarefa Rake para que fique em seu repo. ruby-git pode ajudar com isso. Se você verificar o repo de destino, poderá executar testes apenas ao enviar para o repo de produção.

Finalmente, você pode executar seus testes em seu pre-commitgancho, mas verificar com qual branch está sendo comprometido. Então você poderia ter um, digamos, um productionbranch que requer que todos os testes passem antes de aceitar um commit, mas você masternão se importa. limerick_rake pode ser útil nesse cenário.

Turadg
fonte
obrigado, na verdade eu já escolhi a última variante (finalmente, você pode executar seus testes em seu gancho de pré-confirmação ...)
sheepwalker
1

O script vinculado pela resposta mais votada mostra os parâmetros etc para o pre-pushgancho ( $1é o nome remoto, $2URL) e como acessar os commits (linhas readde stdin têm estrutura <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
fonte