2012-08-06 13 views
6

Tenemos una rama principal en la que hemos combinado aproximadamente 10 ramas de características, una a la vez.¿Es posible reescribir el historial de una sucursal sin perder información de fusión en el camino?

Así que la historia reciente se parece a esto:

merged feat/10 (HEAD of master) 
merged feat/9 
merged feat/8 
merged feat/7 
merged feat/6 
merged feat/5 
... 

Ahora nos encontramos con que feat/7 era malo y queremos sacarlo del maestro. Revertir ese compromiso de fusión no es suficiente porque no queremos que ese compromiso roto exista en absoluto en nuestra historia. Realmente no podemos usar la base de datos interactiva porque eso aplanará el historial para que parezca que todo se hizo en una sola rama, y ​​queremos preservar todo ese buen historial de fusiones.

¿Hay alguna forma de borrar un merge commit de la historia de una sucursal?

Notaré que la historia real es mucho más compleja que la que ve en el ejemplo anterior, por lo que volver a realizar manualmente todas las fusiones ya que feat/7 no sería una buena opción.

Editar

para aclarar a los que votan para cerrar esto como un DUP: este no es el FAQ sobre la forma de llevar a cabo una confirmación usando rebase, que por supuesto ha sido contestada muchas veces. La pregunta aquí es acerca de sacar un commit sin aplanando el historial de fusión.

+0

