Práticas recomendadas de fluxos de trabalho da equipe no Git: mesclar ou rebase?

Nicola Paolucci
Nicola Paolucci
Voltar para a lista

A pergunta é simples: em uma equipe de software que usa git e branch de recursos, qual é a melhor maneira de incorporar o trabalho finalizado de volta à linha principal de desenvolvimento? É um daqueles debates recorrentes em que ambos os lados têm opiniões fortes, e conversas conscientes às vezes podem ser difíceis (para outros exemplos de debates acalorados, consulte: A Internet).

Você deve adotar uma política de rebase em que o histórico do repositório seja mantido simples e limpo? Ou uma política de merge, que oferece rastreabilidade às custas de legibilidade e clareza (chegando ao ponto de proibir merges de avanço rápido)?

Existe um debate

O tópico é um pouco controverso; talvez não tanto quanto os debates clássicos entre Vim e Emacs, ou entre Linux e BSD, mas os dois campos têm opiniões fortes.

Minha opinião empírica baseada no git — científico, eu sei! — é que a abordagem de sempre merge tem uma aceitação um pouco maior. Mas o campo sempre rebase também é bastante popular on-line. Para obter exemplos, consulte:

Para ser honesto, a divisão em dois campos (sempre rebase versus sempre merge) pode ser confusa, porque rebase como limpeza local é diferente de rebase como política de equipe.

Além disso: o rebase como limpeza é incrível no ciclo de vida da codificação

Rebase como política de equipe é diferente de rebase como limpeza. O rebase como limpeza é uma parte saudável do ciclo de vida de codificação do praticante de git. Deixe-me detalhar alguns exemplos de cenários que mostram quando o rebase é razoável e eficaz (e quando não é):

  • Você está desenvolvendo localmente. Você não compartilhou seu trabalho com mais ninguém. Neste ponto, você deve preferir o rebase em vez do merge para manter o histórico organizado. Se você tem sua bifurcação pessoal do repositório e ela não é compartilhada com outros desenvolvedores, você está seguro para fazer o rebase mesmo depois de ter empurrado para a bifurcação.
  • Seu código está pronto para revisão. Você cria uma pull request, outras pessoas estão revisando seu trabalho e estão potencialmente buscando-o em sua bifurcação para revisão local. Neste ponto, você não deve refazer o rebase do seu trabalho. Você deve criar commits de "retrabalho" e atualizar o branch de recursos. Isso ajuda na rastreabilidade da pull request e evita a quebra acidental do histórico.
  • A revisão está concluída e pronta para ser integrada ao branch de destino. Parabéns! Você está prestes a excluir o branch de recursos. Já que outros desenvolvedores não vão fazer o fetch do merge dessas alterações a partir de agora, esta é sua chance de limpar o histórico. Neste ponto, você pode reescrever o histórico e dobrar os commits originais e aqueles incômodos commits "pr rework" e "merge" em um pequeno conjunto de commits focados. Criar um merge explícito para esses commits é opcional, mas tem valor. Ela registra quando o recurso passou para principal.

Com esse ponto esclarecido, agora podemos falar sobre políticas. Vou tentar manter uma visão equilibrada da discussão e falar sobre como o problema é tratado na Atlassian.

Política da equipe de rebase: definição, prós e contras

Obviamente, é difícil generalizar, pois cada equipe é diferente, mas temos que começar de algum lugar. Considere esta política como um exemplo possível: quando o desenvolvimento de um branch de recursos estiver concluído, faça rebase/squash todo o trabalho até o número mínimo de commits significativos e evite criar um commit de merge — seja garantindo que as mudanças avancem rápido ou simplesmente fazendo o cherry-pick desses commits no branch de destino.

Enquanto o trabalho ainda estiver em andamento e um branch de recursos precisar ser atualizado com o branch de destino upstream, use rebase — em vez de pull ou merge — para não poluir o histórico com merges espúrios.

Prós:

  • O histórico do código permanece plano e legível. Mensagens de commit limpas e claras fazem parte da documentação da sua base de código, como comentários de código, comentários no seu rastreador de itens, etc. Por esse motivo, é importante não poluir o histórico com 31 commits de linha única que se cancelam parcialmente para um único recurso ou correção de bug. Percorrer o histórico para descobrir quando e por que um bug ou recurso foi introduzido vai ser difícil em uma situação como essa.
  • Manipular um único commit é fácil (por exemplo, revertendo-os).

