Supondo que o repositório remoto tenha uma cópia do ramo de desenvolvimento (sua descrição inicial o descreve em um repositório local, mas parece que também existe no remoto), você deve conseguir o que eu acho que deseja, mas a abordagem é um pouco diferente do que você imaginou.
A história do Git é baseada em um DAG de confirmações. Ramos (e “refs” em geral) são apenas rótulos transitórios que apontam para confirmações específicas no DAG de confirmação em crescimento contínuo. Como tal, o relacionamento entre ramificações pode variar ao longo do tempo, mas o relacionamento entre confirmações não.
---o---1 foo
\
2---3---o bar
\
4
\
5---6 baz
Parece que baz
é baseado em (uma versão antiga do) bar
? Mas e se excluirmos bar
?
---o---1 foo
\
2---3
\
4
\
5---6 baz
Agora parece que baz
é baseado foo
. Mas a ancestralidade de baz
não mudou, apenas removemos um rótulo (e o commit dangling resultante). E se adicionarmos um novo rótulo em 4
?
---o---1 foo
\
2---3
\
4 quux
\
5---6 baz
Agora parece que baz
é baseado quux
. Ainda assim, a ancestralidade não mudou, apenas os rótulos mudaram.
Se, no entanto, perguntássemos "é comprometer 6
um descendente de confirmar 3
?" (assumindo 3
e 6
com nomes completos de confirmação do SHA-1), a resposta seria "sim", independentemente de os rótulos bar
e quux
estarem presentes.
Portanto, você pode fazer perguntas como "o push enviado é descendente da ponta atual do ramo de desenvolvimento ?", Mas não é possível perguntar com segurança "qual é o ramo pai do commit enviado?".
Uma pergunta principalmente confiável que parece se aproximar do que você deseja é:
Para todos os ancestrais do commit enviado (excluindo a dica atual de desenvolvimento e seus ancestrais), que têm a dica atual de desenvolvimento como pai:
- existe pelo menos um desses commit?
- são todos esses commit confirmados monoparentais?
O que poderia ser implementado como:
pushedrev=...
basename=develop
if ! baserev="$(git rev-parse --verify refs/heads/"$basename" 2>/dev/null)"; then
echo "'$basename' is missing, call for help!"
exit 1
fi
parents_of_children_of_base="$(
git rev-list --pretty=tformat:%P "$pushedrev" --not "$baserev" |
grep -F "$baserev"
)"
case ",$parents_of_children_of_base" in
,) echo "must descend from tip of '$basename'"
exit 1 ;;
,*\ *) echo "must not merge tip of '$basename' (rebase instead)"
exit 1 ;;
,*) exit 0 ;;
esac
Isso cobrirá parte do que você deseja restringir, mas talvez nem tudo.
Para referência, aqui está um histórico de exemplo estendido:
A master
\
\ o-----J
\ / \
\ | o---K---L
\ |/
C--------------D develop
\ |\
F---G---H | F'--G'--H'
| |\
| | o---o---o---N
\ \ \ \
\ \ o---o---P
\ \
R---S
O código acima poderia ser usado para rejeitar H
e S
ao aceitar H'
, J
, K
, ou N
, mas seria também aceitar L
e P
(eles envolvem fusões, mas eles não se fundem a ponta de desenvolver ).
Para também rejeitar L
e P
, você pode alterar a pergunta e fazer
Para todos os ancestrais do commit enviado (excluindo a dica atual de develop e seus ancestrais):
- há algum comprometimento com dois pais?
- caso contrário, pelo menos um desses commit tem a dica atual de desenvolver seu (único) pai?
pushedrev=...
basename=develop
if ! baserev="$(git rev-parse --verify refs/heads/"$basename" 2>/dev/null)"; then
echo "'$basename' is missing, call for help!"
exit 1
fi
parents_of_commits_beyond_base="$(
git rev-list --pretty=tformat:%P "$pushedrev" --not "$baserev" |
grep -v '^commit '
)"
case "$parents_of_commits_beyond_base" in
*\ *) echo "must not push merge commits (rebase instead)"
exit 1 ;;
*"$baserev"*) exit 0 ;;
*) echo "must descend from tip of '$basename'"
exit 1 ;;
esac