¿Cuándo se utiliza Git rebase en lugar de Git merge?

Resuelto Coocoo4Cocoa asked hace 15 años • 0 respuestas

¿Cuándo se recomienda utilizar Git rebase frente a Git merge?

¿Aún necesito fusionarme después de una rebase exitosa?

Coocoo4Cocoa avatar Apr 30 '09 03:04 Coocoo4Cocoa
Aceptado

Version corta

  • Merge toma todos los cambios en una rama y los fusiona en otra rama en una confirmación.
  • Rebase dice que quiero que el punto en el que me bifurqué se mueva a un nuevo punto de partida

Entonces, ¿cuándo usas cualquiera de los dos?

Unir

  • Digamos que ha creado una rama con el fin de desarrollar una única característica. Cuando quieras devolver esos cambios al maestro, probablemente quieras fusionarlos .

Rebase

  • Un segundo escenario sería si comenzara a realizar algún desarrollo y luego otro desarrollador realizara un cambio no relacionado. Probablemente quieras extraer y luego cambiar la base para basar tus cambios desde la versión actual del repositorio.

Aplastamiento : todas las confirmaciones se conservan en ambos casos (por ejemplo: "agregar función", luego "error tipográfico", luego "ups error tipográfico otra vez"...). Las confirmaciones se pueden combinar en una única confirmación mediante aplastamiento. El aplastamiento se puede realizar como parte de una operación de fusión o rebase (--squash flag), en cuyo caso a menudo se le llama squash-merge o squash-rebase.

Solicitudes de extracción : los servidores git populares (Bitbucket, GitLab, GitHub, etc.) permiten configurar cómo se combinan las solicitudes de extracción por repositorio. la interfaz de usuario puede mostrar un botón "Fusionar" por convención, pero el botón puede realizar cualquier operación con cualquier indicador (palabras clave: fusionar, rebase, aplastar, avance rápido).

Rob Di Marco avatar Apr 29 '2009 20:04 Rob Di Marco

Es sencillo. Con rebase dices usar otra rama como nueva base para tu trabajo.

Si tiene, por ejemplo, una rama master, crea una rama para implementar una nueva característica y dice que le ponga un nombre cool-feature; por supuesto, la rama maestra es la base para su nueva característica.

Ahora, en cierto momento, desea agregar la nueva característica que implementó en la masterrama. Podrías simplemente cambiar mastery fusionar la cool-featurerama:

$ git checkout master
$ git merge cool-feature

Pero de esta manera se agrega una nueva confirmación ficticia. Si desea evitar la historia de los espaguetis, puede rebase :

$ git checkout cool-feature
$ git rebase master

Y luego fusionarlo en master:

$ git checkout master
$ git merge cool-feature

Esta vez, dado que la rama del tema tiene las mismas confirmaciones de master más las confirmaciones con la nueva característica, la fusión será solo un avance rápido.

Aldo 'xoen' Giambelluca avatar Feb 05 '2012 06:02 Aldo 'xoen' Giambelluca

TL;DR

Si tienes alguna duda, utiliza merge.

Respuesta corta

Las únicas diferencias entre una rebase y una fusión son:

  • La estructura de árbol resultante del historial (generalmente solo se nota cuando se mira un gráfico de confirmación) es diferente (una tendrá ramas, la otra no).
  • La fusión generalmente creará una confirmación adicional (por ejemplo, un nodo en el árbol).
  • Fusionar y rebase manejarán los conflictos de manera diferente. Rebase presentará los conflictos una vez a la vez, mientras que merge los presentará todos a la vez.

Entonces, la respuesta corta es elegir rebase o fusionar según cómo desea que se vea su historial .

Respuesta larga

Hay algunos factores que debe considerar al elegir qué operación utilizar.

¿La rama de la que recibe cambios se comparte con otros desarrolladores fuera de su equipo (por ejemplo, de código abierto, pública)?

Si es así, no rebase. Rebase destruye la rama y esos desarrolladores tendrán repositorios rotos/inconsistentes a menos que utilicen git pull --rebase. Esta es una buena manera de molestar rápidamente a otros desarrolladores.

¿Qué tan capacitado es su equipo de desarrollo?

Rebase es una operación destructiva. Eso significa que, si no lo aplica correctamente, podría perder el trabajo comprometido y/o romper la coherencia de los repositorios de otros desarrolladores.

