Por que tantos projetos preferem “git rebase” ao invés de “git merge”?

64

Uma das vantagens de usar um DVCS é o fluxo de trabalho edit-commit-merge (sobre edit-merge-commit geralmente aplicado por um CVCS). Permitir que cada alteração única seja registrada no repositório, independentemente das mesclagens, garante que o DAG reflita com precisão o verdadeiro pedigree do projeto.

Por que tantos sites falam sobre querer "evitar mesclar confirmações"? Não mesclar pre-commit ou rebasing pós-merge dificulta isolar regressões, reverter mudanças passadas, etc.?

Ponto de esclarecimento: O comportamento padrão para um DVCS é para criar commits de mesclagem. Por que tantos lugares falam sobre o desejo de ver um histórico de desenvolvimento linear que esconde esses commits de mesclagem?

    
por Jace Browning 18.11.2013 / 19:15
fonte

6 respostas

62

As pessoas querem evitar commits de mesclagem porque isso torna o registro mais bonito. A sério. Ele se parece com os logs centralizados com os quais cresceram e, localmente, eles podem fazer todo o seu desenvolvimento em um único branch. Não há benefícios além dessas estéticas e vários inconvenientes além daqueles que você mencionou, como tornar o conflito propenso a ser enviado diretamente de um colega sem passar pelo servidor "central".

    
por 18.11.2013 / 20:30
fonte
44

Em duas palavras: git bisect

Um histórico linear permite identificar a fonte real de um bug.

Um exemplo. Este é o nosso código inicial:
def bar(arg=None):
    pass

def foo():
    bar()

O ramo 1 faz algumas refatorações de modo que arg não seja mais válido:

def bar():
    pass

def foo():
    bar()

O ramo 2 tem um novo recurso que precisa usar arg :

def bar(arg=None):
    pass

def foo():
    bar(arg=1)

Não haverá conflitos de mesclagem, no entanto, um bug foi introduzido. Felizmente, esse em particular será pego na etapa de compilação, mas nem sempre temos muita sorte. Se o bug se manifestar como comportamento inesperado, em vez de um erro de compilação, talvez não o encontremos por uma semana ou duas. Nesse ponto, git bisect para o resgate!

Oh merda. Isto é o que vê:

(master: green)
|             \_________________________
|                \                      \
(master: green)  (branch 1: green)     (branch 2: green)
|                 |                     |
|                 |                     |
(master/merge commit: green)            |
|                         ______________/
|                        /
(master/merge commit: red)
|
...days pass...
|
(master: red)

Então, quando nós enviamos git bisect para encontrar o commit que quebrou a compilação, ele irá apontar um commit de merge. Bem, isso ajuda um pouco, mas está essencialmente apontando para um pacote de commits, não um único. Todos os ancestrais são verdes. Por outro lado, com rebasing, você obtém uma história linear:

(master: green)
|
(master: green)
|
(all branch 1 commits: green)
|
(some branch 2 commits: green)
|
(branch 2 commit: red)
|
(remaining branch 2 commits: red)
|
...days pass...
|
(master: still red)

Agora, git bisect vai apontar para o commit exato do recurso que quebrou a construção. Idealmente, a mensagem de confirmação explicará o que foi planejado bem o suficiente para fazer outro refatorador e corrigir o erro imediatamente.

O efeito só é composto em grandes projetos quando os mantenedores não escrevem todo o código, então eles não se lembram necessariamente de porque um determinado commit foi feito / para o que cada ramificação foi feita. Então, apontar o commit exato (e então poder examinar os commits em torno dele) é uma grande ajuda.

Dito isto, eu (atualmente) ainda prefiro a fusão. Rebasing em um branch release lhe dará seu histórico linear para uso com git bisect , mantendo o histórico verdadeiro para o trabalho do dia-a-dia.

    
por 19.11.2013 / 05:20
fonte
16
Em suma, porque a fusão é muitas vezes outro lugar para algo dar errado, e só precisa dar errado uma vez para fazer as pessoas com muito medo de lidar com isso novamente (uma vez mordido duas vezes tímido, se você quiser). >

Então, digamos que estamos trabalhando em uma nova tela de gerenciamento de contas, e há um bug descoberto no fluxo de trabalho da nova conta. OK, tomamos dois caminhos separados - você conclui o Gerenciamento de Contas e eu corrijo o bug com Novas Contas. Como estamos lidando com contas, estamos trabalhando com código muito semelhante - talvez tenhamos que ajustar os mesmos códigos.

Agora, neste momento, temos duas versões diferentes, mas totalmente funcionais, do software. Nós dois temos um compromisso com nossas mudanças, nós testamos o nosso código e, independentemente, estamos muito confiantes de que fizemos um ótimo trabalho. E agora?

Bem, é hora de se fundir, mas ... porcaria, o que acontece agora? Poderíamos muito bem ir de dois conjuntos de software para um software unificado e terrivelmente quebrado de software com bugs, onde o seu Gerenciamento de Contas não funciona e as Novas Contas estão quebradas e eu nem sei se o bug antigo ainda está lá .

