Quiero saber un algoritmo exacto (o cerca de eso) detrás de 'git merge'. Las respuestas al menos a estas subpreguntas serán útiles:
- ¿Cómo detecta git el contexto de un cambio no conflictivo en particular?
- ¿Cómo descubre git que hay un conflicto en estas líneas exactas?
- ¿Qué cosas se fusionan automáticamente con git?
- ¿Cómo funciona git cuando no hay una base común para fusionar ramas?
- ¿Cómo funciona git cuando hay varias bases comunes para fusionar ramas?
- ¿Qué sucede cuando fusiono varias ramas a la vez?
- ¿Cuál es la diferencia entre las estrategias de fusión?
Pero la descripción de todo un algoritmo será mucho mejor.
Respuestas:
Puede que sea mejor que busque una descripción de un algoritmo de combinación de 3 vías. Una descripción de alto nivel sería algo como esto:
B
: una versión del archivo que sea un ancestro de las dos nuevas versiones (X
yY
), y generalmente la base más reciente (aunque hay casos en los que tendrá que retroceder más, que es uno de los las características degit
larecursive
combinación predeterminada de s )X
conB
yY
conB
.El algoritmo completo se ocupa de esto con mucho más detalle e incluso tiene algo de documentación ( https://github.com/git/git/blob/master/Documentation/technical/trivial-merge.txt para uno, junto con las
git help XXX
páginas , donde XXX es uno demerge-base
,merge-file
,merge
,merge-one-file
y posiblemente algunos otros). Si eso no es lo suficientemente profundo, siempre hay código fuente ...fuente
¿Cómo funciona git cuando hay varias bases comunes para fusionar ramas?
Este artículo fue muy útil: http://codicesoftware.blogspot.com/2011/09/merge-recursive-strategy.html (aquí está la parte 2 ).
Recursive usa diff3 de manera recursiva para generar una rama virtual que se usará como ancestro.
P.ej:
Entonces:
Hay 2 mejores ancestros comunes (ancestros comunes que no son ancestros de ningún otro)
C
yD
. Git los fusiona en una nueva rama virtualV
y luego los usaV
como base.Supongo que Git simplemente continuaría con el si hubiera más mejores ancestros comunes, fusionándose
V
con el siguiente.El artículo dice que si hay un conflicto de fusión mientras se genera la rama virtual, Git simplemente deja los marcadores de conflicto donde están y continúa.
¿Qué sucede cuando fusiono varias ramas a la vez?
Como explicó @Nevik Rehnel, depende de la estrategia, está bien explicado en la
man git-merge
MERGE STRATEGIES
sección.Solo
octopus
yours
/theirs
admite la fusión de varias ramas a la vez,recursive
por ejemplo, no.octopus
se niega a fusionarse si hubiera conflictos, yours
es una fusión trivial para que no pueda haber conflictos.Esos comandos que generan un nuevo compromiso tendrán más de 2 padres.
Hice uno
merge -X octopus
en Git 1.8.5 sin conflictos para ver cómo va.Estado inicial:
Acción:
Nuevo estado:
Como era de esperar,
E
tiene 3 padres.TODO: cómo funciona exactamente octopus en las modificaciones de un solo archivo. ¿Fusiones recursivas de dos por dos de 3 vías?
¿Cómo funciona git cuando no hay una base común para fusionar ramas?
@Torek menciona que desde 2.9, la fusión falla sin
--allow-unrelated-histories
.Lo probé empíricamente en Git 1.8.5:
a
contiene:Entonces:
a
contiene:Interpretación:
a\nc\n
como una adición de una sola líneafuente
e379fdf34fee96cd205be83ff4e71699bdc32b18
), Git ahora se niega a fusionarse si no hay una base de fusión a menos que agregue--allow-unrelated-histories
.--allow-unrelated-histories
se puede omitir si no hay rutas de archivo comunes entre las ramas que está fusionando.ours
estrategia de fusión, pero no unatheirs
estrategia de fusión.recursive
+ Latheirs
estrategia solo puede resolver dos ramas. git-scm.com/docs/git-merge#_merge_strategiesYo también estoy interesado. No sé la respuesta, pero ...
Creo que la fusión de git es muy sofisticada y será muy difícil de entender, pero una forma de abordar esto es a partir de sus precursores y centrarse en el corazón de su preocupación. Es decir, dados dos archivos que no tienen un ancestro común, ¿cómo resuelve git merge cómo fusionarlos y dónde están los conflictos?
Intentemos encontrar algunos precursores. De
git help merge-file
:De wikipedia: http://en.wikipedia.org/wiki/Git_%28software%29 -> http://en.wikipedia.org/wiki/Three-way_merge#Three-way_merge -> http: //en.wikipedia .org / wiki / Diff3 -> http://www.cis.upenn.edu/~bcpierce/papers/diff3-short.pdf
Ese último enlace es un pdf de un artículo que describe el
diff3
algoritmo en detalle. Aquí hay una versión del visor de pdf de Google . Tiene solo 12 páginas y el algoritmo tiene solo un par de páginas, pero un tratamiento matemático completo. Eso puede parecer un poco demasiado formal, pero si desea comprender la fusión de git, primero deberá comprender la versión más simple. Aún no lo he comprobado, pero con un nombre comodiff3
, probablemente también necesitará comprender diff (que usa un algoritmo de subsecuencia común más largo ). Sin embargo, puede haber una explicación más intuitiva dediff3
ahí fuera, si tiene un google ...Ahora, acabo de hacer un experimento comparando
diff3
ygit merge-file
. Se llevan a los mismos tres archivos de entrada version1 OldVersion version2 y conflictos marcan el camino mismo, con<<<<<<< version1
,=======
,>>>>>>> version2
(diff3
también tiene||||||| oldversion
), mostrando su patrimonio común.He utilizado un archivo vacío para OldVersion y archivos casi idénticos para version1 y version2 con una sola línea extra añadido a version2 .
Resultado:
git merge-file
identificó la única línea modificada como el conflicto; perodiff3
trató los dos archivos completos como un conflicto. Por lo tanto, por más sofisticada que sea diff3, la fusión de git es aún más sofisticada, incluso para el caso más simple.Aquí están los resultados reales (utilicé la respuesta de @ twalberg para el texto). Tenga en cuenta las opciones necesarias (consulte las páginas de manual correspondientes).
$ git merge-file -p fun1.txt fun0.txt fun2.txt
$ diff3 -m fun1.txt fun0.txt fun2.txt
Si está realmente interesado en esto, es una especie de madriguera. Para mí, parece tan profundo como las expresiones regulares, el algoritmo de subsecuencia común más largo de diff, gramáticas libres de contexto o álgebra relacional. Si quiere llegar al fondo, creo que puede, pero requerirá un estudio decidido.
fuente
Aquí está la implementación original
http://git.kaarsemaker.net/git/blob/857f26d2f41e16170e48076758d974820af685ff/git-merge-recursive.py
Básicamente, crea una lista de ancestros comunes para dos confirmaciones y luego los combina de forma recursiva, ya sea adelantándolos rápidamente o creando confirmaciones virtuales que se utilizan para la base de una combinación de tres vías en los archivos.
fuente
Si la misma línea ha cambiado en ambos lados de la fusión, es un conflicto; si no lo han hecho, se acepta el cambio de un lado (si existe).
Cambios que no entran en conflicto (ver arriba)
Según la definición de una base de fusión de Git , solo hay una (el último ancestro común).
Eso depende de la estrategia de fusión (solo el
octopus
yours
/theirs
admiten la fusión de más de dos ramas).Esto se explica en la página de
git merge
manual .fuente
git-merge-recursive
existe?git-merge-recursive
debería ser (no hay una página de manual y Google no arroja nada). Puede encontrar más información sobre esto en las páginas de manualgit merge
ygit merge-base
.git-merge
página de manual y lasgit-merge-base
páginas de manual que señala discuten múltiples ancestros comunes y la fusión recursiva. Siento que su respuesta está incompleta sin una discusión al respecto.