He trabajado en equipos donde todos los desarrolladores procedían de una época en la que las empresas podían permitirse el lujo de contar con personal dedicado para ocuparse de las ramificaciones y fusiones. Esos desarrolladores no saben mucho sobre Git y no quieren saber mucho. En estos equipos no me arriesgaría a recomendar el rebase por ningún motivo.

¿La sucursal en sí representa información útil?

Algunos equipos usan el modelo de rama por característica donde cada rama representa una característica (o corrección de errores, o subcaracterística, etc.). En este modelo, la rama ayuda a identificar conjuntos de confirmaciones relacionadas. Por ejemplo, uno puede revertir rápidamente una característica revirtiendo la fusión de esa rama (para ser justos, esta es una operación poco común). O diferenciar una característica comparando dos ramas (más común). Rebase destruiría la sucursal y esto no sería sencillo.

También trabajé en equipos que usaban el modelo de rama por desarrollador (todos hemos pasado por eso). En este caso, la rama en sí no transmite ninguna información adicional (la confirmación ya tiene el autor). No habría ningún daño en rebasar.

¿Quizás quieras revertir la fusión por algún motivo?

Revertir (como deshacer) una rebase es considerablemente difícil y/o imposible (si la rebase tuvo conflictos) en comparación con revertir una fusión. Si cree que existe la posibilidad de que desee revertir, utilice la combinación.

¿Trabajas en equipo? Si es así, ¿está dispuesto a adoptar un enfoque de todo o nada en esta rama?

Las operaciones de rebase deben realizarse con el correspondiente archivo git pull --rebase. Si está trabajando solo, podrá recordar cuál debe utilizar en el momento adecuado. Si trabajas en equipo, será muy difícil coordinarlo. Es por eso que la mayoría de los flujos de trabajo de rebase recomiendan usar rebase para todas las fusiones (y git pull --rebasepara todas las extracciones).

Mitos comunes

La fusión destruye el historial (aplasta las confirmaciones)

Suponiendo que tiene la siguiente combinación:

    B -- C
   /      \
  A--------D

Algunas personas afirmarán que la fusión "destruye" el historial de confirmaciones porque si mirara el registro solo de la rama maestra (A - D) se perderían los mensajes de confirmación importantes contenidos en B y C.

Si esto fuera cierto, no tendríamos preguntas como esta . Básicamente, verá B y C a menos que solicite explícitamente no verlos (usando --first-parent). Esto es muy fácil de probar usted mismo.

Rebase permite fusiones más seguras/simples

Los dos enfoques se combinan de manera diferente, pero no está claro que uno sea siempre mejor que el otro y puede depender del flujo de trabajo del desarrollador. Por ejemplo, si un desarrollador tiende a comprometerse regularmente (por ejemplo, tal vez se compromete dos veces al día cuando pasa del trabajo a casa), entonces podría haber muchas confirmaciones para una rama determinada. Es posible que muchas de esas confirmaciones no se parezcan en nada al producto final (tiendo a refactorizar mi enfoque una o dos veces por función). Si alguien más estuviera trabajando en un área de código relacionada e intentara cambiar la base de mis cambios, podría ser una operación bastante tediosa.

Rebase es más genial / sexy / más profesional

Si desea utilizar un alias rmpara rm -rf"ahorrar tiempo", tal vez rebase sea para usted.

Mis dos centavos

Siempre pienso que algún día me encontraré con un escenario en el que Git rebase sea la herramienta increíble que resuelva el problema. Al igual que creo, me encontraré con un escenario en el que Git reflog es una herramienta increíble que resuelve mi problema. He trabajado con Git durante más de cinco años. No ha sucedido.

Las historias confusas nunca han sido realmente un problema para mí. Nunca leo mi historial de confirmaciones como una novela emocionante. La mayoría de las veces que necesito un historial, de todos modos voy a usar Git culpa o Git bisect. En ese caso, tener la confirmación de fusión es realmente útil para mí, porque si la fusión introdujo el problema, esa es información significativa para mí.

Actualización (4/2017)

