Em que casos o `git pull 'pode ser prejudicial?


409

Tenho um colega que afirma git pullser prejudicial e fica chateado sempre que alguém o usa.

O git pullcomando parece ser a maneira canônica de atualizar seu repositório local. O uso git pullcria problemas? Que problemas ele cria? Existe uma maneira melhor de atualizar um repositório git?



8
Ou você pode simplesmente git pull --rebasee definir esta estratégia como padrão para novos ramos git config branch.autosetuprebase
knoopx

4
O knoopx está certo, adicionando --rebasesinalizador para git pullsincronizar local com remoto e, em seguida, repete suas alterações locais sobre o local atualizado. Então, quando você pressiona, tudo o que está fazendo é anexar suas novas confirmações no final do controle remoto. Bem simples.
Heath Lilley

4
Obrigado @BenMcCormick. Eu já tinha feito isso, mas a discussão sobre a validade da pergunta parece estar ocorrendo nesses comentários abaixo da pergunta. E acho que fazer uma pergunta para criar uma plataforma para apresentar sua opinião pessoal como fato não é para o que realmente serve a estrutura de perguntas e respostas da SO.
mcv

4
@RichardHansen, parece apenas uma maneira de enganar o sistema de pontos, especialmente porque sua resposta tem uma diferença drástica no tom e um intervalo de tempo tão curto. Usando seu modelo de perguntas e respostas, todos nós poderíamos apenas fazer perguntas e respondê-las, usando nossos conhecimentos anteriores. Nesse ponto, você deve apenas escrever uma postagem no blog, pois isso é muitas vezes mais apropriado. Uma sessão de perguntas e respostas busca especificamente o conhecimento de outras pessoas. Uma postagem no blog exibe o seu.
Josh Brown

Respostas:


546

Sumário

Por padrão, git pullcria confirmações de mesclagem que adicionam ruído e complexidade ao histórico do código. Além disso, pullfacilita não pensar em como suas alterações podem ser afetadas pelas alterações recebidas.

O git pullcomando é seguro desde que execute apenas mesclagens de avanço rápido. Se git pullestiver 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 upalias como este:

git config --global alias.up '!git remote update -p; git merge --ff-only @{u}'

e usando em git upvez de git pull. Prefiro esse alias ao git pull --ff-onlyinvé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 pullnão é ruim se for usado corretamente. Várias alterações recentes no Git tornaram mais fácil o uso git pulladequado, mas infelizmente o comportamento padrão de uma planície git pulltem 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 pullcomando é equivalente a execução git fetchseguido por git merge @{u}. Se houver confirmações não enviadas no repositório local, a parte de git pullmesclagem 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 rebasecomando e por que ele foi criado.) Os commits de mesclagem criados por git pullnã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 pullpara rebase em vez de mesclar, mas isso também tem problemas (discutido mais tarde). Em vez disso, git pulldeve 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 pullmesclará 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 pullseja 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 pullA 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 stashe 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 pulle 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 pullnão lhe dá essa flexibilidade.

Rebaseando em uma ramificação remota

Um padrão de uso comum do Git é fazer um git pullpara trazer as alterações mais recentes seguidas de um git rebase @{u}para eliminar o commit de mesclagem que foi git pullintroduzido. É o suficiente comum que Git tem algumas opções de configuração para reduzir estes dois passos para um único passo, dizendo git pullpara executar uma alteração de base em vez de um merge (ver branch.<branch>.rebase, branch.autosetuprebasee pull.rebaseopçõ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 pullcom branch.<branch>.rebasedefinido como true) nem um merge-pull (o git pullcomportamento padrão ) seguido por um rebase funcionará. Isso ocorre porque git rebaseelimina mesclagens (lineariza o DAG) sem a --preserve-mergesopçã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=preservee git config pull.rebase preserve. Isso git pullocorre git rebase --preserve-mergesdepois de buscar as confirmações upstream. (Obrigado ao funkaster pelo heads-up!)

Limpando ramificações excluídas

git pullnã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 foodo 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 upvez degit pull

Em vez de git pull, recomendo criar e usar o seguinte git upalias:

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 upnunca 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 upalias acima :

git config --global alias.up 'pull --ff-only --all -p'

Esta versão do git uptem o mesmo comportamento que o git upalias 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 -pargumento 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 pullpara fazer mesclagens de avanço rápido por padrão:

git config --global pull.ff only

Isso faz git pullcom 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.


6
@brianz: git remote update -pé equivalente a git fetch --all -p. Eu tenho o hábito de digitar git remote update -pporque era uma vez fetchnão tinha a -popção. Em relação à liderança !, consulte a descrição de alias.*in git help config. Ele diz: "Se a expansão do alias for prefixada com um ponto de exclamação, será tratada como um comando do shell".
Richard Hansen

13
O Git 2.0 adiciona uma pull.ffconfiguração que parece conseguir a mesma coisa, sem aliases.
Danny Thomas

51
Algumas das razões parecem "puxar pode causar problemas quando outros fazem coisas malucas". Não, é uma loucura, como recuperar um commit de um repositório upstream que causa problemas. A rebase da IMO só é segura quando você faz isso localmente em uma confirmação que ainda não foi enviada por push. Por exemplo, quando você puxa antes de empurrar, refazer a consolidação local ajuda a manter seu histórico linear (embora o histórico linear não seja tão importante assim). Ainda assim, git upparece uma alternativa interessante.
mcv

16
A maioria dos seus pontos é porque você está fazendo algo errado: você está tentando revisar o código em seu próprio ramo de trabalho . Não é uma boa ideia, basta criar um novo ramo, puxe --rebase = preserve e, em seguida, atire esse ramo (ou mescle-o, se desejar).
funkaster

5
O argumento do @ funkaster aqui faz muito sentido, especialmente em relação a: "Dificuldade para revisar os compromissos de outras pessoas". Este não é o fluxo de revisão que a maioria dos usuários do Git usa, é algo que eu nunca vi recomendado em nenhum lugar e é a causa de todo o trabalho extra desnecessário descrito abaixo do título, não git pull.
Ben Regenspan 12/03

195

Minha resposta, tirada da discussão que surgiu no HackerNews:

Sinto-me tentado a responder à pergunta usando a Lei de Betteridge das manchetes: Por que é git pullconsiderado prejudicial? Não é.

  • As não linearidades não são intrinsecamente ruins. Se eles representam a história real, estão bem.
  • A reintrodução acidental de confirmações recuperadas a montante é o resultado da reescrita incorreta da história a montante. Você não pode reescrever o histórico quando ele é replicado em várias repos.
  • Modificar o diretório de trabalho é um resultado esperado; de utilidade discutível, especialmente em face do comportamento de hg / monotone / darcs / other_dvcs_predating_git, mas novamente não é intrinsecamente ruim.
  • A pausa para revisar o trabalho de outras pessoas é necessária para uma mesclagem e é novamente um comportamento esperado no git pull. Se você não deseja mesclar, use git fetch. Novamente, essa é uma idiossincrasia do git em comparação com os dvcs populares anteriores, mas é um comportamento esperado e não é intrinsecamente ruim.
  • Tornar difícil a recuperação de uma ramificação remota é bom. Não reescreva a história, a menos que seja absolutamente necessário. Pela minha vida, não consigo entender essa busca por uma história linear (falsa)
  • Não limpar galhos é bom. Cada repo sabe o que deseja realizar. Git não tem noção de relacionamentos mestre-escravo.

13
Concordo. Não há nada inerentemente prejudicial git pull. No entanto, pode entrar em conflito com algumas práticas prejudiciais, como querer reescrever a história mais do que o estritamente necessário. Mas o git é flexível; portanto, se você quiser usá-lo de uma maneira diferente, faça isso de qualquer maneira. Mas isso é porque você (bem, Richard Hansen) quer fazer algo incomum no git, e não porque git pullé prejudicial.
mcv

28
Não poderia concordar mais. As pessoas estão defendendo git rebasee considerando git pullprejudiciais? Mesmo?
Victor Moroz

10
Seria bom ver alguém criar um gráfico, com a moral como seu eixo, e classificar os comandos git como bom, ruim ou em algum lugar intermediário. Esse gráfico seria diferente entre os desenvolvedores, apesar de dizer muito sobre o uso do git.
michaelt

5
Meu problema git pullsem a --rebaseopção é a direção da mesclagem criada. Quando você olha para o diff, todas as alterações nessa mesclagem agora pertencem à pessoa que puxou, e não à pessoa que fez as alterações. Eu gosto de um fluxo de trabalho em que a mesclagem é reservada para duas ramificações separadas (A -> B), para que a confirmação de mesclagem seja clara sobre o que foi introduzido, e o rebasing é reservado para se atualizar na mesma ramificação (remota A -> local A )
Craig Kochis

4
Então, o que faz você saber se alguém deu um puxão apenas alguns segundos antes de outra pessoa ou o contrário? Eu acho que isso é apenas barulho e está ofuscando a história realmente relevante. Isso diminui ainda o valor da história. Uma boa história deve ser a) limpa eb) realmente ter a história importante.
David Ongaro

26

Não é considerado prejudicial se você estiver usando o Git corretamente. Vejo como isso afeta você negativamente, considerando seu caso de uso, mas você pode evitar problemas simplesmente não modificando o histórico compartilhado.


10
Para elaborar sobre isso: se todo mundo trabalha em seu próprio ramo (que na minha opinião é a maneira correta de usar o git), git pullnão há nenhum tipo de problema. Ramificar no git é barato.
AlexQueue

18

A resposta aceita reivindica

A operação rebase-pull não pode ser configurada para preservar mesclagens

mas a partir do Git 1.8.5 , que pós-data dessa resposta, você pode fazer

git pull --rebase=preserve

ou

git config --global pull.rebase preserve

ou

git config branch.<name>.rebase preserve

Os documentos dizem

Quando preserve,também passar --preserve-mergespara 'git rebase' para que as confirmações de mesclagem confirmadas localmente não sejam reduzidas, executando 'git pull'.

Esta discussão anterior possui informações e diagramas mais detalhados: git pull --rebase --preserve-merges . Também explica por que git pull --rebase=preservenão é o mesmo que git pull --rebase --preserve-merges, o que não faz a coisa certa.

Esta outra discussão anterior explica o que a variante preservar-mescla de rebase realmente faz, e como é muito mais complexa do que uma rebase regular: O que exatamente faz a "rebase - preservar-mescla" do git (e por quê?)


Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.