Por que o git não mescla linhas adjacentes sem conflito?

25

Eu aprendi recentemente que, ao mesclar duas ramificações no git, se houver alterações em duas linhas adjacentes , o git declara isso um conflito. Por exemplo, se o arquivo test.txttiver este conteúdo:

Line 1: A
Line 2: B
Line 3: C
Line 4: D

e no ramo mastermudamos isso para

Line 1: A
Line 2: B1
Line 3: C
Line 4: D

enquanto no ramo testing, mudamos isso para

Line 1: A
Line 2: B
Line 3: C1
Line 4: D

e depois tentar fundir testingem master, git declara um conflito de mesclagem. Minha ingênua expectativa era que a fusão ocorresse sem conflito e produzisse o seguinte:

Line 1: A
Line 2: B1
Line 3: C1
Line 4: D

Estou certo de que há uma boa razão pela qual o git não se funde dessa maneira. Alguém pode explicar esse motivo?

rlandster
fonte
Ei, eu também notei isso na semana passada também. Talvez estivéssemos fazendo o mesmo tutorial.
detly
5
habilidades fusão do git são realmente muito pobre, IMO
James
@ James, você tentou usar o algoritmo de paciência? Acho que consigo melhores resultados com ele, principalmente quando lida com os locais em que os pedaços estão divididos (por exemplo, agarrando um corpo de função em vez de dois). Se você não gosta do git, também pode usar o seu próprio (veja blog.wuwon.id.au/2010/09/… para um exemplo).
deterb
1
A causa raiz é que o git tenta fazer a mesclagem em si, em vez de fatorá-la em uma ferramenta especializada. Inteiramente não é a filosofia do Unix. Para arquivos de origem, você pode realmente usar a gramática do idioma para determinar de maneira confiável as diferenças.
MSalters
O único contexto comum é A e D, então por que A / C1 / B1 / D não é a mesclagem correta?
precisa saber é

Respostas:

13

Supondo que esse trecho de código

x=0
x+=1 if foo
x+=1 if bar
return x

foi alterado em um ramo para este

x=0
x+=1 if foo && xyzzy
x+=1 if bar
return x

e em outro ramo neste

x=0
x+=1 if foo
x+=1 if bar && xyzzy
return x

então eu não gostaria que o git mesclasse isso

x=0
x+=1 if foo && xyzzy
x+=1 if bar && xyzzy
return x

sem me alarmar.

Para evitar causar tais problemas, o git geralmente se recusa a mesclar automaticamente as alterações que tocam nas linhas próximas. Permite verificar se a lógica do programa estaria quebrada ou não.

Este exemplo é trivial, mas ao mesclar ramificações enormes, o risco de conflitos "lógicos" semelhantes é muito maior. Às vezes, eu adoraria que o contexto fosse ainda maior do que é atualmente.

Arsen7
fonte
5
Isso não tem nada a ver com isso, basta adicionar uma linha imutável entre esses dois e de repente o git os mescla sem problemas.
Darkhogg 11/09/19
Sim, eu não recebo esta resposta. Você pode usar a mesma lógica para evitar todas as mesclagens automáticas.
Mehrdad
Deve haver uma linha traçada entre segurança e utilidade. Mesclagens automáticas correm o risco de corromper o fluxo do programa - como eu estava tentando mostrar na resposta -, mas se o git se recusasse a mesclar qualquer coisa, ele se tornaria inútil. Os criadores do git acabaram de decidir sobre algum contexto, que deve capturar a maioria dos casos "perigosos". Adicionar alguma linha inalterada (como Darkhogg descobriu) aos tolos em acreditar que a fusão pode ser seguro.
Arsen7
11

Esse comportamento é apenas para git?

Depois de discutir com um colega, tentei, e o SVN lida com isso sem problemas: você modifica as duas linhas.

Os recursos de mesclagem de vários VCS são testados aqui para bazar, darcs, git e mercurial : https://github.com/mndrix/merge-this

Parece que apenas os dardos mesclam com êxito o caso "linhas adjacentes".

A aplicação de alterações adjacentes aos arquivos não é um problema difícil. Eu realmente acho que esse comportamento foi escolhido de propósito.

Por que alguém decidiria que modificar linhas adjacentes produz um conflito?

Eu acho que isso é forçar você a olhar para ele .

int max = MAX_ITEMS;
for(unsigned int i = 0; i < max; i++)
    do_stuff(i);

