Hg: Cómo hacer un rebase como el rebase de git

207

En Git puedo hacer esto:

1. Comience a trabajar en una nueva función:
$ git co -b newfeature-123 # (una rama de desarrollo de características locales)
hacer algunas confirmaciones (M, N, O)

maestro A --- B --- C
                \
novedad-123 M --- N --- O

2. Extraiga nuevos cambios del maestro ascendente:
$ git pull
(maestro actualizado con ff-commits)

maestro A --- B --- C --- D --- E --- F
                \
novedad-123 M --- N --- O

3. Rebase fuera de master para que mi nueva característica 
se puede desarrollar contra los últimos cambios ascendentes:
(de newfeature-123)
$ git rebase master

maestro A --- B --- C --- D --- E --- F
                            \
novedad-123 M --- N --- O


Quiero saber cómo hacer lo mismo en Mercurial, y busqué en la web una respuesta, pero lo mejor que pude encontrar fue: git rebase: ¿puedo hacer eso?

Ese enlace proporciona 2 ejemplos:
1. Admito que esto: (reemplazando las revisiones del ejemplo con las de mi propio ejemplo)

hg arriba -CF  
hg branch -f newfeature-123  
hg transplant -a -b newfeature-123 

no es tan malo, excepto que deja atrás el MNO previo al rebase como una cabeza no fusionada y crea 3 nuevos commits M ', N', O 'que los representan ramificándose de la línea principal actualizada.

Básicamente, el problema es que termino con esto:

maestro A --- B --- C --- D --- E --- F
                \ \
novedad-123 \ M '--- N' --- O '
                  \
novedad-123 M --- N --- O

esto no es bueno porque deja compromisos locales no deseados que deberían descartarse.

  1. La otra opción del mismo enlace es
hg qimport -r M: O
hg qpop -a
hg arriba F
hg branch newfeature-123
hg qpush -a
hg qdel -r qbase: qtip

y esto da como resultado el gráfico deseado:

maestro A --- B --- C --- D --- E --- F
                            \
novedad-123 M --- N --- O

pero estos comandos (¡los 6 de ellos!) parecen mucho más complicados que

$ git rebase master

Quiero saber si este es el único equivalente en Hg o si hay alguna otra forma disponible que sea simple como Git.

jpswain
fuente
77
"Esto no es bueno porque deja compromisos locales no deseados que deberían descartarse". - En realidad, Git hace lo mismo. No cambia ni elimina las confirmaciones en la rama original, solo crea nuevas que aplican el mismo conjunto de cambios sobre el maestro. Todavía puede acceder a los viejos usando git reflogy no se han ido por completo hasta que recogen la basura. Si desea mantenerlos en una rama con nombre para que no tenga que usar el registro, solo hágalo git branch feature-123_originalantes de volver a redactar .
MatrixFrog
55
Pregunta aleatoria: ¿dibujó usted mismo los conjuntos de cambios / ramas o hay una herramienta que lo haga?
Amir Rachum el
2
Solo los hice yo mismo con TextWrangler configurado para "sobrescribir".
jpswain

Respuestas:

235

VonC tiene la respuesta que estás buscando , la extensión Rebase. Sin embargo, vale la pena pasar un segundo o dos pensando por qué ni mq ni rebase están habilitados por defecto en mercurial: porque mercurial se trata de conjuntos de cambios indelebles. Cuando trabajo de la manera que estás describiendo, que es casi a diario, aquí está el patrón que tomo:

1. Start working on a new feature:
$ hg clone mainline-repo newfeature-123
do a few commits (M, N, O)

master A---B---C
                \
newfeature-123   M---N---O

2. Pull new changes from upstream mainline:
$ hg pull

master A---B---C---D---E---F
                \
newfeature-123   M---N---O

3. merge master into my clone so that my new feature 
can be developed against the latest upstream changes:
(from newfeature-123)
$ hg merge F

master A---B---C---D---E---F
                \           \
newfeature-123   M---N---O---P

y eso es realmente todo lo que es necesario. Termino con un nuevo clon de 123 características que puedo empujar fácilmente a la línea principal cuando estoy contento con él. Lo más importante, sin embargo, nunca cambié la historia . Alguien puede mirar mis conjuntos y ver contra qué se codificaron originalmente y cómo reaccioné a los cambios en la línea principal a lo largo de mi trabajo. No todos piensan que eso tiene valor, pero creo firmemente que es el trabajo del control de la fuente mostrarnos no lo que deseamos que hubiera sucedido, sino lo que realmente sucedió: cada diablo y cada refactor deberían dejar un rastro indeleble y un rebase y otras técnicas de edición de historia lo ocultan.

