As solicitações squashing pull quebram o algoritmo de fusão do git?

15

Atualmente, estou trabalhando para uma empresa que usa o VSTS para gerenciar código git. A maneira "recomendada" da Microsoft de mesclar uma ramificação é fazer uma "mesclagem de squash", o que significa que todas as confirmações dessa ramificação são comprimidas em uma nova confirmação incorporando todas as alterações.

O problema é que, se eu fizer algumas alterações em uma ramificação para um item da lista de pendências, imediatamente quiser começar a fazer alterações em outra ramificação para outro item da lista de pendências, e essas alterações dependem do conjunto de alterações da primeira ramificação?

Posso criar uma ramificação para esse item da lista de pendências e baseá-la na primeira ramificação. Por enquanto, tudo bem. No entanto, quando chega a hora de criar uma solicitação de recebimento para o segundo ramo, o primeiro ramo já foi mesclado no mestre e, como foi feito como uma mescla de squash, o git sinaliza vários conflitos. Isso ocorre porque o git não vê os commits originais dos quais o segundo ramo foi baseado, apenas vê uma grande mistura de squash e, portanto, para mesclar o segundo branch para dominar, ele tenta reproduzir todos os commits do primeiro branch em mesclagem no topo da squash, causando muitos conflitos.

Portanto, minha pergunta é: existe alguma maneira de contornar isso (além de nunca basear uma ramificação de recurso em outra, o que limita meu fluxo de trabalho) ou a mesclagem de squash apenas quebra o algoritmo de mesclagem do git?

Jez
fonte

Respostas:

15

Com o Git, confirma

  • são imutáveis,
  • e formar um gráfico acíclico direcionado.

O esmagamento não combina confirmações. Em vez disso, ele registra uma nova confirmação com as alterações de várias outras confirmações. O rebaseamento é semelhante, mas não combina confirmações. Gravar uma nova confirmação com as mesmas alterações que uma confirmação existente é chamado de reescrita do histórico . Mas, como os commits existentes são imutáveis, isso deve ser entendido como "escrever uma história alternativa".

A mesclagem tenta combinar as alterações dos históricos (ramificações) de dois commit a partir de um commit ancestral comum.

Então, vamos dar uma olhada no seu histórico:

                                 F  feature2
                                /
               1---2---3---4---5    feature1 (old)
              /
-o---o---o---A---o---o---S          master

A é o ancestral comum, 1–5 o ramo do recurso original, F o novo ramo do recurso e S o commit compactado que contém as mesmas alterações que 1–5. Como você pode ver, o ancestral comum de F e S é A. No que diz respeito ao git, não há relação entre S e 1-5. Portanto, a fusão do mestre com S de um lado e do recurso2 com 1–5 do outro entrará em conflito. Resolver esses conflitos não é difícil, mas é um trabalho desnecessário e tedioso.

Devido a essas restrições, existem duas abordagens para lidar com mesclagem / compressão:

  • Você usa a reescrita do histórico; nesse caso, você obtém várias confirmações que representam as mesmas alterações. Você então rebase o segundo ramo de recurso no commit achatado:

                                     F  feature2 (old)
                                    /
                   1---2---3---4---5    feature1 (old)
                  /
    -o---o---o---A---o---o---S          master
                              \
                               F'       feature2
    
  • Ou você não usa a reescrita do histórico; nesse caso, você pode obter confirmações adicionais de mesclagem:

                                     F  feature2
                                    /
                   1---2---3---4---5    feature1 (old)
                  /                 \
    -o---o---o---A---o---o-----------M  master
    

    Quando o feature2 e o master são mesclados, o ancestral comum será confirmado 5.

Nos dois casos, você terá algum esforço de fusão. Esse esforço não depende muito de qual das duas estratégias acima você escolhe. Mas certifique-se de que

  • os galhos têm vida curta, para limitar o quão longe eles podem se desviar do ramo mestre e
  • você mescla regularmente o mestre na ramificação de recursos ou refaz a ramificação de recursos no mestre para manter as ramificações sincronizadas.

Ao trabalhar em equipe, é útil coordenar quem está trabalhando atualmente em quê. Isso ajuda a manter pequeno o número de recursos em desenvolvimento e pode reduzir o número de conflitos de mesclagem.

amon
fonte
2
Sua resposta parece não abordar o que acontece se você primeiro mesclar feature1no mestre e depois mesclar feature2mais tarde. Nesse caso, a primeira abordagem não resultaria em conflitos, já que o git tenta reaplicar os feature1commits no topo do commit squashed, enquanto o segundo permitiria ao git determinar que esses commits não precisam ser mesclados?
Jez
@ Jez Isso é exatamente o que acontece quando você esmaga um PR. Recentemente, tive que reescrever manualmente um PR em um projeto OSS ( git cloneinserindo o repositório e copiando meus arquivos alterados!) Porque ramifiquei de uma ramificação e, em seguida, o mantenedor esmagou a primeira ramificação. No meu trabalho, eles também fazem fusões de squash. Isso significa que não posso trabalhar em um recurso bque depende dele aaté que o recurso aseja mesclado.
Jogue fora a conta
1
E isso não é uma quebra realmente irritante de algo que de outra forma funcionaria, como o git é projetado? Veja, vejo várias organizações como a Microsoft e o Github recomendando essas fusões de squash e elas me parecem idiotas.
Jez
1
@Jez No cenário original, sim, você entrará em conflito ao mesclar o feature2 no master porque mesclar os commits 1–5 entrará em conflito com as mesmas alterações em S. tudo (solução 2).
amon
Se as mesclagens de squash são adequadas para você depende do que você deseja gravar no histórico de controle de versão. Se as ramificações do recurso tiverem muitas confirmações WIP, o esmagamento colocará uma única confirmação grande com o recurso completo na ramificação mestre. Se você preferir preservar todas as confirmações intermediárias da ramificação do recurso, use rebasing ou mesclagem.
amon
11

A mesclagem de squash quebra o algoritmo de mesclagem de quaisquer ramificações que contenham quaisquer confirmações que foram removidas pela squash. Dito de outra forma, os rebotes são virais. Se você refazer uma ramificação, precisará refazer qualquer outra ramificação que dependesse dessa ramificação. Se você usar o redirecionamento , os conflitos de mesclagem que você resolveu manualmente em seu repositório local não precisarão ser resolvidos manualmente novamente, mas isso não ajudará nos conflitos resolvidos por outras pessoas.

É por isso que nossa regra não escrita aqui é aceitável esmagar, desde que ninguém mais dependa do ramo de recursos, o que ocorre talvez 90% das vezes. Caso contrário, uma mesclagem direta ajuda todos a evitar problemas.

Karl Bielefeldt
fonte
Uma maneira de ter uma confirmação compactada no histórico mestre e uma ramificação de recursos intacta para criar uma ramificação separada somente para squash. Suponha que você tenha um feature-xyzramo. Você pode criar uma feature-xyz-squashedramificação iniciando na mesma confirmação que feature-xyzramificação, git cherry-pickas confirmações de feature-xyzpara feature-xyz-squashed, esmagá-las lá e mesclar feature-xyz-squashedpara master. Você não deve mesclar feature-xyzentão. Às vezes, o acima mencionado faz sentido (por exemplo, você não deseja incluir confirmações com uma senha oculta), mas é uma solução alternativa, dificilmente uma prática recomendada.
9000