¿Cómo puedo usar git rebase sin requerir un empuje forzado?

92

En un intento por lograr git nirvana, me paso el día aprendiendo cómo aprovechar el rebase para situaciones en las que actualmente me fusiono.

Al ejecutar lo que considero un flujo de git 101 (que deletreo a continuación), tengo que hacerlo push --forcecuando empujo mis cambios al origen.

No soy el único, sé que este es un terreno cubierto (ver 1 , 2 , 3 , 4 , 5 ) y entiendo las razones técnicas por las que es necesaria una fuerza. Mi problema es este: hay muchas (muchas) entradas de blog que alaban a rebase y cómo ha cambiado sus vidas (ver 1 , 2 , 3 , 4 para enumerar algunas), pero ninguna de ellas menciona que push --forcees parte de su flujo. Sin embargo, casi todas las respuestas a las preguntas de stackoverflow existentes dicen cosas como "sí, si vas a rebasar, debes usar push --force".

Dado el número y la religiosidad de los defensores del rebase, tengo que creer que usar 'empujar - fuerza' no es una parte inherente de un flujo de rebase, y que si uno a menudo tiene que forzar sus empujes, está haciendo algo mal .

push --forcees algo malo .

Así que aquí está mi flujo. ¿De qué manera podría lograr los mismos resultados sin una fuerza?

Ejemplo simple

Dos ramas:

  • v1.0 - una rama de lanzamiento, contiene solo parches
  • master : todo para la próxima versión principal.

Tengo algunas confirmaciones de parches y algunas confirmaciones para la próxima versión.

premergerse

Me gustaría incorporar los parches a mi master para que no se pierdan para el próximo lanzamiento. Pre-iluminación Yo simplemente:

git checkout master
git merge v1.0

Pero ahora lo estoy intentando

git checkout master
git rebase v1.0

Así que ahora estoy aquí:

ingrese la descripción de la imagen aquí

Tiempo para:

git push

No dados.

Roy Truelove
fuente

Respuestas:

43

Rebasing es una gran herramienta, pero funciona mejor cuando la usas para crear fusiones de avance rápido para ramas de temas en master. Por ejemplo, puede cambiar la base de su rama add-new-widget contra master:

git checkout add-new-widget
git rebase -i master

antes de realizar una combinación de avance rápido de la rama en master. Por ejemplo:

git checkout master
git merge --ff-only add-new-widget

El beneficio de esto es que su historial no tendrá muchas confirmaciones de fusión complejas o conflictos de fusión, porque todos sus cambios se volverán a basar en la punta del maestro antes de la fusión. Un beneficio secundario es que ha cambiado de base, pero no tiene que usarlo git push --forceporque no está superando el historial en la rama maestra.

Ciertamente, ese no es el único caso de uso de rebase, o el único flujo de trabajo, pero es uno de los usos más sensibles que he visto. YMMV.

Todd A. Jacobs
fuente
4
Gracias CG, creo que "usarlo para crear fusiones de avance rápido" es la clave. No se aplica a mi caso anterior, donde tengo dos ramas en vivo: una rama de desarrollo y una rama de lanzamiento, pero parece aplicarse muy bien para ramas temáticas temporales que solo son necesarias por un tiempo limitado, y luego pueden ser eliminados una vez que se fusionan. Gracias de nuevo.
Roy Truelove
1
Entiendo esto, pero la pregunta original permanece. Creo que la respuesta real es la dada por @Fabien Quatravaux
IsmailS
4
Bueno, todavía tendrías que forzar el empuje de la rama 1.0, ¿no? Al menos, así es como tienden a salirme las cosas todo el tiempo. El método de Fabiens evitaría que eso sucediera.
joerx
23

@CodeGnome tiene razón. No debe rebasar el maestro en la rama v1.0 sino la rama v1.0 en el maestro, eso hará toda la diferencia.

git checkout -b integrate_patches v1.0
git rebase master
git checkout master
git merge integrate_patches

Cree una nueva rama que apunte a v1.0, mueva esa nueva rama encima de la maestra y luego integre la nueva versión de los parches de la V1.0 a la rama maestra. Terminarás con algo como:

o [master] [integrate_patches] Another patch on v1.0
o A patch on v1.0
o Another change for the next major release
o Working on the next major release
|  o [v1.0] Another path on v1.0
|  o A patch on v1.0
| /
o Time for the release

Esta forma de usar rebase es recomendada por la documentación oficial de git .

Creo que tienes razón git push --force: solo debes usarlo si cometiste un error y presionaste algo que no querías.

Fabien Quatravaux
fuente
Creo que esta es la mejor respuesta para el problema específico en el OP. Crea una rama de fusión temporal y vuelve a basarse en ella, luego fusiona para master y empuja al origen. La rama temporal no tiene que ser empujada al origen. El único consejo adicional que daría es tener una rama de desarrollo o qa donde se pueda calificar el código fusionado / rebasado. Después de la calificación, este código solo se fusionaría en el maestro. Esto permite una fácil revisión si su proceso de calificación toma demasiado tiempo. Este es básicamente el proceso "git flow".
Javid Jamae
Gracias Fabien, gran respuesta. Para aquellos de nosotros que solo queremos integrar los cambios mastery no nos importa fusionar la rama de características en sí, esto se puede hacer con:git checkout my-branch; git rebase master; git checkout master; git merge my-branch
Siavas
17