posible duplicado de [¿Cómo se elimina una revisión específica en el historial de git?] (Http://stackoverflow.com/questions/37219/how-do-you-remove-a-specific-revision-in-the -git-history) –

+0

@BillyMoon la pregunta que publicas como posible duplicado no está realmente relacionada ya que mencioné específicamente por qué una simple rebase aquí no es la solución. Por favor invierta su voto cercano si es posible. Las personas que tal vez no entienden realmente los matices podrían pensar que se trata de un dúo. –

+1

¿Estás seguro de que es necesario aplanar tu historial para volver a establecer la base? ¿No puedes simplemente rebase feat/8 en feat/7, y especificar que no quieres mantener el código de feat/7? –

Respuesta

1

Si su historial actual se ve así y usted no eliminó las ramas aún puede simplemente git reset --hard HEAD~4 esto restablecerá su código de nuevo al estado antes de fusionarse en 7 entonces simplemente puede git merge las buenas de nuevo. Este es el más simple forma en que puedo pensar en mi cabeza.

EDIT: Puede utilizar -p interruptor de rebase para preservar fusiones pero utilizando este modificador con -i podría tener consequances. Compruebe la página man git-rebase y vea la parte BUGS para ver los errores actuales.

EDIT2: No asumo ninguna responsabilidad si no toma las precauciones adecuadas antes de usar este comando. No lo use antes de leer la página de manual también.

+0

Agradezco tu respuesta, pero el ejemplo anterior es en realidad, una simplificada. En el mundo real, hay muchas más sucursales y la historia podría ir mucho más atrás, por lo que volver a fusionarnos manualmente no es una buena opción para nosotros. Buscando una solución automática. –

+0

Me pregunto si puedes tener una solución automática porque si sacas una revisión, todas las combinaciones posteriores son diferentes a las anteriores, eso significa que puede haber conflictos que no ocurrieron antes, etc. Tienes que manejarlos manualmente –

+0

Bueno, no lo hiciste t indicar que en su pregunta lo declare por favor. Simplemente pensé que me acaba de dar la historia real – Learath2

0

No se puede simplemente eliminar cosas de git porque el hash actual depende de todo el historial.

Una opción es crear una nueva rama que comience desde feat/6 y luego tenga las fusiones comenzando desde feat/8 para que el branch head pueda apuntar a un hash diferente sin los cambios de feat/7.

Otra opción, si no me equivoco, es git replace (creo que solía llamarse injertos). Puede permitirle "reemplazar" el puntero en feat/8 de feat/7 a feat/6. No estoy del todo seguro de cómo exactamente logra esto, pero parece que no es un verdadero reemplazo porque feat/8 aún tiene un puntero en alguna parte para feat/7 porque el hash no cambia, pero git replace de alguna manera agrega un historia alternativa en la que feat/8 puntos a feat/6. Desde el man page:

git replace [-f] <object> <replacement> 
git replace -d <object>… 
git replace -l [<pattern>] 

añade una referencia en reemplazar.git/refs/replace/

El nombre de la referencia de reemplazo es el SHA1 del objeto que es reemplazado. El contenido de la referencia de reemplazo es el SHA1 del objeto de reemplazo .

A menos que se especifique -f, la referencia de reemplazo aún no debe existir en .git/refs/replace/directory.

Las referencias de reemplazo se usarán de manera predeterminada por todos los comandos de git , excepto aquellos que realizan el recorrido de accesibilidad (podar, transferencia de paquetes y fsck).

Es posible deshabilitar el uso de referencias de reemplazo para cualquier comando usando la opción --no-replace-objects justo después de git.

EDITAR: en un segundo pensamiento git replace podría ser una mala idea ya que los cambios de feat/7 existirían en la fusión de feat/8. Probablemente debería ir con la primera opción: iniciar una nueva rama fuera de la hazaña fusionada/6 y remerge partir hazaña/8

+0

Cuando 'remerge' a partir de feat/8 del master original, también se combinará feat/7. – Evgeny

+0

Creo que no, ya que cada feat/n es una rama diferente con una característica diferente. Al volver a generar feat/8 no es necesariamente cierto que feat/7 se fusionará también – habeanf

+0

¿Qué pasa si 'feat/10' se ramificó de' feat/8' commit en 'master' y luego se fusionó, cuando intentas fusionar' feat/10' "traerás de vuelta" la vieja historia que incluye 'feat/7'. Además, fusionar o seleccionar cuidadosamente cada compromiso manualmente uno por uno no es realmente una buena solución. – Evgeny

1

Puede utilizar git filter-branch --parent-filter reescribir la feat/8 cometen de manera que sus puntos de origen ante la feat/6 comprometen . Dejar a los padres de todos los demás compromisos (9-10) tal como están, lo que debería preservar los compromisos de fusión en la historia tal como eran.

El único problema con esto es lo que sucederá con los conflictos que provoquen que se modifique el código eliminado ... no existe una forma real de saber, y podría ser el culpable.

1

Esto no es exactamente lo que quiere hacer ("zap" la fusión cometió, por así decirlo), pero en términos prácticos es la manera más fácil que convencer a sus colaboradores a git reset --hard después de un rebase o filter-branch. Solo revierte la fusión.

git revert -m 1 <commit_for_feat7> 

me gusta especialmente contaminantes mis ramas magistrales con revierte, pero no hay nada inherentemente malo en ello. Si no va a parchear feat7 por un tiempo, o simplemente quiere que sus conjuntos de cambios salgan de la historia, esta solución es mucho menos problemática que la revisión de la historia.

+0

+1 esta es realmente la mejor solución, ya que también puede solucionar cualquier conflicto durante la reversión, y conserva el historial (algo bueno). – Evgeny

+0

El único inconveniente es que contamina la historia con ruido. Desde un punto de vista práctico, también le impide usar cosas como git-bisect, que se basan en todos los compromisos que cumplen con algún estándar, como por ejemplo compilar sin errores. Naturalmente, esta pregunta ni siquiera surge si estamos dispuestos a aceptar un mal compromiso en algún lugar de la historia. –

+2

Ambas reclamaciones completamente válidas, aunque son reemplazables por el trabajo (por ejemplo, 'git bisect skip '). Es justo donde desea poner el trabajo: depuración futura o reiniciar el trabajo actual en progreso? – Christopher

Cuestiones relacionadas