¿Cómo me acerco a una fusión complicada?

25

Aquí está el trato, me uní a una nueva compañía y me pidieron que terminara el trabajo en una sucursal que no ha sido tocada en casi un año. Mientras tanto, la rama maestra ha estado creciendo a un ritmo constante. Idealmente, me gustaría fusionar todos los cambios de la rama maestra en la rama de características y continuar el trabajo desde allí, pero no estoy muy seguro de cómo abordar esto.

¿Cómo realizo esta fusión de manera segura mientras se preservan los cambios importantes en ambos lados de la rama?

Vlad Spreys
fuente
Gracias a todos por sus excelentes comentarios. Voy a darle una oportunidad a git-imerge y si eso resulta desordenado, ¡usaré un nuevo enfoque de rama!
Vlad Spreys
¿Alguien puede explicar por qué no podemos usar git cherry-pickaquí?
Santosh Kumar
1. Oraciones. 2. rebase. 3. prueba. 4. fusionarse.
AK_
1
Prefiero cambiar de opinión en esta situación, ya que irá comprometido por compromiso. También le permitirá aplastar la nueva característica antes de que el código combinado esté disponible. YMMV.
Stephen

Respuestas:

27

En esencia, cómo combinar dos piezas de código (posiblemente no compatibles) es un problema de desarrollo , no un problema de control de versiones . El comando Git merge puede ayudar en este proceso, pero depende de la forma del problema.

Comparar ambas versiones con la base primero tiene más sentido. Esto le dará una idea de la mejor estrategia para llevar esto adelante. Su enfoque puede ser diferente según la naturaleza y la superposición de los cambios en cada rama.

Imagine el escenario ideal: descubriría que la rama principal y la rama de características solo modificaron partes mutuamente excluyentes del código, por lo que podría confirmar todos los cambios y estar listo.

Por supuesto, casi seguro que ese no será el caso, pero la pregunta es: ¿qué tan lejos estará de este escenario ideal? es decir, ¿qué tan mezclados están los cambios?

Además, ¿qué tan madura era la antigua rama característica? ¿Estaba en buen estado de funcionamiento, o no (o desconocido)? ¿Cuánto de la función se terminó?

Si el código relevante en la rama principal ha cambiado mucho en el último año, o la característica no está en un estado muy maduro, podría considerar crear una nueva bifurcación de la última e incorporar manualmente la característica anterior nuevamente. Esto le permitirá adoptar un enfoque incremental para que funcione.

Si realiza una combinación desordenada de un montón de código y no funciona, será bastante difícil de depurar. Si la rama principal ha cambiado mucho durante el año pasado, es posible que se necesiten cambios importantes en el diseño de la función para que funcione. No sería apropiado hacer estos cambios a través de "resolver conflictos", ya que esto requeriría hacer todos los cambios a la vez y esperar que funcione. Este problema se agravaría por la posibilidad de errores en la antigua rama parcialmente terminada.


fuente
+1 "Podría considerar crear una nueva bifurcación de la última e incorporar manualmente la característica anterior de nuevo"
mika
1
La primera pregunta clave es: ¿se construye la antigua rama de características? ¿Funciona? Si no, será muy difícil probar tu fusión.
Más
22

En mi experiencia limitada de git, puedo decir que a veces es más rápido reiniciar la rama de características nuevamente si el maestro se ha alejado demasiado del punto de desconexión.

Combinar dos sucursales sin conocer el historial detrás del código (dado que acaba de unirse al proyecto) es realmente difícil, y apuesto a que incluso un desarrollador que siguió el proyecto desde el principio probablemente cometerá algunos errores en la fusión.

Por supuesto, esto tiene sentido si la rama de la característica no es enorme, pero simplemente puede mantener abierta la rama de la característica anterior , volver a ramificar desde el maestro y volver a introducir manualmente los cambios que componen esa característica. Sé que es el enfoque más manual, pero le permite tener el control completo en caso de que falte o se mueva el código.

La programación de pareja con un senior en este caso sería el mejor de los casos, ayudándole a conocer mejor el código.
¡Incluso podría resultar más rápido también, si tiene en cuenta los conflictos de fusión y el tiempo de prueba!

Asumí que al menos intentar hacer una fusión es obviamente lo mejor que se puede hacer. Si eso falla o resulta ser demasiado difícil, intente la selección de cereza, si eso sale mal, siga el camino manual.

