He escuchado en algunos lugares que una de las razones principales por las que brillan los sistemas de control de versiones distribuidas es la fusión mucho mejor que en herramientas tradicionales como SVN. ¿Esto se debe realmente a diferencias inherentes en la forma en que funcionan los dos sistemas, o las implementaciones específicas de DVCS como Git / Mercurial solo tienen algoritmos de fusión más inteligentes que SVN?
400
Respuestas:
La afirmación de por qué la fusión es mejor en un DVCS que en Subversion se basó en gran medida en cómo la ramificación y la fusión funcionaron en Subversion hace un tiempo. Subversion anterior a 1.5.0 no almacenaba ninguna información sobre cuándo se fusionaron las sucursales, por lo tanto, cuando deseaba fusionar, tenía que especificar qué rango de revisiones debía fusionar.
Entonces, ¿por qué las fusiones de Subversion apestan ?
Medita este ejemplo:
Cuando queremos fusionar los cambios de b1 en el tronco, emitimos el siguiente comando, mientras estamos parados en una carpeta que tiene el tronco extraído:
... que intentará fusionar los cambios
b1
en su directorio de trabajo local. Y luego confirma los cambios después de resolver cualquier conflicto y probar el resultado. Cuando confirme el árbol de revisión se vería así:Sin embargo, esta forma de especificar rangos de revisiones se pierde rápidamente cuando el árbol de versiones crece, ya que Subversion no tenía metadatos sobre cuándo y qué revisiones se fusionaron. Medita sobre lo que sucede después:
Esto es en gran parte un problema por el diseño del repositorio que tiene Subversion, para crear una rama necesita crear un nuevo directorio virtual en el repositorio que albergará una copia del tronco pero no almacena ninguna información sobre cuándo y qué las cosas se fusionaron nuevamente. Eso conducirá a desagradables conflictos de fusión a veces. Lo que era aún peor es que Subversion usaba la fusión bidireccional por defecto, lo que tiene algunas limitaciones paralizantes en la fusión automática cuando dos cabezas de rama no se comparan con su antepasado común.
Para mitigar esta Subversión ahora almacena metadatos para ramificar y fusionar. Eso resolvería todos los problemas, ¿verdad?
Y, por cierto, Subversion todavía apesta ...
En un sistema centralizado, como la subversión, los directorios virtuales apestan. ¿Por qué? Porque todos tienen acceso para verlos ... incluso los de basura experimentales. La ramificación es buena si quieres experimentar pero no quieres ver la experimentación de todos y sus tías . Este es un ruido cognitivo grave. Cuantas más ramas agregue, más basura podrá ver.
Cuantas más ramas públicas tenga en un repositorio, más difícil será hacer un seguimiento de todas las diferentes ramas. Entonces, la pregunta que tendrá es si la rama aún está en desarrollo o si está realmente muerta, lo cual es difícil de distinguir en cualquier sistema de control de versiones centralizado.
La mayoría de las veces, por lo que he visto, una organización usará por defecto una gran sucursal de todos modos. Lo cual es una pena porque a su vez será difícil hacer un seguimiento de las versiones de prueba y lanzamiento, y cualquier otra cosa buena proviene de la ramificación.
Entonces, ¿por qué los DVCS, como Git, Mercurial y Bazaar, son mejores que Subversion en la ramificación y fusión?
Hay una razón muy simple: la ramificación es un concepto de primera clase . No hay directorios virtuales por diseño y las ramas son objetos duros en DVCS que deben ser tales para poder trabajar simplemente con la sincronización de repositorios (es decir, push and pull ).
Lo primero que debe hacer cuando trabaja con un DVCS es clonar repositorios (git's
clone
, hg'sclone
y bzr'sbranch
). La clonación es conceptualmente lo mismo que crear una rama en el control de versiones. Algunos llaman a esto bifurcación o bifurcación (aunque este último también se usa a menudo para referirse a sucursales ubicadas conjuntamente), pero es exactamente lo mismo. Cada usuario ejecuta su propio repositorio, lo que significa que tiene una ramificación por usuario .La estructura de la versión no es un árbol , sino un gráfico . Más específicamente, un gráfico acíclico dirigido (DAG, que significa un gráfico que no tiene ningún ciclo). Realmente no es necesario profundizar en los detalles de un DAG que no sea cada confirmación tiene una o más referencias principales (en lo que se basa la confirmación). Entonces, los siguientes gráficos mostrarán las flechas entre las revisiones en reversa debido a esto.
Un ejemplo muy simple de fusión sería este; imagine un repositorio central llamado
origin
y una usuaria, Alice, clonando el repositorio en su máquina.Lo que sucede durante un clon es que todas las revisiones se copian a Alice exactamente como estaban (lo que se valida por los hash-id identificables de forma única) y marca dónde están las ramas del origen.
Luego, Alice trabaja en su repositorio, se compromete en su propio repositorio y decide impulsar sus cambios:
La solución es bastante simple, lo único que
origin
debe hacer el repositorio es tomar todas las revisiones nuevas y mover su rama a la revisión más reciente (que git llama "avance rápido"):El caso de uso, que ilustré arriba, ni siquiera necesita fusionar nada . Entonces, el problema realmente no es con los algoritmos de fusión, ya que el algoritmo de fusión de tres vías es prácticamente el mismo entre todos los sistemas de control de versiones. El problema es más sobre la estructura que cualquier otra cosa .
Entonces, ¿qué tal si me muestras un ejemplo que tiene una fusión real ?
Es cierto que el ejemplo anterior es un caso de uso muy simple, así que hagamos uno mucho más retorcido, aunque más común. ¿Recuerdas que
origin
comenzó con tres revisiones? Bueno, el tipo que los hizo, vamos a llamarlo Bob , ha estado trabajando por su cuenta e hizo un compromiso en su propio repositorio:Ahora Bob no puede enviar sus cambios directamente al
origin
repositorio. La forma en que el sistema detecta esto es verificando si las revisiones de Bob descienden directamente deorigin
las de él, lo que en este caso no. Cualquier intento de presionar dará como resultado que el sistema diga algo parecido a " Uh ... me temo que no puedo dejar que hagas eso Bob ".Entonces Bob tiene que ingresar y luego fusionar los cambios (con git
pull
; o hg'spull
ymerge
; o bzr'smerge
). Este es un proceso de dos pasos. Primero Bob tiene que buscar las nuevas revisiones, que las copiarán tal como están desde elorigin
repositorio. Ahora podemos ver que el gráfico diverge:El segundo paso del proceso de extracción es fusionar los consejos divergentes y confirmar el resultado:
Esperemos que la fusión no tenga conflictos (si los anticipa, puede hacer los dos pasos manualmente en git con
fetch
ymerge
). Lo que más adelante debe hacerse es introducir esos cambios nuevamenteorigin
, lo que dará como resultado una fusión de avance rápido ya que la confirmación de fusión es un descendiente directo de lo último en elorigin
repositorio:Hay otra opción para fusionarse en git y hg, llamada rebase , que moverá los cambios de Bob a los cambios más recientes. Como no quiero que esta respuesta sea más detallada, te dejaré leer los documentos de git , mercurial o bazar sobre eso.
Como ejercicio para el lector, intente dibujar cómo funcionará con otro usuario involucrado. Se hace de manera similar al ejemplo anterior con Bob. La fusión entre repositorios es más fácil de lo que parece porque todas las revisiones / confirmaciones son identificables de forma exclusiva.
También está el problema de enviar parches entre cada desarrollador, que fue un gran problema en Subversion que se mitiga en git, hg y bzr mediante revisiones identificables de forma única. Una vez que alguien ha fusionado sus cambios (es decir, ha realizado una confirmación de fusión) y lo envía para que todos los demás en el equipo lo consuman, ya sea presionando a un repositorio central o enviando parches, entonces no tienen que preocuparse por la fusión, porque ya sucedió . Martin Fowler llama a esta forma de trabajar la integración promiscua .
Debido a que la estructura es diferente de Subversion, al emplear un DAG, permite que la ramificación y la fusión se realicen de una manera más fácil no solo para el sistema sino también para el usuario.
fuente
Históricamente, Subversion solo ha podido realizar una fusión bidireccional directa porque no almacena ninguna información de fusión. Esto implica tomar un conjunto de cambios y aplicarlos a un árbol. Incluso con la información de fusión, esta sigue siendo la estrategia de fusión más utilizada.
Git usa un algoritmo de fusión de 3 vías por defecto, que implica encontrar un antepasado común para las cabezas que se fusionan y hacer uso del conocimiento que existe en ambos lados de la fusión. Esto permite que Git sea más inteligente para evitar conflictos.
Git también tiene un código sofisticado para cambiar el nombre, lo que también ayuda. Que no almacenar conjuntos de cambios o almacenar cualquier información de seguimiento - sólo se almacena el estado de los archivos en cada confirmación y utiliza la heurística para localizar y renombrar los movimientos de código según sea necesario (el almacenamiento en disco es más complicado que esto, pero la interfaz se presenta a la capa lógica no expone seguimiento).
fuente
En pocas palabras, la implementación de fusión se realiza mejor en Git que en SVN . Antes de 1.5 SVN no registraba una acción de fusión, por lo que era incapaz de realizar futuras fusiones sin la ayuda del usuario que necesitaba proporcionar información que SVN no registró. Con 1.5 mejoró, y de hecho el modelo de almacenamiento SVN es ligeramente más capaz que el DAG de Git. Pero SVN almacenó la información de fusión en una forma bastante enrevesada que permite que las fusiones tomen masivamente más tiempo que en Git: he observado factores de 300 en el tiempo de ejecución.
Además, SVN afirma rastrear los cambios de nombre para ayudar a las fusiones de archivos movidos. Pero en realidad todavía los almacena como una copia y una acción de eliminación separada, y el algoritmo de fusión aún se topa con ellos en situaciones de modificación / cambio de nombre, es decir, cuando un archivo se modifica en una rama y cambia el nombre en la otra, y esas ramas son para ser fusionado Tales situaciones seguirán produciendo conflictos de fusión espurios y, en el caso de los cambios de nombre de directorio, incluso conducirán a una pérdida silenciosa de modificaciones. (Las personas SVN tienden a señalar que las modificaciones aún están en el historial, pero eso no ayuda mucho cuando no están en un resultado de fusión donde deberían aparecer.
Git, por otro lado, ni siquiera rastrea los cambios de nombre, sino que los resuelve después del hecho (en el momento de la fusión), y lo hace bastante mágicamente.
La representación de fusión SVN también tiene problemas; en 1.5 / 1.6 podría fusionarse de tronco a rama tantas veces como quisiera, automáticamente, pero era necesario anunciar una fusión en la otra dirección (
--reintegrate
), y dejar la rama en un estado inutilizable. Mucho más tarde descubrieron que este no es realmente el caso, y que a)--reintegrate
se puede resolver automáticamente, yb) son posibles fusiones repetidas en ambas direcciones.Pero después de todo esto (que en mi humilde opinión muestra una falta de comprensión de lo que están haciendo), sería (OK, estoy) muy precavido para usar SVN en cualquier escenario de ramificación no trivial, e idealmente trataría de ver qué piensa Git El resultado de la fusión.
Otros puntos señalados en las respuestas, como la visibilidad global forzada de las sucursales en SVN, no son relevantes para fusionar capacidades (sino para usabilidad). Además, el 'Git almacena cambios mientras que las tiendas SVN (algo diferente)' están en su mayoría fuera del punto. Conceptualmente, Git almacena cada confirmación como un árbol separado (como un archivo tar ), y luego usa bastante heurística para almacenar eso de manera eficiente. Calcular los cambios entre dos confirmaciones es independiente de la implementación de almacenamiento. Lo que es cierto es que Git almacena el DAG histórico en una forma mucho más sencilla que SVN hace su mergeinfo. Cualquiera que intente entender esto último sabrá a qué me refiero.
En pocas palabras: Git usa un modelo de datos mucho más simple para almacenar revisiones que SVN y, por lo tanto, podría poner mucha energía en los algoritmos de fusión reales en lugar de tratar de hacer frente a la representación => fusión prácticamente mejor.
fuente
Una cosa que no se ha mencionado en las otras respuestas, y que realmente es una gran ventaja de un DVCS, es que puede comprometerse localmente antes de impulsar sus cambios. En SVN, cuando tuve algún cambio, quería registrarme y, mientras tanto, alguien ya había hecho una confirmación en la misma rama, esto significaba que tenía que hacer una
svn update
antes de poder comprometerme. Esto significa que mis cambios, y los cambios de la otra persona ahora se mezclan, y no hay forma de abortar la fusión (como congit reset
ohg update -C
), porque no hay compromiso para volver. Si la fusión no es trivial, esto significa que no puede continuar trabajando en su función antes de haber limpiado el resultado de la fusión.Pero entonces, tal vez eso sea solo una ventaja para las personas que son demasiado tontas para usar ramas separadas (si no recuerdo mal, solo teníamos una rama que se usó para el desarrollo en la compañía donde usé SVN).
fuente
EDITAR: Esto aborda principalmente esta parte de la pregunta:
¿Esto se debe realmente a diferencias inherentes en la forma en que funcionan los dos sistemas, o las implementaciones específicas de DVCS como Git / Mercurial solo tienen algoritmos de fusión más inteligentes que SVN?
TL; DR: esas herramientas específicas tienen mejores algoritmos. Ser distribuido tiene algunos beneficios de flujo de trabajo, pero es ortogonal a las ventajas de fusión.
EDICIÓN FINAL
Leí la respuesta aceptada. Simplemente está mal.
La fusión de SVN puede ser un dolor, y también puede ser engorroso. Pero, ignore cómo funciona realmente por un minuto. No hay información que Git conserve o pueda derivar que SVN no conserve o pueda derivar. Más importante aún, no hay ninguna razón por la cual mantener copias separadas (a veces parciales) del sistema de control de versiones le proporcionará más información real. Las dos estructuras son completamente equivalentes.
Suponga que quiere hacer "algo inteligente" Git es "mejor en". Y tu cosa está registrada en SVN.
Convierta su SVN en la forma equivalente de Git, hágalo en Git y luego verifique el resultado, tal vez usando confirmaciones múltiples, algunas ramas adicionales. Si puede imaginar una forma automatizada de convertir un problema SVN en un problema Git, entonces Git no tiene una ventaja fundamental.
Al final del día, cualquier sistema de control de versiones me permitirá
Además, para fusionar también es útil (o crítico) saber
Mercurial , Git y Subversion (ahora de forma nativa, anteriormente usando svnmerge.py) pueden proporcionar los tres datos. Para demostrar algo fundamentalmente mejor con DVC, señale una cuarta información que está disponible en Git / Mercurial / DVC no disponible en SVN / VC centralizado.
¡Eso no quiere decir que no sean mejores herramientas!
fuente
git merge-base
. Con git, puedes decir "ramas a y b divididas en la revisión x". Pero svn almacena "los archivos se copiaron de foo a bar", por lo que debe usar la heurística para determinar que la copia a barra estaba creando una nueva rama en lugar de copiar archivos dentro de un proyecto. El truco es que una revisión en svn se define por el número de revisión y la ruta base. Aunque es posible suponer "tronco" la mayor parte del tiempo, muerde si realmente hay ramas.SVN rastrea archivos mientras que Git rastrea cambios de
contenido. Es lo suficientemente inteligente como para rastrear un bloque de código que se refactorizó de una clase / archivo a otro. Utilizan dos enfoques diferentes completos para rastrear su fuente.Todavía uso SVN en gran medida, pero estoy muy satisfecho con las pocas veces que he usado Git.
Una buena lectura si tienes tiempo: por qué elegí Git
fuente
Acabo de leer un artículo en el blog de Joel (lamentablemente el último). Este es sobre Mercurial, pero en realidad habla sobre las ventajas de los sistemas de VC distribuidos como Git.
Lee el artículo aquí .
fuente