Ahora ve a buscar la respuesta de VonC mientras guardo mi caja de jabón. :)

Ry4an Brase
fuente
18
Nota: por supuesto, Git no le permite exactamente reescribir el historial, solo para crear uno nuevo fácilmente ( utcc.utoronto.ca/~cks/space/blog/tech/GitNewHistory ). Al agregar RebaseExtension, Mercurial proporciona la misma manera conveniente y conveniente de reemplazar un historial antiguo por uno nuevo. ¿Por qué? Porque una combinación no siempre es la respuesta correcta, especialmente cuando su conjunto de cambios debe verse como una evolución en la parte superior de F, y no al revés (P combinado en la parte superior de O)
VonC
19
VonC, estoy de acuerdo, una fusión no siempre es la elección correcta, pero creo que la diferencia es en lo que uno quiere que el historial de VCS pueda contarles. Creo que la historia siempre debería ser capaz de responder preguntas como "¿De qué manera traté de integrarlo al principio que no funcionó y pensé que era inútil en ese momento". Los científicos mantienen los libros de registro en la pluma con páginas numeradas, y los ingenieros de software de AFAIC deben guardar cada byte que hayan escrito. Reescribir el historial, incluso la paternidad de cset, pero ciertamente CollapseExtension, HistEdit, etc., viola eso. Es totalmente una cuestión de elección personal.
Ry4an Brase el
14
+1 para elección personal. En consecuencia, con Git utilizo rebase para divergencias triviales y fusionar para no trivial. Esto me permite conservar el historial de fusión donde creo que es importante, pero mantener el registro limpio y lineal la mayor parte del tiempo.
Kos
16
También hago toneladas de rebases interactivos porque, en primer lugar, tiendo a hacer muchas confirmaciones pequeñas, luego unirlas, etiquetarlas y limpiarlas, luego fusionarlas de nuevo con (o rebase encima) de la rama principal. Me gusta codificar y administrar los cambios como pasos separados.
Kos
66
La filosofía de "guardar cada byte a lo largo del camino del desarrollo" no es realmente apropiada para grandes proyectos de código abierto, donde los contribuyentes proponen un conjunto de parches y luego los revisan en función de los comentarios de los mantenedores. Luego, eventualmente, el repositorio principal del proyecto tiene la versión adecuada de todos los cambios, con todos los cambios relacionados en una sola confirmación. git interactive rebase es realmente bueno para limpiar los cambios de trabajo en una secuencia de confirmaciones que no dejan el árbol roto, y con mensajes de confirmación que reflejan lo que decides decir después de que hayas terminado de trabajar en todo.
Peter Cordes
104

Quizás estés buscando la extensión Rebase . (implementado como parte del SummerOfCode 2008 )

En esos casos, puede ser útil "separar" los cambios locales, sincronizar el repositorio con la corriente principal y luego agregar los cambios privados además de los nuevos cambios remotos. Esta operación se llama rebase.

Llegando desde :

texto alternativo

a:

texto alternativo


Como se comenta a continuación por steprobe :

En el caso de que no esté aplicando los cambios y tenga las dos ramas en su repositorio, puede hacer ( usandokeepbranches ):

hg up newfeature-123 
hg rebase -d master --keepbranches

( --keepbranches: Hereda el nombre de la sucursal original).

Mojca menciona:

Me gusta usar hg rebase --source {L1's-sha} --dest {R2's-sha}, pero no sabía que podía agregar --keepbranchesal final.

Como se ilustra a continuación por Jonathan Blackburn :

 hg rebase -d default --keepbranches
VonC
fuente
1
He mirado la extensión Rebase, pero todavía no me queda clara. ¿Podría explicar los pasos para hacer lo que he descrito anteriormente?
jpswain
66
En el caso de que no esté aplicando los cambios y tenga las dos ramas en su repositorio, puede hacer lo siguiente: hg up newfeature-123seguido dehg rebase -d master --keepbranches
steprobe
2
Creo que no hay nada malo con rebase, es solo una cuestión de elección. El problema es que se abusa de rebase y estoy con @ Ry4an en esto, no reescriba el historial para que pueda saber qué sucede y cuándo.
Jorge Vargas
@steprobe: gracias por la pista para usar --keepbranches. Me gusta usar hg rebase --source {L1's-sha} --dest {R2's-sha}, pero no sabía que podía agregar --keepbranchesal final. Esto se menciona en otras respuestas de menor rango, pero sería bueno escribirlo explícitamente en esta respuesta también.
Mojca
@Mojca No hay problema. He editado la respuesta en consecuencia.
VonC
44

