Git rebase explicado: qué hace y cuándo usarlo

Git rebase reproduce tus commits sobre una base nueva. Qué hace, cuándo usarlo en vez de merge y la regla de oro para no romperle el historial al equipo.

Git rebase explicado: qué hace y cuándo usarlo

Terminas tu rama de feature, haces git merge main para ponerte al día, y aparece un commit que no escribiste tú: “Merge branch ‘main’ into feature”. No aporta nada. Solo ensucia el historial. Un compañero mira tu pull request y te suelta un “esto hazlo con rebase”. Y ahí empieza la duda, porque git rebase tiene fama de magia peligrosa que te puede romper el repo. Vamos a quitarle el misterio: qué le hace exactamente a tus commits, cuándo conviene usarlo en vez de merge, y la única regla que no puedes saltarte.

¿Qué hace git rebase exactamente?

git rebase coge los commits de tu rama y los vuelve a aplicar, uno detrás de otro, sobre una base distinta. La palabra clave es reaplicar. Git no arrastra tus commits de sitio como quien mueve una caja: los reproduce desde cero encima de la nueva base, y al hacerlo genera commits nuevos con hashes nuevos[1].

Ese detalle es el que confunde a todo el mundo al principio. Tú ves “los mismos commits”, con el mismo mensaje y los mismos cambios, pero para git son otros. El hash de un commit se calcula a partir de su contenido, su padre y metadatos como el autor y la fecha. Al cambiar el padre (ahora cuelgan del último estado de main en vez del viejo), el hash cambia. Tus commits originales no se borran de golpe: quedan un tiempo en el reflog, huérfanos, hasta que git los recolecta. Lo que ves en tu rama son copias.

Piénsalo como reescribir a limpio la última página de un cuaderno. El texto es el mismo, pero es una hoja nueva. La vieja sigue por ahí, arrancada, hasta que la tiras.

Rebase paso a paso: el flujo típico

El caso de uso más común cabe en dos líneas: te pones encima de la última versión de main sin generar un commit de merge.

# Estás en tu rama de feature y quieres ponerla al día con main
git switch feature
git rebase main
# Successfully rebased and updated refs/heads/feature.

git switch es la forma moderna y estable de cambiar de rama. El aviso de “experimental” que quizá recuerdes ya no aplica: hoy es un comando estable y recomendado[2], así que puedes usarlo con total confianza en lugar del viejo git checkout.

¿Y qué significa “reproducir sobre otra base” mirando los hashes? Esto lo deja claro:

# Antes del rebase: tus commits cuelgan de un main desactualizado
git log --oneline
# 9c4f1a2 (feature) valida el email en el registro
# 3e7b8d0 crea el formulario de registro

# Después de git rebase main: mismos cambios, hashes nuevos
git log --oneline
# a1f92c7 (feature) valida el email en el registro
# b6d0e34 crea el formulario de registro

Mismo trabajo, misma descripción, identificadores distintos. Esa es toda la mecánica. El resto (cuándo hacerlo, cuándo no) sale de entender esta consecuencia.

¿En qué se diferencia de un merge?

Un merge une dos ramas creando un commit extra que tiene dos padres; un rebase evita ese commit extra reescribiendo tus commits encima de la otra rama. El resultado se ve distinto en el historial. Con merge, el git log conserva la bifurcación real: aquí me separé de main, aquí trabajé, aquí volví a juntarme. Con rebase, la línea queda recta, como si hubieras empezado tu feature justo desde el último commit de main.

git merge maingit rebase main
Historial resultanteRamificado, con la bifurcación visibleLineal, sin bifurcación
Commit de mergeSí, uno extra con dos padresNo, ninguno
Hashes de tus commitsIntactos, los de siempreNuevos, reescritos
Cuándo encajaIntegrar ramas ya compartidas sin tocar su historiaLimpiar tu rama local antes de integrarla
Comparación del grafo de commits resultante de un git merge frente a un git rebase partiendo de la misma rama feature divergida de main: el merge añade un commit de fusión con dos padres y conserva la bifurcación, el rebase reescribe los commits de feature con hashes nuevos encima de main dejando un historial en línea recta.
La misma rama feature integrada de dos formas: merge conserva la bifurcación real y añade un commit de fusión; rebase reescribe los commits con hashes nuevos y deja el historial en línea recta.

