2009-03-04 13 views
53

SVN hizo la bifurcación mucho más fácil al hacer las sucursales realmente baratas, pero las fusiones siguen siendo un problema real en SVN, una que Git supuestamente resuelve.¿Cómo resuelve Git el problema de fusión?

¿Lo logra Git y cómo?

(exención de responsabilidad: Todo lo que sé sobre Git se basa en la conferencia Linus - novato total de git aquí)

+8

En respuesta a: "SVN hizo la bifurcación mucho más fácil haciendo que las ramas fueran realmente baratas". ¿Estás seguro de que no reemplazaste accidentalmente a Git con SVN? Sé que una de las características principales que Git presume es la ramificación barata ... He oído que la ramificación en SVN es una pesadilla porque gran parte de ella es manual (crea un nuevo directorio con contenido ramificado, etc.). – Stunner

Respuesta

73

Git no evitará el conflicto en las fusiones, pero puede reconciliar el historial incluso cuando no comparten ningún antecesor padre.
(a través de The grafts file (.git/info/grafts), que es una lista, uno por línea, de una confirmación seguida por sus padres, que puede modificar para ese propósito de "reconciliación")
Tan poderoso de gran alcance ahí.

Pero para realmente tener una idea de "cómo fusiones se han pensado", que can start by turning to Linus himself, y darse cuenta de que esta cuestión no se trata tanto de "algoritmo":

Linus: Me personalmente , Quiero tener algo que sea muy repetible y no inteligente. Algo que entiendo o me dice que no puede hacerlo.
Y, francamente, fusionar la historia de un solo archivo sin teniendo en cuenta el historial de todos los demás archivos me hace ir "ugh".

La parte importante de una combinación no es cómo maneja los conflictos (que deben ser verificados por un humano de todos modos si son del todo interesantes), sino que debe combinar la historia para que tenga una nueva base sólida para futuras fusiones.

En otras palabras, la parte más importante es trivial parte: el nombramiento de los padres y el seguimiento de su relación. No los enfrentamientos

Y parece que el 99% de las personas de SCM parecen pensar que la solución para eso es ser más inteligentes sobre las fusiones de contenido. Lo cual pasa por alto el punto por completo.


Así Wincent Colaiuta añade (el énfasis es mío):

No hay necesidad de metadatos de fantasía, cambiar el nombre de seguimiento y así sucesivamente.
Lo único que necesita almacenar es el estado del árbol antes y después de cada cambio.

¿Qué archivos se han cambiado de nombre? ¿Cuáles fueron copiados? ¿Cuáles fueron eliminados? ¿Qué líneas se agregaron? ¿Cuáles fueron eliminados? ¿Qué líneas tuvieron cambios dentro de ellas? ¿Qué losas de texto se copiaron de un archivo a otro?
No debería tener que preocuparse por ninguna de estas preguntas y, desde luego, no debería tener que mantener datos de seguimiento especiales para ayudarlo a responderlas: todos los cambios en el árbol (adiciones, eliminaciones, cambios de nombre, ediciones, etc.) están codificados implícitamente en el delta entre los dos estados del árbol; usted acaba de pista ¿cuál es el contenido.

Absolutamente todo puede (y debe) inferirse.

Git rompe el molde porque piensa en el contenido, no en los archivos.
No rastrea los cambios de nombre, rastrea el contenido. Y lo hace a nivel de todo un árbol.
Esta es una desviación radical de la mayoría de los sistemas de control de versiones.
No molesta tratar de almacenar historiales por archivo; en su lugar, almacena la historia en el nivel del árbol.
Cuando realiza un diff, está comparando dos árboles, no dos archivos.

La otra decisión de diseño fundamentalmente inteligente es cómo se fusiona Git.
Los algoritmos de fusión son inteligentes pero no intentan ser demasiado inteligentes. Las decisiones no ambiguas se toman automáticamente, pero cuando hay dudas, es decisión del usuario.
Esta es la forma en que debería ser. No quiere que una máquina tome esas decisiones por usted. Nunca lo querrás.
Esa es la visión fundamental en el enfoque de Git para la fusión: mientras que el resto del sistema de control de versiones intenta ser más inteligente, Git se describe felizmente como el "administrador de contenido estúpido", y es mejor para él.

+1

Esto me parece una característica destinada a ayudarte a recuperarte de los errores del pasado. Si bien eso es algo noble y bueno, realmente no te ayuda a no cometer el error en primer lugar. – RibaldEddie

+0

¿Puedes explicar mejor qué es un árbol? git n00b aquí. – hasen

+0

hansen_j, para más información sobre los árboles git, lea http://www.newartisans.com/2008/04/git-from-the-bottom-up.html –

5

Por lo que yo sé, los algoritmos que se fusionan no son más inteligentes que los de otros sistemas de control de versiones. Sin embargo, debido a la naturaleza distribuida de git, no hay necesidad de esfuerzos de fusión centralizados. Cada desarrollador puede reubicar o fusionar pequeños cambios de otros desarrolladores en su árbol en cualquier momento, por lo tanto, los conflictos que surgen tienden a ser más pequeños.

-10