Suponiendo que tiene una instalación moderna de Hg, simplemente puede agregar:

[extensions]
rebase = 

a ~ / .hgrc.

A continuación, puede utilizar los comandos hg rebase, hg pull --rebaseo hg help rebase.

sblom
fuente
2
Solo para agregar a esto en términos del comando, entonces debe ejecutar: hg rebase -s o -d f
Simple-Solution
21

No creo que las respuestas anteriores logren el objetivo del OP, que era mantener su rama de tareas, simplemente se basaron en un punto posterior en la rama principal.

Digamos que empiezo con este gráfico (generado usando la extensión de Graphlog. Serio amor geek por Graphlog).

@  9a4c0eb66429 Feature 3 commit 2 tip feature3
|
| o  af630ccb4a80 default againagainagain  
| |
o |  98bdde5d2185 Feature 3 branch commit 1  feature3
|/
o  e9f850ac41da foo   

Si estoy en la rama de feature3 y quiero volver a basarlo en el nuevo compromiso, entiendo que me ejecutaría hg rebase -d default. Esto tiene el siguiente resultado:

@  89dada24591e Feature 3 commit 2 tip 
|
o  77dcce88786d Feature 3 branch commit 1  
|
o  af630ccb4a80 default againagainagain  
|
o  e9f850ac41da foo  

¿Misión cumplida? No lo creo. El problema es que cuando los commits en la rama feature3 se volvieron a basar de nuevo, se eliminó la rama feature3 . Mis confirmaciones se han movido a la rama predeterminada, que era lo que estaba tratando de evitar en primer lugar.

En Git, el resultado se vería así:

@  9a4c0eb66429 Feature 3 commit 2 tip
|
o  98bdde5d2185 Feature 3 branch commit 1 **feature3**
|
o  af630ccb4a80 default againagainagain
|
o  e9f850ac41da foo

Tenga en cuenta que la rama de feature3 todavía existe, las dos confirmaciones todavía están en la rama de feature3 y no están visibles por defecto. Sin preservar la rama de tareas, no veo cómo esto es funcionalmente diferente de una fusión.

ACTUALIZACIÓN : descubrí la --keepbranchesbandera compatible con hg rebase, y estoy feliz de informar que todo es okey-dokey. Utilizando hg rebase -d default --keepbranches, reproduzco exactamente el comportamiento de Git que ansiaba. Un par de alias más tarde y estoy rebatiendo como si no fuera asunto de nadie.

Jonathan Blackburn
fuente
77
Acabo de descubrir la bandera --keepbranches en rebase. Problema resuelto. Si este fuera mi código, sería el predeterminado, pero solo soy yo.
Jonathan Blackburn
1
Creo que sería útil si agregaras ese bit de información a tu respuesta anterior, me tomó un tiempo encontrarlo.
HansMari
3

Dado que algunas personas han intervenido diciendo que piensan que es bueno mantener cada iteración de todo, señalaré que para proyectos de código abierto más grandes, aceptar cambios llenos de fusiones e iteraciones de desarrollo crearía un desordenado historial de revisión de la línea principal, y el historial de revisión es menos útil para ver cómo llegó la versión actual.

Esto funciona bien cuando los cambios enviados son revisados ​​por personas que no los escribieron, antes de que sean aceptados, por lo que los cambios que entran en la línea principal generalmente se depuran y funcionan. Luego, cuando retrocede al origen de una línea, ve todos los cambios que la acompañan, no algún punto en el medio del desarrollo del cambio del que forma parte.

La página de contribuyentes x265 explica cómo volver a confirmar un conjunto de cambios en los que está trabajando, para prepararlos para su envío al proyecto x265. (Incluyendo el uso de TortoiseHG para confirmar algunos, pero no todos, los cambios en un archivo individual, como stage / unstage diff hunk de git gui para commit).

El proceso consiste en actualizar hg a la sugerencia ascendente y luego obtener todos los cambios sin confirmar en el directorio de trabajo. Guarde los que no sean parte de lo que desea enviar, luego divida el resto en tantos commits separados como sea apropiado, con buenos mensajes de commit.

Supongo que copiaría / pegaría y luego editaría los mensajes de confirmación de las iteraciones anteriores de un conjunto de parches que está revisando. O tal vez podría injertar sus antiguas confirmaciones (selección de cereza en lenguaje git), y luego modificarlas una por una, para obtener sus antiguos mensajes de confirmación como punto de partida para la edición.

Peter Cordes
fuente