Me siento obligado a mencionar que personalmente me he suavizado con el uso de rebase, aunque mi consejo general sigue vigente. Recientemente he estado interactuando mucho con el proyecto Angular 2 Material . Han utilizado rebase para mantener un historial de confirmaciones muy limpio. Esto me ha permitido ver muy fácilmente qué confirmación solucionó un defecto determinado y si esa confirmación se incluyó o no en una versión. Sirve como un gran ejemplo del uso correcto de rebase.

Pace avatar Apr 13 '2016 02:04 Pace

Acabo de crear una pregunta frecuente para mi equipo con mis propias palabras que responde a esta pregunta. Dejame compartir:

¿Qué es un merge?

Una confirmación que combina todos los cambios de una rama diferente en la actual.

¿Qué es un rebase?

Volver a confirmar todas las confirmaciones de la rama actual en una confirmación base diferente.

¿Cuáles son las principales diferencias entre mergey rebase?

  1. mergeejecuta solo una nueva confirmación. rebasenormalmente ejecuta múltiples (número de confirmaciones en la rama actual).
  2. mergeproduce una nueva confirmación generada (la llamada confirmación de fusión). rebasesolo mueve las confirmaciones existentes .

¿En qué situaciones deberíamos utilizar un merge?

Úselo mergesiempre que desee agregar cambios de una rama ramificada nuevamente a la rama base.

Normalmente, esto se hace haciendo clic en el botón "Fusionar" en Solicitudes de extracción/fusión, por ejemplo, en GitHub.

¿En qué situaciones deberíamos utilizar un rebase?

Úselo rebasesiempre que desee agregar cambios de una rama base a una rama ramificada.

Normalmente, esto se hace en featurelas sucursales cada vez que hay un cambio en la mainsucursal.

¿Por qué no utilizar mergepara fusionar cambios de la rama base en una rama de características?

  1. El historial de git incluirá muchas confirmaciones de fusión innecesarias . Si se necesitaran múltiples fusiones en una rama de características, entonces la rama de características podría incluso contener más confirmaciones de fusión que confirmaciones reales.

  2. Esto crea un bucle que destruye el modelo mental con el que se diseñó Git, lo que causa problemas en cualquier visualización del historial de Git.

    Imaginemos que hay un río (por ejemplo, el "Nilo"). El agua fluye en una dirección (dirección del tiempo en la historia de Git). De vez en cuando, imagine que hay un brazo en ese río y suponga que la mayoría de esos brazos se fusionan nuevamente con el río. Así es como se vería naturalmente el flujo de un río. Que tiene sentido.

    Pero luego imagina que hay un pequeño brazo de ese río. Luego, por alguna razón, el río se fusiona con el brazo y el brazo continúa desde allí. El río ya técnicamente ha desaparecido, ahora está en el brazo. Pero entonces, de alguna manera mágica, esa rama se fusiona nuevamente con el río. ¿Qué río preguntas? No sé. En realidad, el río debería estar en el brazo ahora, pero de alguna manera sigue existiendo y puedo fusionar el brazo nuevamente con el río. Entonces, el río está en el río. No tiene sentido.

    Esto es exactamente lo que sucede cuando mergela base se ramifica en una featurerama y luego, cuando la featurerama finaliza, se fusiona nuevamente con la rama base. El modelo mental está roto. Y debido a eso, terminas con una visualización de rama que no es muy útil.

Ejemplo de historial de Git cuando se usa merge:

Ejemplo de historial de Git al utilizar la combinación

Tenga en cuenta las muchas confirmaciones que comienzan con Merge branch 'main' into .... Ni siquiera existen si cambia la base (allí, solo tendrá confirmaciones de fusión de solicitudes de extracción). También muchos bucles de fusión de ramas visuales ( maininto featureinto main).

Ejemplo de historial de Git cuando se usa rebase:

Ejemplo de historial de Git al usar rebase

Historial de Git mucho más limpio con muchas menos confirmaciones de fusión y sin bucles de fusión de ramas visuales desordenados.

¿Existen desventajas o dificultades con rebase?

