2009-09-23 17 views
23

Estoy tratando de tener dos ramas con archivos binarios en git - un "desarrollo" y un "estable". La rama de desarrollo puede tener varios cambios de estos archivos antes de que quiera "liberarlos" a la rama estable (y la rama estable tiene esos archivos renombrados, en caso de que sea relevante).Git fusionar squash repetidamente

Podría hacer una fusión normal, esto funciona bien, pero conserva demasiada historia: cuando se tira de la rama "estable", se extraen todas las confirmaciones intermedias de la rama "desarrollo" (porque son confirmaciones principales) . Pero estamos hablando de archivos binarios que no tienen ninguna estrategia de fusión razonable (excepto la suya/la nuestra), por lo que el historial real de los archivos en la rama de desarrollo es inútil. Cuando me tire de la rama "estable", me sale esto:

 
      X-------------------G stable 
     /    /
    a---b---c---d---e---f---g development 

Debido a que G tiene un padre en la rama de desarrollo, consigo toda la historia de la rama de desarrollo (objetos de datos para c, d, e, f y g) en mi repositorio, que no me interesa (X es lo mismo que b, con algunos cambios de nombre de archivo aplicados).

Así que traté de git merge --squash cambios de la rama de desarrollo a la rama estable. La primera de esas fusión y comprometerse ido bien, los resultados fueron los esperados (buen registro de cambios en el mensaje de confirmación, sin relación con la rama de desarrollo):

   X-------------------G stable 
     /     
    a---b---c---d---e---f---g development

Después de apretar el rama estable aplastada, me sale esto en mi repositorio, que es lo que quiero:

 
a---b---X---G 

Pero la segunda fusión fallida (porque Git no tenía manera de saber lo mucho que ya se fusionaron y se confundió).

  • ¿Es posible grabar la fusión de alguna manera, sin producir un "commit de fusión" con dos padres?
  • O, ¿es posible decirle a git que combine solo cierto "rango" de revisiones, como en SVN?
  • O, ¿es posible hacer una fusión normal sin tener que descargar todas las referencias desde la otra rama cuando se tira?
  • ¿O debería proporcionar un controlador de combinación personalizado para los archivos en cuestión, que simplemente cambia el nombre de "su" versión a "nuestra", resolviendo así los conflictos? Todavía tengo miedo de que --squash siempre intente fusionar toda la historia, hasta el padre común, resolviendo solo la mitad de mi problema.

Actualización: rebase

Si entiendo correctamente rebase, voy a terminar con esto:

 
           X stable 
          /
    a---b---c---d---e---f---g development 

Lo cual me lleva todos los datos que no estoy interesado en (c , d, e, f) y como extra, perderé la información de que b era una versión estable en la sucursal.

Cada revisión de desarrollo agrega unos 5MB al tamaño del repositorio (y reempaquetar todo el repositorio lo reduce solo un 10%), la rama "estable" es casi gratuita (los datos ya están allí). Quiero sacar una nueva revisión de la rama estable para extraer solo los 5MB nuevos, pero en cambio actualizar de X a G descarga 25MB, porque de alguna manera no puedo decir que no me importan los contenidos de c, d , e y f.

Respuesta

23

A partir de

 X stable 
    /     
a---b---c---d---e---f---g development 

Se pueden utilizar los siguientes pasos para copiar la última confirmación de su rama de desarrollo de su rama estable:

git checkout [email protected]{0} # get working tree from "development", detach HEAD 
git reset --soft stable # reposition detached HEAD on "stable" 
git commit # enter the appropriate commit message 
git branch temp # create a temporary branch "temp" at HEAD 
git checkout temp # get on the new temporary branch 
git branch -M stable # rename "temp" to "stable" 

por lo que terminan hasta con:

 X-------------------G stable 
    /     
a---b---c---d---e---f---g development 

Si continúa el trabajo en el "desarrollo", por ejemplo,

 X-------------------G stable 
    /     
a---b---c---d---e---f---g---h---i---j development 

puede repetir la misma git ordena que el anterior y el resultado final será con:

 X-------------------G-----------J stable 
    /     
a---b---c---d---e---f---g---h---i---j development 
+1

Esto se ve genial, pero no entiendo exactamente por qué es necesario crear una rama temporal y luego cambiarle el nombre. ¿Es porque todavía estás desapegado después del compromiso? –

+0

@ kim-sullivan: el "por qué exactamente": cuando haces la confirmación en el ejemplo anterior, mueve 'HEAD' pero no' stable' para apuntar a la nueva confirmación: la cabeza aún está "separada", y ' stable' sigue apuntando a la versión anterior (X, en su ejemplo original). Hacer la bifurcación y cambiar el nombre (la salida es técnicamente opcional, aunque sin ella tendrás que suministrar argumentos a 'git branch -M', y aún estarás desapegado, lo que probablemente no sea deseable) hace que las causas sean estables a punto a J (por la respuesta). – lindes

+4

También: Puede ejecutar 'git log --graph --oneline --all --decorate' y' git status' en cada punto de esta serie de comandos para ser informativo. Y/o mirando los contenidos de varios archivos en .git /, quizás con 'grep. .git/HEAD .git/refs/heads/* ' – lindes

7

Este no es el lugar correcto para usar merge --squash.Un buen lugar para usarlo es en una rama de tema descartable, que se fusionará en su rama principal y luego se deshará. Todo el desarrollo realizado en la rama de tema se muestra como uno comprometido en la rama principal. En su situación, debe fusionarse desde la rama de desarrollo normalmente, y luego usar git-rebase --interactive para aplastar las confirmaciones que desee.