Tienes que forzar el empuje si vuelves a basar, y ya has publicado tus cambios, ¿verdad?

Utilizo rebase mucho, pero publico en algo privado donde un impulso forzado no importa (por ejemplo: mi propio clon en GitHub, como parte de una solicitud de extracción), o realizo un cambio de base antes de presionar por primera vez.

Este es el corazón del flujo de trabajo donde usa rebase, pero no fuerce el empuje mucho: no publique cosas hasta que estén listas, no rebase después de empujar.

Daniel Pittman
fuente
Gracias Dan. Entonces, ¿podría decirme cómo se debe lograr lo anterior? ¿No es este un escenario en el que se aplica el rebase?
Roy Truelove
2
Si aísla todo su trabajo en ramas temáticas, se crea un buen escenario para la reestructuración. Nuevamente rebaseextrajo cambios en su rama de tema, pero cuando haya completado los cambios de mergeesa rama, regresará a la rama de desarrollo principal.
redhotvengeance
1
El problema es que ha publicado la rama, por lo que debe forzar el envío al repositorio. Debe renunciar a uno de los dos: publicar como lo hace o reajustar. Lo siento.
Daniel Pittman
Parece que el rebase no funciona para este escenario. La v1.0 no es una rama de tema, es una rama de lanzamiento, por lo que nunca morirá y debe ser publicada.
Roy Truelove
5

Creo que hay un buen caso de uso para este patrón de rebase-luego-force-push que no es el resultado de un empujón equivocado: trabajar en una rama de función usted mismo desde múltiples ubicaciones (computadoras). Hago esto a menudo, ya que a veces trabajo en la oficina en mi escritorio y, a veces, desde mi casa / sitio del cliente en mi computadora portátil. Necesito reajustar ocasionalmente para mantenerme al día con la rama principal y / o hacer fusiones más limpias, pero también necesito forzar cuando dejo una máquina para ir a trabajar en otra (donde solo tiro). Funciona a las mil maravillas, siempre que sea el único que trabaje en la sucursal.

8 cuarenta
fuente
2
Tengo este mismo flujo de trabajo (trabajando desde múltiples computadoras / ubicaciones). Entonces, supongamos que está trabajando en una rama de tema llamada 'mytopic'. Siempre que vuelva a establecer una base de una rama local desechable (que es simplemente una rama de mytopic) en "master" y luego vuelva a fusionarla con mytopic, nunca tendrá que forzar el empuje. OP tiene un escenario ligeramente diferente, por lo que en un caso como ese, podría ser necesario un empuje forzado. Sin embargo, creo que OP está reajustando de manera incorrecta: si lo hiciera como lo describí, entonces no sería necesario un empuje forzado.
bwv549
3

Esto es lo que uso (asumiendo que el nombre de su rama es foobar ):

git checkout master              # switch to master
git rebase   foobar              # rebase with branch
git merge -s ours origin/master  # do a basic merge -- but this should be empty
git push origin master           # aaand this should work
fragante
fuente
2
Volver a basar al maestro parece extraño.
Emil Vikström
2
gitno está exento de personalidad
oloroso
1
git merge -s ours origin/<branch>fue lo que nos arregló
Max
0

tl; dr fusionar con ramas compartidas, rebase con ramas individuales. --force-with-leasees una alternativa más segura a la fuerza y ​​debería ayudarlo a lograr dicho nirvana git sin la naturaleza destructiva de la fuerza.

Una regla general que he visto que funciona para los flujos de trabajo de varios equipos es usarla mergepara ramas compartidas (es decir, master o desarrollar) y usarla rebasecuando se trabaja en su propia rama de funciones. Este es un ciclo de vida típico de una rama de características

git checkout master
git checkout -b new-feature
git commit -am "commit new work"
git push -u origin new-feature
# have code reviewed, run CI, etc.,
# meanwhile master has new commits
git checkout master
git pull
git rebase -i master # -i for interactive rebase allows you to squash intermediate commits
git push --force-with-lease
git merge master

Una versión en inglés simple de lo que hemos hecho aquí:

  1. Creó una nueva rama fuera del maestro
  2. Trabajo realizado en la rama y empujado a control remoto
  3. rebasado en el maestro
  4. Empuje el trabajo a control remoto con force-with-lease
  5. fusionar en maestro con un registro de git muy limpio, reduciendo el desorden de múltiples fusiones de nuestra rama compartida para que nuestra rama "se ponga al día" con el último maestro (rama compartida)

El cuarto paso es REALMENTE importante y una de las principales razones por las que comencé a abogar por el uso de rebase. force-with-leasecomprueba el control remoto para ver si se han agregado nuevas confirmaciones. Si git pushse demostró que es destructivo, ¡no presionará!

Espero que esto le dé a alguien más confianza para usar rebase.

lucasnad27
fuente