← Volver al blog

Git rebase vs merge: cuándo usar cada uno

tutorial git

Dos formas de integrar cambios

Cuando trabajas con branches de Git, llega un momento en el que necesitas traer los cambios de una branch a otra. Git te ofrece dos estrategias principales para esto: merge y rebase. Ambas logran el mismo resultado final -- tu branch tiene todos los cambios más recientes -- pero lo hacen de maneras fundamentalmente diferentes, y elegir la correcta importa para el historial de tu proyecto y el workflow de tu equipo.

Cómo funciona Merge

Un merge toma dos branches y las combina creando un nuevo merge commit. Este merge commit tiene dos padres: la punta de tu branch actual y la punta de la branch que estás integrando.

git checkout feature/login
git merge main

Después de esto, tu branch de funcionalidad contiene todos los cambios de main, unidos por un merge commit. El historial de ambas branches se preserva exactamente como ocurrió. Si miras el grafo de commits, verás las dos líneas de desarrollo convergiendo en el punto de merge.

Las características clave del merge:

  • No destructivo. No se alteran commits existentes. El historial es exactamente lo que sucedió.
  • Crea un merge commit. Este commit adicional une los dos historiales.
  • Preserva el contexto. Siempre puedes ver cuándo se creó una branch y cuándo se fusionó.

Merge fast-forward

Si tu branch no ha divergido del objetivo (es decir, no hay nuevos commits en el objetivo desde que creaste tu branch), Git realiza un merge fast-forward por defecto. Simplemente mueve el puntero de branch hacia adelante. No se crea merge commit porque no es necesario. Puedes forzar un merge commit incluso en este caso con:

git merge --no-ff feature/login

Esto es útil cuando quieres preservar el hecho de que el trabajo se realizó en una branch separada, aunque el merge fuera trivial.

Cómo funciona Rebase

Rebase toma un enfoque diferente. En lugar de crear un merge commit, reproduce tus commits encima de otra branch. El resultado es un historial lineal que parece como si hubieras comenzado tu trabajo desde el punto más reciente de la branch objetivo.

git checkout feature/login
git rebase main

Lo que sucede entre bastidores:

  • Git encuentra el ancestro común de las dos branches.
  • Retira temporalmente los commits de tu branch.
  • Mueve el puntero de tu branch a la punta de main.
  • Reproduce tus commits uno por uno encima.

El historial resultante es perfectamente lineal. Sin merge commit, sin líneas que se bifurcan y convergen en el grafo. Parece como si hubieras escrito todo tu código después de los últimos cambios en main, aunque no sea lo que realmente ocurrió.

Las características clave del rebase:

  • Crea un historial lineal. El grafo de commits es limpio y fácil de leer.
  • Reescribe los hashes de los commits. Los commits reproducidos obtienen nuevos hashes SHA, aunque su contenido sea el mismo.
  • Sin merge commit. La branch se integra limpiamente sin commits adicionales.

Cuándo usar Merge

Merge es la opción más segura y directa en varias situaciones:

  • Branches compartidas. Cuando varias personas trabajan en la misma branch, merge preserva el historial de todos sin reescribir commits de los que otros dependen.
  • Preservar el historial es importante. En proyectos donde necesitas rastrear exactamente cuándo y cómo se integraron los cambios, merge proporciona un registro completo e inalterado.
  • Integración de branches de larga duración. Al hacer merge de una branch de release en main, o al combinar dos branches que han divergido significativamente, un merge commit marca claramente el punto de integración.
  • Quieres simplicidad. Merge es conceptualmente más fácil. Hay menos cosas que pueden salir mal, especialmente para desarrolladores nuevos en Git.

Cuándo usar Rebase

Rebase brilla cuando quieres un historial de proyecto limpio y lineal:

  • Actualizar una branch de funcionalidad. Antes de hacer merge de tu funcionalidad en main, hacer rebase sobre el último main te da un conjunto limpio de commits que se aplican directamente sin merge commit.
  • Mantener un historial limpio. Un historial lineal es más fácil de navegar con git log, más fácil de bisectar al buscar bugs, y más fácil de revisar en pull requests.
  • Limpieza local. Si has estado haciendo pequeños commits incrementales mientras trabajas, puedes usar rebase para ordenar antes de compartir tu branch.

La regla de oro del Rebase

Nunca hagas rebase de commits que han sido pushados a una branch compartida.

Esta es la regla más importante a recordar. Como rebase reescribe los hashes de los commits, cualquier persona que haya basado su trabajo en los commits originales tendrá problemas. Su historial ya no coincide con el historial rebaseado, lo que lleva a commits duplicados, conflictos y confusión.

La regla es simple: si otras personas podrían haber hecho pull de tus commits, no hagas rebase. Usa rebase solo en tus propias branches locales, o en branches donde eres el único contribuidor.

Si accidentalmente haces rebase de una branch compartida, los otros desarrolladores del equipo tendrán que lidiar con el historial divergente. Esto típicamente significa hacer force-push (que sobrescribe el remoto) y que todos los demás reseteen sus copias locales. Es disruptivo y evitable.

Rebase interactivo: reescribir el historial con precisión

El rebase interactivo (git rebase -i) va más allá de simplemente mover commits. Te permite modificar el historial de commits en sí:

git rebase -i HEAD~5

Esto abre una lista de los últimos 5 commits, donde puedes:

  • pick -- mantener el commit tal cual
  • reword -- cambiar el mensaje del commit
  • edit -- pausar para modificar el commit
  • squash -- combinar con el commit anterior
  • fixup -- como squash, pero descartando el mensaje del commit
  • drop -- eliminar el commit por completo
  • reorder -- cambiar el orden reordenando las líneas

El rebase interactivo es poderoso para limpiar una branch desordenada antes del merge. Puedes hacer squash de commits de "corregir typo" en su padre, reformular mensajes poco claros y reordenar commits para que cuenten una historia lógica.

Un workflow práctico

Muchos equipos usan un enfoque combinado que obtiene lo mejor de ambas estrategias:

  1. Crear una branch de funcionalidad desde main.
  2. Mientras trabajas, hacer rebase periódicamente de tu branch de funcionalidad sobre main para mantenerse actualizado.
  3. Antes de abrir una pull request, usar rebase interactivo para limpiar tus commits.
  4. Hacer merge de la branch de funcionalidad en main (a menudo con un merge commit para marcar el punto de integración).

Esto te da un historial limpio y lineal en la branch de funcionalidad y un punto de merge claro en main. Es un compromiso pragmático que funciona bien para la mayoría de los equipos.

Rebase y Merge en GitSquid

GitSquid soporta tanto merge como rebase directamente desde la interfaz. Puedes hacer merge de branches desde el menú contextual en cualquier etiqueta de branch en el grafo de commits. Para rebase, GitSquid incluye un editor visual de rebase interactivo que te permite reordenar, squash, fixup, reword y drop commits arrastrando y haciendo clic en lugar de editar un archivo de texto en tu terminal. Esto hace que el rebase interactivo sea accesible incluso si encuentras intimidante la versión de línea de comandos.

Resumen

Merge Rebase
Historial No lineal, preserva branches Lineal, limpio
Hashes de commits Sin cambios Reescritos
Merge commit Sí (salvo fast-forward) No
Seguro para branches compartidas No
Mejor para Integrar trabajo compartido Limpiar branches de funcionalidad

Ni merge ni rebase es universalmente mejor. Son herramientas para diferentes situaciones. Entiende lo que cada uno hace con tu historial, sigue la regla de oro del rebase y elige el que se ajuste al contexto. Tu yo futuro leyendo git log te lo agradecerá.