Sumário
Por padrão, git pull
cria confirmações de mesclagem que adicionam ruído e complexidade ao histórico do código. Além disso, pull
facilita não pensar em como suas alterações podem ser afetadas pelas alterações recebidas.
O git pull
comando é seguro desde que execute apenas mesclagens de avanço rápido. Se git pull
estiver configurado para fazer mesclagens de avanço rápido e quando uma mesclagem de avanço rápido não for possível, o Git sairá com um erro. Isso lhe dará a oportunidade de estudar as confirmações recebidas, pensar em como elas podem afetar as confirmações locais e decidir o melhor curso de ação (mesclagem, rebase, redefinição etc.).
Com o Git 2.0 e mais recente, você pode executar:
git config --global pull.ff only
para alterar o comportamento padrão para avançar apenas. Nas versões Git entre 1.6.6 e 1.9.x, você terá o hábito de digitar:
git pull --ff-only
No entanto, com todas as versões do Git, recomendo configurar um git up
alias como este:
git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
e usando em git up
vez de git pull
. Prefiro esse alias ao git pull --ff-only
invés de:
- funciona com todas as versões (não antigas) do Git,
- busca todos os ramos upstream (não apenas o ramo em que você está trabalhando) e
- limpa
origin/*
galhos antigos que não existem mais a montante.
Problemas com git pull
git pull
não é ruim se for usado corretamente. Várias alterações recentes no Git tornaram mais fácil o uso git pull
adequado, mas infelizmente o comportamento padrão de uma planície git pull
tem vários problemas:
- introduz não-linearidades desnecessárias na história
- facilita a reintrodução acidental de confirmações que foram intencionalmente rebatidas para montante
- ele modifica seu diretório de trabalho de maneiras imprevisíveis
- pausar o que você está fazendo para revisar o trabalho de outra pessoa é irritante
git pull
- dificulta a reorganização correta na ramificação remota
- ele não limpa as ramificações que foram excluídas no repositório remoto
Esses problemas são descritos em mais detalhes abaixo.
História não linear
Por padrão, o git pull
comando é equivalente a execução git fetch
seguido por git merge @{u}
. Se houver confirmações não enviadas no repositório local, a parte de git pull
mesclagem criará uma confirmação de mesclagem.
Não há nada de inerentemente ruim nos commits de mesclagem, mas eles podem ser perigosos e devem ser tratados com respeito:
- Confirmações de mesclagem são inerentemente difíceis de examinar. Para entender o que uma mesclagem está fazendo, você precisa entender as diferenças para todos os pais. Um diff convencional não transmite bem essas informações multidimensionais. Por outro lado, é fácil revisar uma série de confirmações normais.
- A resolução de conflitos de mesclagem é complicada e os erros geralmente não são detectados por um longo tempo porque é difícil revisar os commits de mesclagem.
- Mesclagens podem substituir silenciosamente os efeitos de confirmações regulares. O código não é mais a soma de confirmações incrementais, levando a mal-entendidos sobre o que realmente mudou.
- As confirmações de mesclagem podem interromper alguns esquemas de integração contínua (por exemplo, construir automaticamente apenas o caminho do primeiro pai sob a convenção assumida de que os segundos pais apontam para trabalhos incompletos em andamento).
É claro que existe um tempo e um lugar para mesclagens, mas entender quando as mesclagens devem e não devem ser usadas pode melhorar a utilidade do seu repositório.
Observe que o objetivo do Git é facilitar o compartilhamento e o consumo da evolução de uma base de código, para não registrar com precisão o histórico exatamente como ele se desenrolou. (Se você não concordar, considere o rebase
comando e por que ele foi criado.) Os commits de mesclagem criados por git pull
não transmitem semânticas úteis a outras pessoas - eles apenas dizem que alguém passou por push no repositório antes de concluir as alterações. Por que esses commit de mesclagem se não são significativos para os outros e podem ser perigosos?
É possível configurar git pull
para rebase em vez de mesclar, mas isso também tem problemas (discutido mais tarde). Em vez disso, git pull
deve ser configurado para fazer apenas mesclagens de avanço rápido.
Reintrodução de confirmações rebatizadas
Suponha que alguém refaça um ramo e a força o empurre. Isso geralmente não deveria acontecer, mas às vezes é necessário (por exemplo, remover um arquivo de log 50GiB que foi acidentalmente confirmado e enviado por push). A mesclagem feita por git pull
mesclará a nova versão da ramificação upstream na versão antiga que ainda existe no seu repositório local. Se você pressionar o resultado, garfos e tochas começarão a aparecer.
Alguns podem argumentar que o problema real é a atualização forçada. Sim, geralmente é aconselhável evitar empurrões de força sempre que possível, mas às vezes são inevitáveis. Os desenvolvedores devem estar preparados para lidar com as atualizações de força, porque elas às vezes acontecem. Isso significa não mesclar cegamente os antigos commit por meio de um ordinário git pull
.
Surpreenda as modificações do diretório de trabalho
Não há como prever como será o diretório ou índice de trabalho até que git pull
seja concluído. Pode haver conflitos de mesclagem que você precisa resolver antes de fazer qualquer outra coisa; pode introduzir um arquivo de log 50GiB no seu diretório de trabalho porque alguém o pressionou acidentalmente, pode renomear um diretório em que você está trabalhando etc.
git remote update -p
(ou git fetch --all -p
) permite que você analise os comprometimentos de outras pessoas antes de decidir mesclar ou se refazer, permitindo que você forme um plano antes de tomar uma ação.
Dificuldade para revisar os compromissos de outras pessoas
Suponha que você esteja no meio de fazer algumas alterações e que alguém queira que você revise alguns commits que eles acabaram de enviar. git pull
A operação de mesclagem (ou rebase) do modifica o diretório e o índice de trabalho, o que significa que seu diretório e índice de trabalho devem estar limpos.
Você pode usar git stash
e git pull
, em seguida , mas o que você faz quando termina de revisar? Para voltar para onde você estava, você deve desfazer a mesclagem criada por git pull
e aplicar o stash.
git remote update -p
(ou git fetch --all -p
) não modifica o diretório ou índice de trabalho, portanto, é seguro executá-lo a qualquer momento, mesmo que você tenha feito mudanças de etapa e / ou etapas. Você pode pausar o que está fazendo e revisar o commit de outra pessoa sem se preocupar em esconder ou concluir o commit em que está trabalhando. git pull
não lhe dá essa flexibilidade.
Rebaseando em uma ramificação remota
Um padrão de uso comum do Git é fazer um git pull
para trazer as alterações mais recentes seguidas de um git rebase @{u}
para eliminar o commit de mesclagem que foi git pull
introduzido. É o suficiente comum que Git tem algumas opções de configuração para reduzir estes dois passos para um único passo, dizendo git pull
para executar uma alteração de base em vez de um merge (ver branch.<branch>.rebase
, branch.autosetuprebase
e pull.rebase
opções).
Infelizmente, se você tiver um commit de mesclagem não empurrado que deseja preservar (por exemplo, um commit mesclando um ramo de recurso enviado master
), nem um rebase-pull ( git pull
com branch.<branch>.rebase
definido como true
) nem um merge-pull (o git pull
comportamento padrão ) seguido por um rebase funcionará. Isso ocorre porque git rebase
elimina mesclagens (lineariza o DAG) sem a --preserve-merges
opção A operação rebase-pull não pode ser configurada para preservar mesclagens e um pull de mesclagem seguido por um git rebase -p @{u}
não eliminará a mesclagem causada pelo pull de mesclagem. Atualização: Git v1.8.5 adicionado git pull --rebase=preserve
e git config pull.rebase preserve
. Isso git pull
ocorre git rebase --preserve-merges
depois de buscar as confirmações upstream. (Obrigado ao funkaster pelo heads-up!)
Limpando ramificações excluídas
git pull
não remove ramificações de rastreamento remoto correspondentes às ramificações que foram excluídas do repositório remoto. Por exemplo, se alguém excluir uma ramificação foo
do repositório remoto, você ainda verá origin/foo
.
Isso leva os usuários a ressuscitar acidentalmente os ramos mortos porque eles ainda pensam que estão ativos.
Uma alternativa melhor: use em git up
vez degit pull
Em vez de git pull
, recomendo criar e usar o seguinte git up
alias:
git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'
Esse alias baixa todas as confirmações mais recentes de todas as ramificações upstream (removendo as ramificações inativas) e tenta avançar rapidamente a ramificação local para a confirmação mais recente na ramificação upstream. Se for bem-sucedido, não haverá confirmações locais, portanto não haverá risco de conflito de mesclagem. O avanço rápido falhará se houver confirmações locais (não pressionadas), dando a você a oportunidade de revisar as confirmações anteriores antes de tomar uma ação.
Isso ainda modifica seu diretório de trabalho de maneiras imprevisíveis, mas somente se você não tiver nenhuma alteração local. Ao contrário git pull
, git up
nunca o levará a um prompt esperando que você corrija um conflito de mesclagem.
Outra opção: git pull --ff-only --all -p
A seguir, é uma alternativa ao git up
alias acima :
git config --global alias.up 'pull --ff-only --all -p'
Esta versão do git up
tem o mesmo comportamento que o git up
alias anterior , exceto:
- a mensagem de erro é um pouco mais enigmática se sua filial local não estiver configurada com uma filial upstream
- ele se baseia em um recurso não documentado (o
-p
argumento que é passado para fetch
) que pode mudar nas versões futuras do Git
Se você estiver executando o Git 2.0 ou mais recente
Com o Git 2.0 e mais recente, você pode configurar git pull
para fazer mesclagens de avanço rápido por padrão:
git config --global pull.ff only
Isso faz git pull
com que funcione como ele git pull --ff-only
, mas ainda não busca todos os commit upstream nem limpa os origin/*
galhos antigos, então eu ainda prefiro git up
.