Talvez o software tenha sido inteligente e tenha dito que houve um conflito e insistimos em dar-lhe orientação. Bem, droga - eu sento para fazer isso e vejo que você adicionou algum código complexo que eu não entendo imediatamente. Eu acho que entra em conflito com as mudanças que eu fiz ... Eu te peço, e quando você chega um minuto você checa e vê meu código que você não entende. Um ou dois de nós têm que reservar um tempo para sentar, fazer uma mescla adequada e, possivelmente, testar novamente a coisa toda para ter certeza de que não a quebramos.

Enquanto isso, outros 8 caras estão cometendo código como os sádicos que são, eu fiz alguns pequenos consertos de erros e os enviei antes que eu soubesse que tínhamos um conflito de mesclagem, e cara, parece ser uma boa hora para fazer uma pausa, e talvez você esteja de folga durante a tarde ou preso em uma reunião ou qualquer outra coisa. Talvez eu deva tirar férias. Ou mude de carreira.

E assim, para escapar desse pesadelo, algumas pessoas ficaram com muito medo de compromisso (o que mais é novo, direito?). Naturalmente, somos avessos ao risco em cenários como este - a menos que pensemos que somos péssimos e vamos estragar tudo de qualquer maneira, caso em que as pessoas começam a agir com abandono imprudente. suspiro

Então lá vai você. Sim, os sistemas modernos são projetados para aliviar essa dor, e é suposto ser capaz de retroceder e rebaixar e rebaixar e liberar o freebase e o hanglide e tudo isso.

Mas é tudo mais trabalho, e nós só queremos apertar o botão no micro-ondas e fazer uma refeição de 4 pratos antes que tenhamos tempo de encontrar um garfo, e tudo parece muito insatisfatório - código é trabalho, é produtivo, é significativo, mas lidar com a mesclagem de um modo simples não conta.

Os programadores, como regra, precisam desenvolver uma ótima memória de trabalho, e depois tendem a esquecer imediatamente todos os nomes e escopos de variáveis e escopos assim que terminam o problema, e lidar com um conflito de mesclagem (ou pior, uma fusão mal administrada) é um convite para ser lembrado de sua mortalidade.

    
por 18.11.2013 / 19:38
fonte
4

Rebasing fornece um ponto de ramificação móvel que simplifica o processo de empurrar as alterações de volta para a linha de base. Isso permite que você trate uma ramificação de longa duração como se fosse uma alteração local. Sem rebasing, os branches acumulam mudanças da linha de base que serão incluídas nas mudanças que estão sendo mescladas de volta à linha de base.

A mesclagem deixa sua linha de base no ponto de ramificação original. Se você mesclar algumas semanas de alterações da linha de ramificações, agora terá muitas alterações em seu ponto de ramificação, muitas das quais estarão em sua linha de base. Isso dificulta a identificação de suas alterações no seu ramo. O envio de alterações de volta à linha de base pode gerar conflitos não relacionados às suas alterações. No caso de conflitos, é possível promover mudanças inconsistentes. As fusões contínuas exigem esforço para gerenciar e é relativamente fácil perder as alterações.

A Rebase move seu ponto de ramificação para a revisão mais recente em sua linha de base. Qualquer conflito que você encontrar será apenas para a (s) alteração (ões). Empurrando mudanças é muito mais simples. Conflitos são tratados no ramo local fazendo um rebase adicional. No caso de envios conflitantes, o último a forçar sua mudança precisará resolver o problema com sua mudança.

    
por 19.11.2013 / 03:31
fonte
3

As ferramentas automatizadas estão melhorando para garantir que o código de mesclagem seja compilado e executado, evitando conflitos sintáticos , mas eles não podem garantir a ausência de conflitos lógicos pode ser introduzido por fusões. Portanto, uma fusão bem-sucedida dá a você uma sensação de falsa confiança, quando na realidade não garante nada, e você precisa refazer todos os seus testes.

O problema real com a ramificação e fusão, como eu vejo, é que ele chuta a lata proverbial abaixo da estrada. Ele permite que você diga "Eu só trabalharei no meu próprio mundinho" por uma semana, e lide com qualquer problema que surgir depois. Mas consertar bugs é sempre mais rápido / barato quando eles são novos. No momento em que todas as ramificações de código começarem a ser mescladas, você já poderá esquecer algumas das nuances das coisas que foram feitas.

Considere os dois problemas mencionados anteriormente e você poderá se deparar com uma situação em que seja mais simples e fácil fazer com que todos trabalhem no mesmo tronco e resolver conflitos continuamente, mesmo que eles tornem o desenvolvimento ativo um pouco mais lento .

    
por 18.11.2013 / 20:49
fonte
0

Um outro ponto relevante é o seguinte: com o rebasing, posso facilmente escolher ou reverter um recurso no meu release.

    
por 28.01.2018 / 18:07
fonte