¿Por qué los retrocesos Mercurial en una twig afectan a otras twigs?

Esta es una situación difícil de explicar, así que tengan paciencia conmigo. Tengo un repository Mercurial con 2 twigs principales, por defecto y dev .

El trabajo generalmente se realiza en una twig específica de dev (una twig de características). Puede haber muchas twigs de características en cualquier momento. Una vez que el trabajo se completa en esa twig, se fusiona de nuevo en dev .

Cuando llega el momento de preparar una versión, se crea otra twig con nombre fuera de dev (una twig de publicación). En ocasiones, es necesario excluir funciones completas de una versión. Si ese es el caso, el set de cambios de fusión desde donde se fusionó la twig de características en dev se retira de la nueva twig de publicación.

Una vez que una twig de publicación está list para ser liberada, se fusiona con la pnetworkingeterminada (por lo que el valor pnetworkingeterminado siempre representa el estado del código en producción). El trabajo continúa de forma normal en la twig de desarrollo y en las twigs de características.

El problema ocurre cuando llega el momento de hacer otra versión, incluida la function que se retiró en la versión anterior. Se crea una nueva twig de versión de forma normal (fuera de dev ). Esta nueva twig de versión ahora contiene la característica que se retiró de la twig de la versión anterior (ya que el retroceso se realizó en la twig de la versión, y el set de cambios de fusión permanece en la twig de desarrollo).

Esta vez, cuando la twig de publicación está list para su lanzamiento y se fusiona con la pnetworkingeterminada , cualquier cambio que se haya retirado como resultado de la reversión de fusión en la twig de la versión anterior no se fusionará a la pnetworkingeterminada . ¿Por qué es este el caso? Dado que la nueva twig de versión contiene todos los sets de cambios de la twig de características (no se ha restituido nada), ¿por qué la twig pnetworkingeterminada no recibe todos estos sets de cambios también?

Si todo lo anterior es difícil de seguir, aquí hay una captura de pantalla de TortoiseHg que muestra el problema básico. "branch1" y "branch2" son twigs de características, "release" y "release2" son las twigs de publicación:

enter image description here

Creo que el problema es que las fusiones funcionan de manera diferente a lo que piensas. Usted escribe

Dado que la nueva twig de versión contiene todos los sets de cambios de la twig de características (no se ha restituido nada), ¿por qué la twig pnetworkingeterminada no recibe todos estos sets de cambios también?

Cuando fusionas dos twigs, es incorrecto pensar que aplica todos los cambios de una twig a otra. Por lo tanto, la twig default no "recibe" ningún set de cambios de release2 . Sé que así es como normalmente pensamos en las fusiones, pero es inexacto.

Lo que realmente sucede cuando fusionas dos sets de cambios es el siguiente:

  1. Mercurial encuentra el ancestro común para los dos sets de cambios.

  2. Para cada file que difiere entre los dos sets de cambios, Mercurial ejecuta un algorithm de combinación de tres vías utilizando el file ancestro, el file en el primer set de cambios y el file en el segundo set de cambios.

En su caso, está fusionando las revisiones 11 y 12. El ancestro less común es la revisión 8. Esto significa que Mercurial ejecutará una fusión tripartita entre los files de sus revisiones:

  • Revisión 8: sin retroceso

  • Revisión 11: la twig de características ha sido retirada

  • Revisión 12: sin retroceso

En una combinación de tres vías, un cambio siempre prevalece sin cambios. Mercurial ve que los files se han cambiado entre 8 y 11 y no ve ningún cambio entre 8 y 12. Por lo tanto, usa la versión modificada de la revisión 11 en la fusión. Esto se aplica a cualquier algorithm de combinación de tres vías. La tabla de combinación completa se ve así, donde lo old , lo new , … son el contenido de los trozos coincidentes en los tres files:

 ancestor local other -> merge old old old old (nobody changed the hunk) old old new new (they changed the hunk) old new old new (you changed the hunk) old new new new (hunk was cherry picked onto both branches) old foo bar <!> (conflict, both changed hunk but differently) 