Ninguno es “mejor” en abstracto. Merge preserva el contexto real de cuándo integraste algo, y eso a veces importa. Rebase da un historial más fácil de leer, y eso otras veces importa más. Si quieres la comparativa a fondo entre las dos estrategias, la tienes en el post sobre comparativa a fondo entre merge y rebase. Y si todavía te falta la base de por qué integramos ramas y cómo encaja todo esto, empieza por la guía completa de ramas en Git.

La regla de oro: nunca reescribas historia compartida

No hagas rebase de commits que ya has empujado a una rama que otros usan. Esta es la única regla del rebase que no admite excepciones, y entender el porqué te ahorra el peor día de tu semana.

Recuerda lo de antes: rebase cambia los hashes. Cuando reescribes commits que solo existen en tu máquina, no pasa nada, nadie más los ha visto. Pero si esos commits ya están en main o en una rama compartida, tus compañeros tienen los hashes viejos en sus repos. Tú los reescribes, empujas, y de repente el historial de todos deja de coincidir con el tuyo. La documentación de Git lo dice sin rodeos: reescribir una rama sobre la que otros han basado su trabajo es una mala idea, porque cualquiera aguas abajo se ve forzado a arreglar su historial a mano[1].

La versión práctica que puedes memorizar: rebase para lo tuyo, lo local, lo que aún no ha salido de tu ordenador. Para lo que ya es de todos, merge.

¿Y si aparece un conflicto durante el rebase?

Si git no puede reaplicar limpiamente uno de tus commits sobre la nueva base, se detiene en ese commit y te deja resolver el conflicto a mano. El ciclo es el mismo que ya conoces de un merge, con dos comandos propios del rebase al final:

git switch feature
git rebase main
# CONFLICT (content): Merge conflict in src/auth.ts
# error: could not apply 3e7b8d0... crea el formulario de registro

git status              # ver en qué archivos se paró
# abre src/auth.ts y resuelve los marcadores <<<<<<< ======= >>>>>>>
git add src/auth.ts     # marca ese archivo como resuelto
git rebase --continue   # git retoma el rebase desde donde se detuvo

Si el rebase arrastra varios commits, git puede pararse más de una vez, una por cada commit conflictivo. Resuelves, git add, git rebase --continue, y repites hasta que termine. No es un bucle infinito: cada parada avanza uno.

¿Y si te agobias y prefieres salir? git rebase --abort cancela todo y te devuelve tu rama exactamente como estaba antes de empezar, con los hashes viejos y sin cambios[1]. Es la red de seguridad que hace que el rebase dé menos miedo de lo que parece. El mecanismo de resolver los marcadores de conflicto es idéntico al de un merge normal, así que si esa parte todavía se te atraganta, repásala en cómo resolver conflictos de fusión en Git.

Entonces, ¿cuándo uso rebase y cuándo merge?

Usa rebase para dejar tu rama limpia antes de integrarla, y merge para juntar trabajo que ya es compartido. Con esos dos casos cubres la gran mayoría de lo que te vas a encontrar.

Rebase encaja cuando quieres ponerte al día con main sin ensuciar tu historial con un commit de merge, o cuando vas a abrir un pull request y prefieres que tus commits cuelguen limpios de la última versión de main. Merge encaja cuando integras una rama que otros ya han descargado, o cuando quieres que quede constancia explícita de “aquí se fusionó esta feature”. Muchos equipos combinan las dos: rebase en local mientras trabajas, merge al final para integrar en la rama principal.