GavinoGrifoni
fuente
2
Definitivamente no es el enfoque correcto.
Andy
12
no, este es el enfoque correcto: si tiene una rama antigua y no conoce el código, tratar de fusionar no tendrá mucho éxito y será muy riesgoso. Determinar los cambios que se realizaron originalmente, investigar si son relevantes para el nuevo código y luego aplicarlos para que tengan sentido es la forma de hacerlo. Este es un enfoque manual, pero en estas circunstancias, el único seguro para tomar. Por supuesto, todavía intentaría una fusión primero, solo para ver qué sucede, y revisaría el registro para ver cuánto cambio también se produjo en la rama, podría ser trivial.
gbjbaanb
10
@DavidPacker: No creo que GavianoGrifoni sugiera tirar todo el trabajo por la borda. Sugiere transferir los cambios de la rama anterior manualmente a la línea de desarrollo actual, de manera paso a paso. Eso tirará la vieja historia , no más.
Doc Brown
3
@DavidPacker 1er: la sucursal tiene un año desactualizado, 2do: el tipo encargado de terminarlo no conoce el código en absoluto. Dados estos 2 factores, una nueva aplicación manual es la única forma realista de abordar la tarea. Nadie sugiere una simple copia y pegue de la revisión de la punta de la rama anterior.
gbjbaanb
55
@DavidPacker: los conflictos de fusión pueden volverse malvados, si tiene que resolver 500 de ellos a la vez antes de volver a poner el programa en un estado compilable y comprobable. Ese es el tipo de situación que el OP espera aquí. Si cree que es posible usar git de manera eficiente para evitar esta situación de "todo o nada", ¿por qué no edita su respuesta y le dice al OP cómo se puede lograr esto?
Doc Brown
16

git-imerge está diseñado exactamente para este propósito. Es una herramienta git que proporciona un método para la fusión incremental . Al fusionarse de forma incremental, solo necesita lidiar con las colisiones entre dos versiones, nunca más. Además, se puede realizar un número mucho mayor de fusiones automáticamente ya que los conjuntos de cambios individuales son más pequeños.

Joe
fuente
6

Intentar fusionar la cabeza de la línea principal en una rama rancia de un año puede ser un ejercicio de frustraciones y profundizar la abolladura en el escritorio con la frente.

La línea principal no llegó a donde está de una vez en el transcurso de los meses. También tuvo desarrollo y lanzamientos. Intentar actualizarlo todo en una fusión monolítica puede ser abrumador.

En su lugar, comience fusionándose desde la primera característica hasta la línea principal después de la división de la rama obsoleta. Haz que la fusión funcione. Luego, la siguiente característica se fusiona. Y así. Muchas de esas fusiones de características se fusionarán sin conflicto. Todavía es importante asegurarse de que la funcionalidad actual de la rama obsoleta sigue siendo compatible con la dirección en que se ha ido la línea principal.

Es posible que desee ramificarse desde la cabeza de la rama obsoleta para el papel de fusionarse en otros cambios. Se trata más de asegurarse de que las confirmaciones y el historial cuando alguien mira hacia atrás es claro y comunica cuál es el papel y la política de cada rama. La rama rancia era una rama característica. El que está trabajando es una rama de acumulación y reconciliación.

Gran parte de esto será más fácil si las ramas de características o versiones anteriores todavía existen y son de fácil acceso (algunos lugares tienen la política de limpiar los nombres de las ramas que son más antiguas que alguna fecha para que la lista de ramas no sea abrumadora )

Lo importante en todo esto es asegurarse de probar y corregir después de fusionar con éxito cada parte del historial de la línea principal en su lugar. Aunque algo puede fusionarse sin conflictos, eso solo significa que el código no entró en conflicto. Si la forma en que se accedió a la característica obsoleta fue desaprobada o eliminada, es posible que deba haber correcciones después de la fusión exitosa.

Además, esto también funciona para otros sistemas de control de versiones. Ocasionalmente he tenido que fusionar un grupo específico de confirmaciones svn en una rama (selección de cereza) para una característica, arreglar la rama para que funcione con esa característica y luego fusionar el siguiente grupo de confirmaciones svn en lugar de simplemente hacer una svn mayorista unir.

Si bien uno puede hacer una selección de cereza de git aquí, y permite incorporar compromisos específicos , esto tiene algunas desventajas que pueden complicar el proceso. La selección de cereza no mostrará información sobre la confirmación que seleccionó (puede agregarla al mensaje de confirmación). Esto hace que el seguimiento de las confirmaciones en el historial sea más difícil.

Además, significa que no volverá a reproducir efectivamente el maestro en la rama obsoleta, sino que elegirá características posiblemente incompletas, y esas características pueden reproducirse fuera de orden.

La razón clave por la que uno debe fusionarse de las confirmaciones históricas para dominar en la rama obsoleta es para poder mantenerla, llamémosla "historia futura" de la rama obsoleta en un estado sobre el que pueda razonar. Puede ver claramente las fusiones del historial en la rama obsoleta y las soluciones para reintegrar la funcionalidad. Las características se agregan en el mismo orden en que fueron dominadas. Y cuando termines, y finalmente hagas la fusión del jefe del maestro con la rama rancia, sabrás que todo se ha fusionado y no te faltarán confirmaciones.


fuente
+1 Esta es una solución alternativa potencial interesante que utiliza la combinación para incorporar grandes cambios. Sin embargo, puedo ver una desventaja: suponga que tiene versiones principales ABCDE y desea incorporar la rama de características A1 en E. Puede haber un gran esfuerzo desperdiciado fusionando el código en BC y D. Por ejemplo, ¿qué pasaría si D a E fuera ¿Un gran cambio de diseño que hizo que los cambios incrementales en B, C y D fueran irrelevantes? Además, nuevamente depende de cuán madura fue la función en primer lugar. Un enfoque potencial útil, pero su idoneidad debe considerarse antes de comenzar.
1

