¿Por qué git no combina líneas adyacentes sin conflicto?

25

Recientemente aprendí que al fusionar dos ramas en git, si hay cambios en dos líneas adyacentes , git declara que esto es un conflicto. Por ejemplo, si el archivo test.txttiene este contenido:

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

y en rama mastercambiamos esto a

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

mientras que en la rama testingcambiamos esto a

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

y luego tratar de fusionar testingen master, git declara un conflicto de combinación. Mi ingenua expectativa era que la fusión ocurriría sin conflicto y produciría esto:

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

Estoy seguro de que hay una buena razón por la cual git no se fusiona de esta manera. ¿Alguien puede explicar esta razón?

rlandster
fuente
Hola, también me di cuenta de esto la semana pasada. Tal vez estábamos haciendo el mismo tutorial.
detly
55
Las habilidades de fusión de git son bastante pobres, IMO
James
@ James, ¿has intentado usar el algoritmo de paciencia? Creo que obtengo mejores resultados con él, en particular cuando se trata de dónde se dividen los trozos (por ejemplo, agarrar un cuerpo de función en lugar de dos). Si no le gustan los git, también puede usar los suyos (consulte blog.wuwon.id.au/2010/09/… para ver un ejemplo).
determinar
1
La causa raíz es que git intenta hacer la fusión en sí, en lugar de factorizarlo en una herramienta especializada. Totalmente no es la filosofía de Unix. Para los archivos de origen, en realidad puede usar la gramática del lenguaje para determinar de manera confiable las diferencias.
MSalters
El único contexto común es A y D, entonces, ¿por qué A / C1 / B1 / D no es la combinación correcta?
Izkata

Respuestas:

13

Suponiendo que este fragmento de código

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

se ha cambiado en una rama en este

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

y en otra rama en este

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

entonces no quisiera que git lo fusionara en esto

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

sin alarmarme

Para evitar causar tales problemas, git generalmente se niega a combinar automáticamente los cambios que tocan las líneas cercanas. Le da la oportunidad de verificar si la lógica del programa se rompería o no.

Este ejemplo es trivial, pero cuando se fusionan grandes ramas, el riesgo de conflictos "lógicos" similares es mucho mayor. A veces incluso me encantaría que el contexto sea aún más grande de lo que es actualmente.

Arsen7
fuente
55
Sin embargo, eso no tiene nada que ver con eso, simplemente agregue una línea invariable entre esos dos y de repente git los combine sin ningún problema.
Darkhogg
Sí, no entiendo esta respuesta. Puede usar la misma lógica para evitar todas las fusiones automáticas.
Mehrdad
Debe haber una línea trazada entre seguridad y utilidad. Las fusiones automáticas conllevan el riesgo de corromper el flujo del programa, como estaba tratando de mostrar en la respuesta, pero si git se negara a fusionar algo, sería inútil. Los creadores de git acaban de decidir sobre algún contexto, que captará la mayoría de los casos "peligrosos". Al agregar una línea sin cambios (como descubrió Darkhogg), los tontos creen que la fusión podría ser segura.
Arsen7
11

¿Es este comportamiento solo git?

Después de discutir con un colega, acabo de intentarlo y SVN lo maneja sin problemas: se modifican las 2 líneas.

Las capacidades de fusión de varios VCS se prueban aquí para bazar, darcs, git y mercurial : https://github.com/mndrix/merge-this

Parece que solo los darcs fusionan con éxito el caso de "líneas adyacentes".

Aplicar cambios adyacentes a los archivos no es un problema difícil. Realmente creo que este comportamiento ha sido elegido a propósito.

¿Por qué alguien decidiría que modificar líneas adyacentes produce un conflicto?

Creo que esto es para obligarlo a mirarlo .

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

Modif número 1, en master:

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, fusionado de una rama:

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

Después de la fusión, no quieres eso:

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);

Ver este comportamiento como una característica

Puede convertir el comportamiento de fusión de git en una ventaja. Cuando necesite mantener 2 líneas consistentes pero no pueda detectarlo (en el momento de la compilación, al comienzo de sus pruebas o de lo contrario), puede intentar unirlas.

Reescribe esto ...:

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

...a esto:

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

Entonces, cuando fusionas 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

... con 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

..., git producirá un conflicto y te obligará a mirarlo.

ofaurax
fuente
2
Solo voy a seguir adelante y decir que creo que su respuesta es perfectamente razonable, pero dependiendo de la interacción compleja e implementada entre su código y su control de origen para la verificación de la cordura es una vía rápida a thedailywtf.com. Combinar ciegamente el código sin un analizador de idiomas es SIEMPRE el mejor esfuerzo de todos modos, y he tenido algunas instancias en las que git automatizó de manera útil algo que no debería tener y produjo código que ni siquiera se compilaría.
Wug
5

Supongo que en su mayoría, pero creo que tiene que ver con que la línea 2 se use como contexto para el cambio de la línea 3.

Git no puede decir simplemente que "La línea con C se convirtió en una línea con C1" porque podría haber otra línea con "C", por lo que dice "La línea con C, justo después del inicio del archivo, la línea con A, y la línea con B, ahora es C1 "

Si "la línea con B" ya no está allí, se pierde parte del contexto y git solo puede decir aproximadamente a dónde debe ir la nueva línea.

Mike Gossmann
fuente
55
también es muy probable que C dependa de B, por lo que una combinación ingenua puede ser problemática incluso si git "sabe cómo hacerlo"
Lucina
Créeme, Git solo "cree que lo sabe". ¡Git es un cementerio de conceptos erróneos que trata de enderezarse!
usuario3833732
2

Las otras respuestas aquí son todas correctas, pero para mí esto siempre me pareció una limitación innecesaria.

Como han dicho otros, en estos casos definitivamente no querrás que Git combine las líneas sin previo aviso.

Pero aún quería la opción de hacerlo automáticamente, después de ser advertido. Así que escribí un controlador de fusión git personalizado que podría fusionar conflictos en líneas adyacentes (o individuales) de forma interactiva:

ingrese la descripción de la imagen aquí

Me ahorra una gran cantidad de tiempo, ya que administro un proyecto donde las personas a menudo trabajan en los mismos archivos y refactorizan mucho código.

El script está disponible en GitHub bajo una licencia GPLv3 +. Tal vez lo encuentres útil:

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

deltacrux
fuente
44
¿Le importaría a alguien explicar por qué esto fue rechazado? Soy bastante nuevo aquí, así que si he hecho algo mal me gustaría saber qué fue para poder evitarlo en el futuro. Me doy cuenta de que mi publicación no responde exactamente la pregunta que se le hizo, pero sigue siendo relevante y creo que la mayoría de las personas que vienen aquí querrían saber no solo por qué Git hace esto sino también qué pueden hacer al respecto (como lo hice cuando yo primero llegó a esta pregunta de una búsqueda de Google).
deltacrux
Todavía no lo he probado, pero vine buscando una forma de automatizar esto y me alegré de encontrarlo aquí. Gracias :) eres genial.
Mehrdad
1
¡No hay problema! Espero que lo encuentre útil, y siéntase libre de dejar comentarios sobre Github si tiene problemas o tiene alguna sugerencia para mejorar. ¡Gracias!
deltacrux