Sí:

  1. Debido a que un rebasemovimiento se confirma (técnicamente, los vuelve a ejecutar), la fecha de confirmación de todas las confirmaciones movidas será el momento de la rebase y el historial de git podría parecer que perdió el tiempo de confirmación inicial . Entonces, si por algún motivo se necesita la fecha exacta de una confirmación en todas las herramientas, entonces mergees la mejor opción. Pero normalmente, un historial de git limpio es mucho más útil que las fechas de confirmación exactas. Y el campo de fecha de autor seguirá manteniendo la fecha de confirmación original cuando sea necesario.

  2. Si la rama rebasada tiene múltiples confirmaciones que cambian la misma línea y esa línea también se cambió en la rama base, es posible que deba resolver conflictos de fusión para esa misma línea varias veces, lo que nunca necesitará hacer al fusionar. Entonces, en promedio, hay más conflictos de fusión que resolver.

Tenga en cuenta que existe la idea errónea de que a rebasecausa más conflictos de fusión incluso si la misma línea no se editó varias veces. Esto se debe a que se ejecutan múltiples confirmaciones y, por lo tanto, por ejemplo, 10 conflictos que habrían surgido en una sola mergeconfirmación se distribuyen entre estas múltiples confirmaciones. Por lo tanto, recibe el mensaje "hay conflictos de fusión" de su cliente Git con más frecuencia, pero el número total de conflictos sigue siendo el mismo (a menos que haya cambiado la misma línea varias veces en su rama).

Consejos para reducir los conflictos de fusión al utilizar rebase:

  1. Rebase a menudo . Normalmente recomiendo hacerlo al menos una vez al día.
  2. Intente agrupar los cambios en la misma línea en una sola confirmación tanto como sea posible.
Jeehut avatar Oct 12 '2020 14:10 Jeehut

Para complementar mi propia respuesta mencionada por TSamper ,

  • A menudo es una buena idea realizar una rebase antes de una fusión, porque la idea es que integres en tu rama Yel trabajo de la rama Bsobre la que te fusionarás.
    Pero nuevamente, antes de fusionar, resuelve cualquier conflicto en su rama (es decir, "rebase", como en "reproducir mi trabajo en mi rama comenzando desde un punto reciente de la rama B).
    Si se hace correctamente, la fusión posterior de su rama a La rama Bpuede avanzar rápidamente.

  • una fusión afecta directamente a la rama de destino B, lo que significa que es mejor que las fusiones sean triviales; de lo contrario, esa rama Bpuede tardar mucho en volver a un estado estable (es hora de que resuelvas todos los conflictos)


¿Cuál es el punto de fusionarse después de una rebase?

En el caso que describo, cambio la base Ba mi rama, solo para tener la oportunidad de reproducir mi trabajo desde un punto más reciente B, pero mientras permanezco en mi rama.
En este caso, aún es necesaria una fusión para llevar mi trabajo "reproducido" a B.

El otro escenario ( descrito en Git Ready, por ejemplo), es traer su trabajo directamente a Btravés de una rebase (lo que conserva todas sus confirmaciones agradables, o incluso le brinda la oportunidad de reordenarlas a través de una rebase interactiva).
En ese caso (donde realiza un cambio de base mientras está en la rama B), tiene razón: no se necesita más fusión:

Un árbol de Git por defecto cuando no lo hemos fusionado ni rebasado

rebase1

obtenemos rebasando:

rebase3

Ese segundo escenario tiene que ver con: ¿cómo puedo hacer que la nueva característica vuelva al maestro?

Mi punto, al describir el primer escenario de rebase, es recordarles a todos que un rebase también se puede usar como un paso preliminar para eso (es decir, "volver a tener la nueva característica en el maestro").
Puede usar rebase para traer primero master "en" la rama de nuevas características: la rebase reproducirá las confirmaciones de nuevas características desde HEAD master, pero aún en la rama de nuevas características, moviendo efectivamente el punto de inicio de su rama desde una confirmación maestra anterior a HEAD-master.
Eso le permite resolver cualquier conflicto en su rama (es decir, de forma aislada, mientras permite que el maestro continúe evolucionando en paralelo si la etapa de resolución de conflictos lleva demasiado tiempo).
Luego puede cambiar a master y fusionar new-feature(o rebase new-featuresi masterdesea conservar las confirmaciones realizadas en su new-featurerama).

Entonces:

  • "rebase versus fusionar" puede verse como dos formas de importar un trabajo, por ejemplo, en master.
  • Pero "rebase y luego fusionar" puede ser un flujo de trabajo válido para resolver primero el conflicto de forma aislada y luego recuperar su trabajo.
VonC avatar Apr 29 '2009 20:04 VonC