Paso 1. Conozca el código, analice su arquitectura y los cambios que se han realizado en ambas ramas desde el último antepasado común.

Paso 2. Si la función parece ampliamente independiente y toca principalmente diferentes áreas de código, fusionar, solucionar conflictos, probar, corregir, etc. Este es el camino feliz, está bastante bien para ir. De lo contrario, vaya al paso 3

Paso 3. Analice las áreas de conflicto, comprenda el impacto funcional y las razones en detalle. Fácilmente podría haber conflictos en los requisitos comerciales que salgan a la luz aquí. Discuta con los BA, otros desarrolladores, según corresponda. Obtenga una idea de la complejidad que implica resolver la interferencia.

Paso 4. A la luz de lo anterior, decida si desea fusionar / seleccionar / incluso cortar-pegar solo aquellas partes que no entren en conflicto y reescriban las piezas en conflicto, O si reescribir toda la característica desde cero .

Brad Thomas
fuente
0

1. Cambie a la rama que se utiliza como una rama principal de desarrollador / lanzamiento.

Esta es la rama que contiene los últimos cambios en el sistema. Puede ser master, core, dev, que depende de la empresa. En su caso, probablemente sea masterdirectamente.

git checkout master
git pull

Tire para asegurarse de tener la última versión de la rama de desarrollo principal adquirida.

2. Pague y extraiga la rama que contiene el trabajo que debe terminar.

Tire para asegurarse de que tiene los últimos contenidos de la rama. Al verificarlo directamente, sin crearlo localmente primero, se asegura de no tener los nuevos contenidos de master(o la rama de desarrollo principal, respectivamente) en él.

git checkout <name of the obsolete branch>
git pull origin <name of the obsolete branch>

3. Combine la rama de desarrollo principal con la rama obsoleta.

Antes de ejecutar el siguiente comando, asegúrese de escribir git brancho git statusque esté en la rama obsoleta.

git merge master

El git mergecomando intentará fusionar el contenido de la rama especificada, en este caso master, con la rama en la que se encuentra actualmente.

El énfasis en lo intentará . Puede haber conflictos de fusión, que solo usted y usted deberán resolver.

4. Arregle los conflictos de fusión, comprométase y empuje el arreglo

Después de arreglar el conflicto de fusión en todos los archivos donde hay, realice, comprometa y empuje la resolución del conflicto origin.

git add .
git commit -m "fixed the merge conflict from the past year to update the branch"
git push

Generalmente puede llamar git add .para organizar todos los archivos para confirmar. Cuando se trata de conflictos de fusión, desea que se actualicen todos los archivos necesarios.

Nota adicional

Resolver conflictos de fusión puede ser un trabajo tedioso. Especialmente si eres nuevo en una empresa. Es posible que aún no tenga el conocimiento adecuado para resolver todos los conflictos de fusión solo.

Tómese su tiempo para inspeccionar cuidadosamente todos los conflictos que ocurrieron y solucionarlos adecuadamente, antes de continuar su trabajo.


Puede suceder, por lo que comienza a trabajar en una sucursal de un año de antigüedad, fusiona el estado de desarrollo actual en ella y no tendrá ningún conflicto de fusión.

Esto sucede cuando, aunque el sistema ha cambiado mucho en el año, nadie ha tocado los archivos que en realidad se modificaron en la rama de un año.

Andy
fuente
66
# 4 es potencialmente el problema. Si hubo muchos cambios durante el año pasado, podrían ser necesarios cambios importantes en la función anterior. Hacer esto por fusión requiere que realice cambios importantes en el código de una vez y espere que funcione, lo cual no es una buena práctica de desarrollo. Además, ¿quién sabe cuál era el estado de la característica inacabada? Puede terminar con una gran cantidad de código que no funciona, y ¿quién sabe si se debió a problemas con el original o los cambios que realizó?
David, mientras este enfoque estándar funcione, está bien, y el OP debería intentarlo primero. Pero definitivamente existe el riesgo de tener demasiados conflictos de fusión en la situación descrita para manejarlos de esta manera "todo o nada".
Doc Brown
@ dan1111 Que hay conflictos de fusión está completamente bien, y de hecho, superarlos es el camino a seguir. Dado que la sucursal se dejó intacta durante un año, puede estar bastante seguro de que no es nada tan importante y que no afectará gran parte del sistema. Entonces, a pesar de que la sucursal tiene un año de retraso, puede obtener tan solo 2 o ninguno de los conflictos de fusión.
Andy
44
La suposición de que esta rama no es importante es injustificada. Podría haber sido un cambio de diseño fundamental que se abandonó y ahora se está retomando. Podría ser cualquier cosa. Tiene razón en que podría ser un asunto simple y podría haber pocos o ningún conflicto, en cuyo caso su respuesta sería correcta. Pero esa no es la única posibilidad.
@ dan1111 Si alguien no ha tocado un lugar destacado en una sucursal separada durante un año, no se reflejará tanto en los cambios del sistema. Esto proviene de mi propia experiencia con sucursales obsoletas (más de 6 meses).
Andy