¿Es posible grabar de alguna manera la fusión, sin que se produzca una "combinación comprometen" con dos padres?

Si entiendo la pregunta correctamente, no. Absolutamente no.

¿Es posible decir git fusionar sólo una cierta "gama" de las revisiones, al igual que en el SVN?

Sí. git merge [commit hash]

O, ¿es posible hacer una fusión normal sin tener que descargar todos los árbitros de la otra rama cuando se tira?

Ver respuesta a la pregunta anterior.

o debería proporcionar un controlador de combinación personalizada para los archivos en cuestión, que simplemente cambia el nombre de "su" versión de "nuestro", resolviendo así los conflictos? Todavía tengo miedo de que --squash siempre intente fusionar toda la historia, hasta el padre común, resolviendo solo la mitad de mi problema.

No! Simplemente no use git merge --squash. ¡Este no es el lugar correcto para usarlo!

+0

Gracias por su respuesta! No estoy seguro de que lo que necesito sea volver a basarme. Necesito registrar algo de historial en la rama "estable", así puedo ir y venir entre "lanzamientos".Esto es lo que quiero: ' D -> S D | D | D -> S D | D -> S' Pero solo necesito el historial de la rama S: no me importa el historial de la rama D. El problema que necesito resolver es cómo tirar de la rama S sin tirar de todas las referencias padre de la rama D (si lo entiendo correctamente, la reescritura elimina todas las confirmaciones S anteriores, que no es lo que quiero). ' D -> x D D D -> x D D -> S ' –

+0

Sí, entiendo que es necesario preservar la historia existente de la rama S. No, rebasing no elimina todos los commits S anteriores; antes de fusionar el desarrollo en estable, fíjese en [commit hash] y luego en git rebase -i [commit hash] - entonces solo estará aplastando las confirmaciones que acaba de fusionar. Además, en lo que respecta a las versiones, ¿por qué no solo etiquetar las confirmaciones específicas como versiones? – artagnon

+0

Lo siento, no entiendo muy bien tu D -> S D | D diagrama muy bien. Espero que mi comentario anterior aclare todo. – artagnon

4

podría utilizar git rebase -i (modo interactivo) a Sqash todos los cambios, sólo cambiar las líneas de m o meld (actualización de nuevos Git Tipo Comunicados s o squash)

va a tener un solo comprometen la cual puedes unirtesi desea que cada paso suyo reservado, debe crear otra rama en su rama de piratería antes de hacer la rebase, esto se puede hacer con git branch small-dirty-changes-i-don’t-want-anybody-to-see

todo esto tiene que hacerse antes de intentar fusionar; paso a paso:

# on branch "hacking": hack commit hack commit hack commit 

# create a reference to your history 
$ git branch dirty-history 

# sqash your commits by setting all lines to "meld" 
$ git rebase -i 

# checkout master or the branch you want to merge to 
$ git checkout master 

# merge your squashed commit 
$ git merge hacking 
# or: $ git pull hacking 
+0

Hm, probablemente podría volver a establecer la base de los cambios en la rama de desarrollo ("sucio") de vez en cuando (en realidad, antes de fusionarme a estable y liberar), así que no tengo todos esos binarios intermedios. No me importa mucho . Lo único importante entre los lanzamientos estables es el hash y los mensajes de confirmación ... El hash es importante para las pruebas (qué versión fue probada y cuál no). Tendré que pensarlo un poco más. –

+0

buena respuesta, supongo que "meld" hoy en día significa "squash" (¡tu respuesta es de 2009!) –

+1

Sí, ahora es 's' /' squash'. – knittl

-1

Soy nuevo en Git, pero parece que --squash es lo que quieres, solo necesitas manejar la rama de manera diferente.

$ git merge --squash development 
$ git branch -d development 
$ git checkout -b development 
# continue work on development branch 

Esto sería esencialmente truncar la historia de la rama de desarrollo, y la siguiente combinación única sería lo que quería. Al mirar la página de manual, parece que git branch -f development podría hacer lo mismo que eliminar y volver a crear la rama.

Como ya he dicho, soy bastante nuevo en git, así que me gustaría recibir algunos comentarios sobre esta idea.

0

El top voted answer diferencia eficazmente las ramas de desarrollo y estables y aplica todos los cambios que faltan en el establo al comprometerse. Hay una forma alternativa de hacerlo:

$ git checkout stable 
$ git diff stable development | git apply --index - 
$ git commit 

Antes de comprometerse, puede revisar los archivos montados. Si desea cancelar el proceso después de aplicar el diff, puede usar comandos básicos para eliminar los cambios del índice y del directorio de trabajo.

$ git reset --hard 

puede repetir el proceso tantas veces, ya que son en efecto sólo diff-ción de las puntas de ambos las ramas cada vez que se necesita combinar.

Tenga en cuenta que aquí se supone que también se aplica a la otra respuesta: no hay confirmaciones en la rama estable que no sean las que provienen de la rama de desarrollo. Si la misma línea se cambiara independientemente en ambas ramas, a diferencia de una combinación regular, este enfoque no le daría un conflicto. En cambio, simplemente anularía los cambios en la rama estable con los que están en desarrollo.

Si le preocupa esto, cada vez que se comprometa en estable, puede poner el rango de hash de consolidación de desarrollo combinado en el mensaje de confirmación. De esa forma, tienes algún registro, si alguna vez necesitas dar marcha atrás.

Cuestiones relacionadas