tutorial mercurial merge dvcs branch

mercurial repository tutorial



Retrocediendo una fusión hacia atrás en Mercurial (5)

¿Cómo revertir el efecto de una fusión en ramas polarizadas sin morir de agonía?

Este problema me ha estado plagando durante meses y finalmente me he dado por vencido.

Tienes 1 Repositorio, con 2 Ramas con nombre . A y B.

Los cambios que ocurren en A inevitablemente ocurrirán en B.

Los cambios que ocurren directamente en B NUNCA deben ocurrir en A.

En tal configuración, fusionar "B" en "A" produce un problema grave en el repositorio, ya que todos los cambios a B aparecen en A como si estuvieran hechos en A.

La única forma "normal" de recuperarse de esta situación parece ser "anular" la fusión, es decir:

hg up -r A hg backout -r BadMergeRev --parent BadMergerevBeforeOnA

Que se ve bien y elegantemente, hasta que decida fusionarse más tarde en la dirección correcta, y termina con toda clase de cosas desagradables sucediendo y el código que fue borrado / comentado específicamente en la rama B repentinamente se borra o descomenta.

Hasta ahora, no ha habido una solución viable viable que no sea "dejar que haga lo suyo, y luego arreglar todos los problemas" y para ser sincero es un poco fubar.

Aquí hay una imagen que aclara el problema:

[Imagen original perdida]

Los archivos C y E (o cambios C y E) deben aparecer solo en la rama b, y no en la rama a. La revisión A9 aquí (sucursal a, revno 9) es el comienzo del problema.

Las revisiones A10 y A11 son las fases "Combinación de retroceso" y "fusión de restitución".

Y la revisión B12 es mercurial, erróneamente arrojando repetidamente un cambio que no debía descartarse.

Este dilema ha causado mucha frustración y humo azul y me gustaría ponerle fin.

Nota

Puede ser la respuesta obvia intentar prohibir la fusión inversa, ya sea con anzuelos o con políticas. He descubierto que la capacidad de reprimir esto es bastante alta y la posibilidad de que ocurra es tan probable que, incluso con contramedidas, todavía debes supongamos que, inevitablemente, sucederá para que pueda resolverlo cuando lo haga.

Elaborar

En el modelo, he usado archivos separados. Estos hacen que el problema suene simple. Estos simplemente representan cambios arbitrarios que podrían ser una línea separada.

Además, para colmo de males, se han producido cambios sustanciales en la rama A que deja el problema pendiente "hacer los cambios en la rama A en conflicto con los cambios en la rama B que acaba de aparecer (y se retiró) que parece un cambio en la rama A en su lugar "

En trucos de reescritura de la historia:

El problema con todas estas soluciones retroactivas es el siguiente:

  1. Tenemos 9000 commits.
  2. La clonación recién lleva media hora
  3. Si existe incluso un clon malo del repositorio en algún lugar , existe la posibilidad de que vuelva a estar en contacto con el repositorio original y lo golpee de nuevo.
  4. Todos ya han clonado este repositorio, y ahora han pasado varios días con confirmaciones continuas.
  5. Uno de esos clones es un sitio en vivo, por lo que "borrar ese y comenzar desde cero" = "big nono"

(Lo admito, muchos de los anteriores son un poco tontos, pero están fuera de mi control).

Las únicas soluciones que son viables son las que suponen que las personas pueden y van a hacer todo mal, y que hay una manera de ''deshacer'' esta incorrección.


¿Entonces quieres fusionar solo algunos conjuntos de cambios de B a A? Restringir conjuntos de cambios como lo que has estado haciendo es una muy mala idea ya que ya has sufrido.

Debe usar la extensión de trasplante o tener una tercera rama donde realice cambios comunes para fusionarse en A y B.


Creo que encontré una solución que soluciona de forma permanente la mala fusión y que no requiere que compruebe manualmente los diffs. El truco es volver atrás en la historia y generar compromisos paralelos a la combinación incorrecta.

Así que tenemos repositorio con ramas separadas por versión mantenida de un solo producto. Al igual que la situación planteada en la pregunta, todos los cambios realizados en una rama de una versión anterior (es decir, las correcciones de errores en esa versión) deben eventualmente fusionarse con las ramas de las versiones posteriores.

Entonces, específicamente, si algo está registrado en BRANCH_V8, debe estar combinado a BRANCH_V9.

Ahora uno de los desarrolladores comete el siguiente error: fusiona todos los cambios de BRANCH_V9 a BRANCH_V8 (es decir, una fusión en la dirección incorrecta). Además, después de esa mala fusión, realiza algunos commits adicionales antes de que advierta su error.

Entonces la situación es como se muestra en el registro gráfico a continuación.