Si estos comandos te suenan a idioma nuevo y quieres construir el modelo mental de Git desde los cimientos en vez de memorizar recetas sueltas, este tema es una de las piezas del curso Git de cero a profesional, donde el flujo de ramas y la integración se practican paso a paso.

Errores comunes

Hacer rebase de una rama que otros ya tienen

Es la regla de oro incumplida, y merece repetirse porque es el error que de verdad rompe cosas. Reescribes main o una rama compartida, empujas, y le fastidias el historial a todo el equipo. Antes de un rebase, hazte una pregunta: ¿estos commits los ha visto alguien más? Si la respuesta es sí, no rebases.

Forzar el push a ciegas con --force

Después de un rebase, tus hashes locales no coinciden con los del remoto, así que un git push normal es rechazado y git te pide forzar. Aquí mucha gente teclea git push --force sin pensar y machaca lo que hubiera en el remoto, incluido trabajo que otro haya subido mientras tanto. Usa git push --force-with-lease: solo empuja si nadie ha tocado la rama desde tu última sincronización, y si alguien lo hizo, se detiene y te avisa[3]. Es más seguro que --force, aunque no es una garantía mágica: sigue reescribiendo el remoto, así que reserva el push forzado para tus propias ramas.

Entrar en pánico durante un conflicto

Te paras en mitad de un rebase, ves marcadores de conflicto por todas partes y no recuerdas cómo salir. git rebase --abort. Eso es todo lo que necesitas para volver al punto de partida. Escríbelo en un pósit si hace falta.

Creer que rebase “mueve” tus commits

No los mueve: crea copias con hashes nuevos y deja los originales huérfanos en el reflog. Esto importa porque significa que un rebase casi nunca es irreversible. Si te arrepientes después de terminar, git reflog te enseña dónde estaban tus commits antiguos y puedes volver a ellos con git reset --hard <hash>.

Checklist antes de hacer rebase

  • Los commits que voy a reescribir solo existen en mi máquina (nadie más los ha descargado)
  • Estoy en la rama correcta (git switch feature antes de git rebase main)
  • Sé que los hashes de mis commits van a cambiar tras el rebase
  • Si aparece un conflicto, resuelvo, git add y git rebase --continue
  • Si me lío, git rebase --abort me devuelve al estado inicial
  • Al empujar uso git push --force-with-lease, nunca --force a ciegas

Si quieres recibir las próximas guías de Git y flujo de trabajo con ejemplos prácticos, déjame tu correo:

Un concepto nuevo cada semana

Fuentes

  1. Documentación oficial de Git: git-rebase. Semántica de reaplicar commits creando copias nuevas, ciclo --continue/--abort y advertencia sobre reescribir historia sobre la que otros han basado su trabajo.
  2. Documentación oficial de Git: git-switch. git switch como comando estable para cambiar de rama.
  3. Documentación oficial de Git: git-push. --force-with-lease como alternativa más segura que --force al empujar una rama reescrita.

Preguntas Frecuentes

¿Rebase borra mis commits?

No: crea copias con hashes nuevos; los originales quedan en el reflog un tiempo.

¿Puedo deshacer un rebase?

Sí. Si estás en mitad de uno, git rebase --abort te devuelve al estado inicial. Si ya terminó y quieres volver atrás, usa git reflog para localizar el hash donde estaba tu rama antes del rebase y git reset --hard <hash> para regresar a ese punto.

¿Rebase o merge para ponerme al día con main?

Rebase si esos commits solo existen en tu rama local, porque te deja un historial lineal sin un commit de merge que no aporta nada. Merge si la rama ya es compartida y reescribirla afectaría a otros. La duda casi siempre se resuelve con la misma pregunta: ¿han visto estos commits en algún otro sitio?

¿Por qué me pide --force al hacer push después de un rebase?

Porque el rebase reescribió los hashes de tus commits y ahora tu rama local y la remota tienen historias distintas. Git rechaza el push normal para que no borres nada sin querer. La forma sensata de forzarlo es git push --force-with-lease, que solo empuja si nadie ha tocado la rama remota desde tu última sincronización.