Git hace que sea más difícil arruinar el repositorio de todos los demás con una combinación incorrecta.

El único beneficio real es que Git es mucho, mucho más rápido en la fusión porque todo se hace a nivel local y que está escrito en C.

SVN, se utiliza correctamente, es perfectamente utilizable.

+2

Git también difiere de manera diferente. Mira la diferencia de contenido, en lugar de un archivo por ediciones de línea de archivo. – Saem

16

Ahora se acepta generalmente ese algoritmo de fusión tridireccional (quizás con mejoras tales como detección de renombrado y manejo de historial más complicado), que tiene en cuenta la versión en la rama actual ('nuestra'), versión en La rama fusionada ('theirs'), y la versión del ancestro común de las ramas fusionadas ('ancestro') es (desde el punto de vista práctico) la mejor forma de resolver las fusiones. En la mayoría de los casos, y para la mayoría de los contenidos, la fusión de nivel de árbol (qué versión de archivo tomar) es suficiente; rara vez es necesario tratar con conflictos de contenido, y entonces el algoritmo diff3 es lo suficientemente bueno.

Para usar la combinación de 3 vías, necesita conocer el antecesor común de las ramas fusionadas (co llamado fusionar base). Para esto necesita saber completo historial entre esas ramas. Lo que faltaba antes de Subversion (versión actual) 1.5 (sin herramientas de terceros como SVK o svnmerge) era merge tracking, es decir, recordar para fusionar confirmar qué padres (qué confirma) se usaron en la fusión. Sin esta información, no es posible calcular correctamente el antecesor común en presencia de fusiones repetidas.

Toma para la cuenta el siguiente diagrama:

---.---a---.---b---d---.---1 
     \  /
     \-.---c/------.---2 

(lo que probablemente conseguir destrozado ... sería bueno tener capacidad de dibujar diagramas del arte ASCII aquí).
Cuando estábamos fusionando commits 'b' y 'c' (creando commit 'd'), el antecesor común era el punto de bifurcación, commit 'a'. Pero cuando queremos combinar commits '1' y '2', ahora el antecesor común es commit 'c'. Sin almacenar la información de fusión, tendríamos que concluir erróneamente que es commit 'a'.

Subversion (anterior a la versión 1.5), y CVS anterior, fusionó duro porque tenía que calcular antepasado común usted mismo, y proporcionar información acerca del antecesor manualmente al hacer una fusión.

Git almacena información sobre todos los padres de una confirmación (más de un padre en el caso de la fusión de confirmación) en el objeto de confirmación. De esta forma, puede decir que Git almacena DAG (gráfico acíclico directo) de revisiones, almacenando y recordando las relaciones entre commits.


(no estoy seguro de cómo Subversion trata las cuestiones que se mencionan a continuación)

Adicionalmente se fusionan en Git pueden hacer frente a dos problemas de complicaciones adicionales: archivo renombra (cuando un lado a llamarse un archivo , y otros no; queremos obtener un cambio de nombre, y queremos que se apliquen los cambios al archivo correcto) y se fusiona cruzada (historial más complicado, cuando hay más de un antepasado común).

  • del archivo cambia el nombre de durante la fusión se gestionan mediante puntuación de similitud basado en heurística (ambos similitud del contenido del archivo y la similitud de nombre de ruta se tiene en cuenta) detección de cambio de nombre. Git detecta qué archivos se corresponden entre sí en ramas fusionadas (y ancestros). En la práctica, funciona bastante bien para casos del mundo real.
  • Criss-cross fusiona, ver definition at revctrl.org wiki, (y la presencia de múltiples bases de combinación de) se gestionan mediante el uso de estrategia de combinación recursiva, que genera único ancestro común virtual.
+1

He intentado mejorar el diagrama, formaándolo como una frase en bloque ... Espero no haberlo roto debido a una comprensión insuficiente, mis disculpas en ese caso. – unwind

9

Las respuestas anteriores son todas correctas, pero creo que omiten el punto central de las fusiones fáciles de git para mí. Una fusión de SVN requiere que haga un seguimiento y recuerde qué se ha fusionado y eso es un gran PITA. A partir de sus documentos:

svn merge -r 23:30 file:///tmp/repos/trunk/vendors 

Ahora eso no es asesino, pero si se olvida de si se trata de 23-30 o 23-30 incluido exclusiva, o si ya se ha fusionado algunas de esas confirmaciones, estás manguera y Tengo que averiguar las respuestas para evitar repetir o faltar compromisos. Dios te ayude si ramas una rama.

Con git, solo se trata de git merge y todo esto sucede a la perfección, incluso si has escogido un par de veces o has hecho una cantidad de fantásticas cosas de git land.

+2

Creo que te estás olvidando del seguimiento de combinación que svn tiene desde hace poco. –

+3

es cierto, no he tenido mucha experiencia con las nuevas fusiones. Desde la distancia, parece sospechoso: "una vez que se realiza una fusión integrada de una rama a otra, la rama ya no se puede usar para seguir trabajando. No es capaz de absorber correctamente los nuevos cambios en el tronco ... mejor que nada. – jdwyah

Cuestiones relacionadas