o BRANCH_V8 - 13 - important commit right after the bad merge | o BRANCH_V8 - 12 - wrong merge from BRANCH_V9 |/ | o BRANCH_V8 - 11 - adding comment on BRANCH_V8 (ie. last known good state) | | o | BRANCH_V9 - 10 - last commit on BRANCH_V9 | |

Podemos solucionar este error de la siguiente manera:

  1. actualice su directorio local al último buen estado de BRANCH_V8: hg update 11
  2. Crea un nuevo hijo de ese último buen estado:
    1. cambie algún archivo $EDITOR some/file.txt (esto es necesario porque Mercurial no permite commits vacíos)
    2. confirmar estos cambios hg commit -m "generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9"
      La situación ahora se ve de la siguiente manera:

      o BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9 | | o BRANCH_V8 - 13 - important commit right after the bad merge | | | o BRANCH_V8 - 12 - wrong merge from BRANCH_V9 |/| o | BRANCH_V8 - 11 - adding comment on BRANCH_V8 | | | o BRANCH_V9 - 10 - last commit on BRANCH_V9

  3. Combina el encabezado recién generado con la revisión en la que ocurrió la fusión incorrecta, y desecha todos los cambios antes de cometer. ¡No mezcles simplemente las dos cabezas, porque luego perderás el compromiso importante que sucedió después de la fusión también!

    1. fusionar: hg merge 12 (ignorar cualquier conflicto)
    2. deseche todos los cambios: hg revert -a --no-backup -r 14
    3. cometer los cambios: hg commit -m "throwing away wrong merge from BRANCH_V9" La situación ahora se ve así:

      o BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9 |/ | o BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9 | | +---o BRANCH_V8 - 13 - important commit right after the bad merge | | o | BRANCH_V8 - 12 - wrong merge from BRANCH_V9 |/| | o BRANCH_V8 - 11 - adding comment on BRANCH_V8 | | o | BRANCH_V9 - 10 - last commit on BRANCH_V9 | |

    Es decir. hay dos cabezales en BRANCH_V8: uno que contiene la corrección de la fusión incorrecta, y el otro que contiene la confirmación importante sobrante en BRANCH_V8 que ocurrió justo después de la fusión.

  4. Combina las dos cabezas en BRANCH_V8:
    1. fusionar: hg merge
    2. commit: hg commit -m "merged two heads used to revert from bad merge"

La situación al final en BRANCH_V8 ahora está corregida, y se ve así:

o BRANCH_V8 - 16 - merged two heads used to revert from bad merge |/ | o BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9 | |/ | | o BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9 | | | o | | BRANCH_V8 - 13 - important commit right after the bad merge |/ / o | BRANCH_V8 - 12 - wrong merge from BRANCH_V9 |/| | o BRANCH_V8 - 11 - adding comment on BRANCH_V8 | | o | BRANCH_V9 - 10 - last commit on BRANCH_V9 | |

Ahora la situación en BRANCH_V8 es correcta. El único problema que queda es que la próxima combinación de BRANCH_V8 a BRANCH_V9 será incorrecta, ya que se combinará también en la ''corrección'' de la fusión incorrecta, que no queremos en BRANCH_V9. El truco aquí es fusionar de BRANCH_V8 a BRANCH_V9 en cambios separados:

  • Primera combinación, de BRANCH_V8 a BRANCH_V9, los cambios correctos en BRANCH_V8 antes de la fusión incorrecta.
  • Segunda fusión en el error de fusión y su solución, y, sin necesidad de verificar nada, deseche todos los cambios
  • En tercer lugar, combine los cambios restantes de BRANCH_V8.

En detalle:

  1. Cambia tu directorio de trabajo a BRANCH_V9: hg update BRANCH_V9
  2. Fusionar en el último buen estado de BRANCH_V8 (es decir, la confirmación que generó para corregir la fusión incorrecta). Esta fusión es una fusión como cualquier combinación regular, es decir. los conflictos deben resolverse como de costumbre, y no es necesario descartar nada.
    1. fusionar: fusionar hg merge 14
    2. commit: hg commit -m "Merging in last good state of BRANCH_V8" La situación es ahora:

      @ BRANCH_V9 - 17 - Merging in last good state of BRANCH_V8 |/ | | o BRANCH_V8 - 16 - merged two heads used to revert from bad merge | | |/ | +---o BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9 | | | | | o | | BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9 | | | | | | o | BRANCH_V8 - 13 - important commit right after the bad merge | | |/ +---o BRANCH_V8 - 12 - wrong merge from BRANCH_V9 | |/ | o BRANCH_V8 - 11 - adding comment on BRANCH_V8 | | o | BRANCH_V9 - 10 - last commit on BRANCH_V9 | |

  3. Incorpórate a la combinación incorrecta en BRANCH_V8 + su solución, y descarta todos los cambios:
    1. fusionar: fusionar hg merge 15
    2. revertir todos los cambios: hg revert -a --no-backup -r 17
    3. commit the merge: hg commit -m "Merging in bad merge from BRANCH_V8 and its fix and throwing it all away" Situación actual:

      @ BRANCH_V9 - 18 - Merging in bad merge from BRANCH_V8 and its fix and throwing it all away |/ | o BRANCH_V9 - 17 - Merging in last good state of BRANCH_V8 | |/ +-----o BRANCH_V8 - 16 - merged two heads used to revert from bad merge | | | | o---+ | BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9 | | | | | | o | BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9 | | | | +-----o BRANCH_V8 - 13 - important commit right after the bad merge | | | o---+ BRANCH_V8 - 12 - wrong merge from BRANCH_V9 |/ / | o BRANCH_V8 - 11 - adding comment on BRANCH_V8 | | o | BRANCH_V9 - 10 - last commit on BRANCH_V9 | |

  4. Combina los cambios sobrantes de BRANCH_V8:
    1. merge: hg merge BRANCH_V8
    2. commit: hg commit -m "merging changes from BRANCH_V8"