Contras:

  • Esmagar o recurso para um punhado de commits pode ocultar o contexto, a menos que você mantenha o branch de histórico com todo o histórico de desenvolvimento.
  • O rebase não funciona bem com pull requests, porque você não pode ver quais pequenas alterações alguém fez se elas tiverem feito rebase (aliás, o consenso dentro da equipe de desenvolvimento do Bitbucket é nunca fazer o rebase durante uma pull request).
  • O rebase pode ser perigoso! Reescrever o histórico de branches compartilhados pode quebrar o trabalho em equipe. Esse risco pode ser mitigado fazendo o rebase/squash em uma cópia do branch de recursos, mas o rebase carrega a implicação de que competência e cuidado devem ser empregados.
  • É mais trabalhoso: usar o rebase para manter o branch de recursos atualizado requer que você resolva conflitos semelhantes repetidamente. Sim, você pode reutilizar resoluções gravadas (rerere) às vezes, mas os merges levam vantagem aqui: basta resolver os conflitos uma vez e pronto.
  • Outro efeito colateral do rebase com branches remotos é que você precisa forçar o push em algum momento. O maior problema que vimos na Atlassian é que as pessoas forçam o push – o que é bom — mas não definiram git push.default. O resultado: atualizações para todos os branches com o mesmo nome, local e remotamente, o que é uma situação terrível de tratar.

NOTA: Quando o histórico é reescrito em um branch compartilhado tocado por vários desenvolvedores, a quebra acontece.

Política da equipe de merge: definição, prós e contras

Em vez disso, as políticas baseadas em Sempre merge fluem assim: quando um branch de recursos estiver completo, faça o merge com o branch de destino (main ou development ou next).

Garanta que o merge seja explícito com --no-ff, o que força o git a registrar um commit de merge em todos os casos, mesmo que as alterações possam ser repetidas automaticamente no branch de destino.

Prós:

  • Rastreabilidade: ajuda a manter informações sobre a existência histórica de um branch de recursos e agrupa todos os commits que fazem parte do recurso.

Contras:

  • O histórico pode ficar bastante poluído por muitos commits de merge, e os gráficos visuais do repositório podem ter branches de arco-íris que não adicionam muita informação, quando não ofuscam completamente o que está acontecendo. (Agora, para ser justo, a confusão é facilmente resolvida sabendo como navegar em seu histórico; o truque aqui é usar, por exemplo, git log --first-parent para entender o que aconteceu.)
Merge de árvores
  • Depurar usando o git bisect pode se tornar muito mais difícil devido aos commits de merge.

Decisões, decisões, decisões: O que você mais valoriza?

Então, o que é melhor? O que os especialistas recomendam?

Se você e sua equipe não estão familiarizados ou não entendem as complexidades do rebase, provavelmente não deveriam usá-lo. Nesse contexto, sempre merge é a opção mais segura.

Se você e sua equipe estão familiarizados com as duas opções, a decisão principal gira em torno do seguinte: você valoriza mais um histórico limpo e linear? Ou a rastreabilidade de os branches? No primeiro caso, escolha uma política de rebase, no segundo, uma política de merge.

Observe que uma política de rebase vem com pequenas contraindicações e exige mais esforço.

Na Atlassian

A política da equipe do Bitbucket da Atlassian é sempre fazer o merge de branches de recursos e exigir que o merge dos branches seja feito por meio de uma pull request para revisão de qualidade e código. Mas a equipe não é muito rígida em relação ao avanço rápido.

Conclusões e agradecimentos

Este artigo é o resultado da confluência de trocas perspicazes (gostou do trocadilho?) com a equipe do Bitbucket sobre o tópico.

Esperamos que este artigo esclareça as dúvidas sobre esse assunto e permita que você adote uma abordagem que funcione para sua equipe. Siga-me em @durdn e a fantástica equipe @AtlDevtools para mais coisas incríveis do git.

Pronto(a) para aprender Git?

Tente este tutorial interativo.

Comece agora mesmo