Modif número 1, no mestre:

int max = MAX_ITEMS/2; // Do stuff only on the first half
for(unsigned int i = 0; i < max; i++)
    do_stuff(i);

Modif número 2, mesclado de uma filial:

int max = MAX_ITEMS;
for(unsigned int i = 0; i < max/2; i++) // max/2: only on 1st half
    do_stuff(i);

Após a mesclagem, você não deseja isso:

int max = MAX_ITEMS/2; // Do stuff only on the first half
for(unsigned int i = 0; i < max/2; i++) // max/2: only on 1st half
    do_stuff(i);

Vendo esse comportamento como um recurso

Você pode transformar o comportamento de mesclagem do git em vantagem. Quando você precisar manter duas linhas consistentes, mas não conseguir detectá-las (no momento da compilação, no início dos testes ou de outra forma), tente juntar-se a elas.

Reescreva isso ...:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i);
    // Need to do something else
    do_something_else(r);

...para isso:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i);
    do_something_else(r); // Need to do something else

Então, quando você mescla o Modif 1 ...:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i)/2; // we need only the half
    do_something_else(r); // Need to do something else

... com o Modif 2 ...:

for(unsigned int i = 0; i < max; i++)
    r = do_stuff(i);
    if(r < 0) // do_stuff can return an error
        handle_error(r);
    do_something_else(r/2); // Need to do something else

..., o git produzirá um conflito, e você forçará você a olhar para ele.

ofaurax
fonte
2
Vou apenas prosseguir e dizer que acho que sua resposta é perfeitamente razoável, mas, dependendo da interação complexa e definida da implementação entre seu código e seu controle de origem para verificação de sanidade, é uma via rápida para thedailywtf.com. Mesclar cegamente o código sem um analisador de idioma é SEMPRE o melhor esforço de qualquer maneira, e eu tive alguns casos em que o git forneceu algo útil que não deveria ter e produziu código que nem seria compilado.
Wug
5

Estou adivinhando, mas acho que tem a ver com a linha 2 ser usada como contexto para a mudança da linha 3.

O Git não pode simplesmente dizer que "A linha com C se tornou uma linha com C1" porque poderia haver outra linha com "C", então diz "A linha com C, logo após o início do arquivo, a linha com A, e a linha com B, agora é C1 "

Se "a linha com B" não estiver mais lá, parte do contexto será perdida, e o git pode dizer apenas aonde a nova linha deve ir.

Mike Gossmann
fonte
5
a sua também muito provável que C depende de B, então uma mesclagem ingênuo pode ser problemático, mesmo que git "sabe" de fazê-lo
Lucina
Acredite em mim, o Git apenas "pensa que sabe". Git é um cemitério de conceitos errados, tentando ser corrigido!
user3833732 24/10
2

As outras respostas aqui são importantes, mas para mim isso sempre pareceu uma limitação desnecessária.

Como outros já disseram, nesses casos, você definitivamente não gostaria que o Git mesclasse as linhas sem aviso.

Mas eu ainda queria a opção de fazer isso automaticamente, depois de ser avisado. Então, escrevi um driver de mesclagem git personalizado que pode mesclar conflitos em linhas adjacentes (ou individuais) interativamente:

insira a descrição da imagem aqui

Isso economiza muito tempo, pois gerencio um projeto em que as pessoas geralmente trabalham nos mesmos arquivos e refatoram muito código.

O script está disponível no GitHub sob uma licença GPLv3 +. Talvez você ache útil:

https://github.com/paulaltin/git-subline-merge

deltacrux
fonte
4
Alguém se importaria de explicar por que isso foi rebaixado? Sou bastante novo aqui, por isso, se eu fiz algo errado, gostaria de saber o que era para evitar no futuro. Percebo que meu post não responde exatamente à pergunta conforme solicitado, mas ainda é relevante e acho que a maioria das pessoas que vem aqui gostaria de saber não apenas por que o git faz isso, mas também o que eles podem fazer sobre isso (como fiz quando primeiro chegou a essa pergunta em uma pesquisa no Google).
Deltacrux 12/0918
Ainda não tentei, mas procurei uma maneira de automatizar isso e fiquei feliz em encontrá-lo aqui. Obrigado :) você é incrível.
Mehrdad
1
Sem problemas! Espero que você ache útil, e sinta-se à vontade para deixar comentários no Github se tiver problemas ou tiver alguma sugestão de melhoria. Obrigado!
deltacrux