Me temo que un set de cambios de fusión no debería retirarse en absoluto debido a este sorprendente comportamiento de fusión. Mercurial 2.0 y versiones posteriores abortarán y se quejarán si intenta anular una combinación.

En general, se puede decir que el algorithm de fusión tridireccional asume que todo cambio es bueno . Entonces, si branch1 en dev y luego deshace la fusión con un retroceso, entonces el algorithm de fusión pensará que el estado es "mejor" que antes. Esto significa que no puede volver a fusionar branch1 en dev en un momento posterior para recuperar los cambios restituidos.

Lo que puedes hacer es usar una "combinación ficticia" cuando te fusionas en el modo default . Simplemente se fusiona y siempre mantiene los cambios de la twig de publicación que está fusionando en el default :

 $ hg update default $ hg merge release2 --tool internal:other -y $ hg revert --all --rev release2 $ hg commit -m "Release 2 is the new default" 

Eso dejará de lado el problema y forzará que el default sea ​​como release2 . Esto supone que no se realizan cambios en el default sin fusionarse en una twig de publicación.

Si debe poder hacer lanzamientos con características omitidas, entonces la manera "correcta" es no fusionar esas características. La fusión es un gran compromiso: le dices a Mercurial que el set de cambios de fusión ahora tiene todas las cosas buenas de sus antepasados. Siempre que Mercurial no le permita elegir su propia revisión base cuando se fusiona , el algorithm de combinación de tres vías no le permitirá cambiar de opinión sobre un retroceso.

Lo que puedes hacer, sin embargo, es retroceder el retroceso . Esto significa que vuelve a introducir los cambios de su twig de características en su twig de publicación. Entonces comienzas con un gráfico como

 release: ... o --- o --- m1 --- m2 / / feature-A: ... o --- o / / feature-B: ... o --- o --- o 

Ahora decidiste que la function A era mala y deshaces la fusión:

 release: ... o --- o --- m1 --- m2 --- b1 / / feature-A: ... o --- o / / feature-B: ... o --- o --- o 

A continuación, fusiona otra function en su twig de publicación:

 release: ... o --- o --- m1 --- m2 --- b1 --- m3 / / / feature-A: ... o --- o / / / / feature-B: ... o --- o --- o / / feature-C: ... o --- o --- o --- o --- o 

Si ahora quiere volver a introducir la function A, puede retroceder b1 :

 release: ... o --- o --- m1 --- m2 --- b1 --- m3 --- b2 / / / feature-A: ... o --- o / / / / feature-B: ... o --- o --- o / / feature-C: ... o --- o --- o --- o --- o 

Podemos agregar los deltas al gráfico para mostrar mejor qué cambia dónde y cuándo:

  +A +B -A +C --A release: ... o --- o --- m1 --- m2 --- b1 --- m3 --- b2 

Después de este segundo retroceso, puede fusionar nuevamente con la feature-A en caso de que se hayan agregado allí nuevos sets de cambios. El gráfico que está fusionando se ve así:

 release: ... o --- o --- m1 --- m2 --- b1 --- m3 --- b2 / / / feature-A: ... o -- a1 - a2 / / / / feature-B: ... o --- o --- o / / feature-C: ... o --- o --- o --- o --- o 

y fusionas a2 y b2 . El ancestro común será a1 . Esto significa que los únicos cambios que deberá tener en count en la combinación de tres vías son aquellos entre a1 y a2 y a1 y b2 . Aquí b2 ya tiene la mayor parte de los cambios "en" a2 por lo que la fusión será pequeña.

La respuesta de Martin es, como siempre, sobre el dinero, pero solo quería agregar mi 2p.

Otra forma de pensar sobre esto es que el retroceso no elimina nada, agrega el cambio inverso.

Entonces cuando te fusionas no estás haciendo:

 Branch after changes <-> Branch before changes => Result with changes 

estás haciendo:

 Branch after changes <-> Branch after changes with removal of changes => Result with changes removed. 

Básicamente, la primera versión se hizo mal. Sería mejor elegir las características en el lanzamiento, que include todo y las características de selección de cereza. Graft puede ayudarte aquí, pero aún no he intentado usarlo con ira para conocer todas las trampas.