Al final, la situación se ve así:

@ BRANCH_V9 - 19 - merging changes from BRANCH_V8 |/ | o BRANCH_V9 - 18 - Merging in bad merge from BRANCH_V8 and its fix and throwing it all away | |/ | | o BRANCH_V9 - 17 - Merging in last good state of BRANCH_V8 | | |/ o | | | BRANCH_V8 - 16 - merged two heads used to revert from bad merge |/| | | | o---+ BRANCH_V8 - 15 - throwing away wrong merge from BRANCH_V9 | | | | | | | o BRANCH_V8 - 14 - generating commit on BRANCH_V8 to rectify wrong merge from BRANCH_V9 | | | | o | | | BRANCH_V8 - 13 - important commit right after the bad merge |/ / / o---+ BRANCH_V8 - 12 - wrong merge from BRANCH_V9 |/ / | o BRANCH_V8 - 11 - adding comment on BRANCH_V8 | | o | BRANCH_V9 - 10 - last commit on BRANCH_V9 | |

Después de todos estos pasos, en los que no tiene que comprobar ninguna diferencia manualmente, BRANCH_V8 y BRANCH_V9 son correctos, y las fusiones futuras de BRANCH_V8 a BRANCH_V9 también serán correctas.


Después de mucha discusión con algunas de las personas más útiles sobre #mercurial en freenode, mpm ha proporcionado una solución parcial que parece funcionar para mi caso de prueba (generé un repositorio falso que intentaba replicar el escenario)

Sin embargo, en mi repositorio real, por razones que aún no entiendo del todo, todavía no es perfecto.

Aquí hay un diagrama de la forma actualmente propuesta de resolver este problema:

[Imagen original perdida]

Ahora es un problema menor de arreglar, pero todavía tengo que comparar los diffs (es decir: b46: b11 vs b46: b8, a43: a10 contra a43: a9) y editar manualmente algunos cambios en.

No cerrar esta pregunta / tomar una respuesta hasta que obtenga una forma garantizada que funcione en cualquier repositorio.

Importante

Cualquiera que intente esto debería clonar su repositorio y jugar con él primero como una caja de arena. Como debería estar haciendo con cualquier proceso de fusión, porque de esa manera, si sale mal, puede tirarlo y comenzar de nuevo.


En un momento dado, puede exportar el repositorio a un grupo de diffs, editar el historial y luego volver a unir lo que quiera, en un nuevo repositorio, para que no haya riesgo de daños. Probablemente no sea tan malo para su ejemplo, pero no sé cómo es la historia real.

Hice referencia a esta página mientras realizaba una operación más simple:

http://strongdynamic.blogspot.com/2007/08/expunging-problem-file-from-mercurial.html


OK, comience creando un nuevo repositorio vacío en un directorio separado del repositorio roto (hg init). Ahora, seleccione e incluya la última versión válida conocida en el nuevo repositorio; asegúrate de no tirar de la combinación incorrecta y de tirar de todo antes. En el repositorio anterior, actualice a la última versión buena conocida de A, y haga esto:

hg graft r1 r2 r3

donde r1-3 son los cambios realizados después de la fusión fallida. Puede tener conflictos en este punto; arreglalos.

Esto debería producir nuevos cambios contra la última versión buena conocida de A. Tire esos nuevos cambios en el nuevo repositorio. Solo para comprobar que no te perdiste nada, haz un hg entrante contra el antiguo repositorio. Si ve algo diferente de la fusión frustrada y r1-3, tire de ella.

Tirar el viejo repositorio de distancia. Ya terminaste La fusión no está en el nuevo repositorio y nunca tuvo que reescribir el historial.