W3docs

git rebase

Conoce el comando git rebase, ve ejemplos de uso y descubre la diferencia entre Git rebase estándar e interactivo.

Qué hace git rebase

Hacer rebase significa mover una serie de commits a un nuevo commit base. En otras palabras, git rebase cambia el punto de inicio de la rama actual de un commit a otro, de modo que parece como si hubieras creado la rama desde un punto diferente en la historia.

Lo clave que hay que entender: rebase no mueve tus commits originales. Crea commits completamente nuevos con los mismos cambios y mensajes, pero con commits padre diferentes y, por tanto, hashes de commit distintos (SHAs). Los commits antiguos quedan inalcanzables, aunque la rama parezca igual.

git rebase

Esta página cubre el rebase estándar, el rebase interactivo, la forma --onto, la resolución de conflictos y cómo recuperarse cuando un rebase sale mal.

Rebase vs. merge

Tanto git rebase como git merge integran cambios de una rama en otra, pero moldean el historial de forma diferente:

  • Merge crea un nuevo commit de fusión que une los historiales de ambas ramas. El historial es no lineal pero totalmente preservado — nada se reescribe.
  • Rebase reproduce tus commits encima de la rama destino. El historial permanece lineal (sin commits de fusión), pero los commits originales se reescriben.

Usa rebase cuando quieras un historial limpio y en línea recta para una rama de funcionalidad antes de compartirla. Usa merge cuando quieras preservar el historial exacto de cómo se unieron las ramas, o cuando la rama ya es pública.

Rebase estándar

En modo estándar, git rebase toma los commits exclusivos de tu rama actual y los reproduce encima de <base>. El <base> puede ser el nombre de una rama, una etiqueta o un ID de commit.

git rebase <base>

El flujo de trabajo típico: master ha avanzado desde que creaste tu rama, y quieres los últimos cambios de master por debajo de tu trabajo de funcionalidad sin un commit de fusión.

# you are on your feature branch
git switch feature
# replay feature's commits on top of the current tip of master
git rebase master

Antes del rebase, el historial tiene este aspecto (feature se ramificó desde un master más antiguo):

      A---B---C feature
     /
D---E---F---G master

Después de git rebase master, los commits de feature se recrean encima de G:

              A'--B'--C' feature
             /
D---E---F---G master

A', B' y C' llevan los mismos cambios que A, B, C, pero son commits nuevos con nuevos hashes.

Nunca hagas rebase del historial público

Nunca hagas rebase de commits que ya han sido enviados y en los que otras personas pueden haber basado su trabajo. Como rebase sustituye los commits por otros nuevos, cualquiera que haya descargado los commits antiguos encontrará su historial desincronizado — parecerá que parte del proyecto desapareció, y obtendrá commits duplicados o en conflicto en el próximo pull.

Advertencia

La regla de oro del rebase: solo haz rebase de commits que existan únicamente en tu rama local y que no hayan sido compartidos. Hacer rebase de commits ya publicados es la forma más común en que los equipos rompen los repositorios de los demás.

El límite seguro es simple: haz rebase libremente en trabajo privado; nunca hagas rebase de la rama compartida master/main ni de ninguna rama en la que otras personas estén trabajando activamente. Esta es la misma precaución que aplica a git reset y git commit --amend.

Rebase interactivo

El modo interactivo (-i, abreviatura de "interactive") te permite editar, reordenar, combinar (squash) o eliminar commits individuales mientras se reproducen. Esta es la herramienta a la que recurren los desarrolladores para limpiar una rama de funcionalidad antes de fusionarla — combinar pequeños commits de "corrección de errata", reformular mensajes poco claros y eliminar experimentos sin salida.

git rebase -i <base>

Por ejemplo, para ordenar los últimos 3 commits en tu rama:

git rebase -i HEAD~3

Esto abre tu editor con una lista "todo" — una línea por commit, el más antiguo arriba:

pick 11a1456 Add login form
pick a23db19 Fix typo in label
pick 31d332c Add password validation

# Rebase d4e5f6a..31d332c onto d4e5f6a (3 commands)
#
# Commands:
# p, pick   = use commit
# r, reword = use commit, but edit the commit message
# e, edit   = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup  = like "squash", but discard this commit's log message
# x, exec   = run command (the rest of the line) using shell
# d, drop   = remove commit

Para combinar la corrección de errata en el commit del formulario de inicio de sesión y reformular el commit de validación, cambiarías la lista a:

pick 11a1456 Add login form
fixup a23db19 Fix typo in label
reword 31d332c Add password validation

Guarda y cierra el editor; Git reproduce los commits aplicando cada instrucción en orden.

Comandos del rebase interactivo

Cada línea en la lista todo comienza con uno de estos comandos:

  • pick — mantiene el commit tal como está. Este es el valor predeterminado para cada línea.
  • reword — mantiene los cambios del commit, pero se detiene para editar su mensaje.
  • edit — se detiene en este commit para que puedas modificar su contenido (dividirlo, agregar archivos, etc.), y luego git rebase --continue.
  • squash — fusiona este commit con el anterior y combina ambos mensajes en uno.
  • fixup — como squash, pero descarta el mensaje de este commit (conserva solo el del anterior).
  • drop — elimina el commit completamente del historial.
  • exec — ejecuta un comando de shell después del commit anterior (útil para ejecutar pruebas en cada paso).

Reordenar las líneas reordena los commits; eliminar una línea equivale a drop.

La forma --onto

El indicador --onto te da control preciso sobre qué commits se mueven y dónde aterrizan:

git rebase --onto <newbase> <oldbase> <branch>

Toma los commits que están en <branch> pero no en <oldbase>, y los reproduce en <newbase>. Esta es la manera de mover una rama fuera de una base que ya no necesita.

Supongamos que featureY se ramificó desde featureX, pero en realidad es independiente de los cambios de featureX y pertenece a master:

                          o---o---o featureY
                         /
            o---o---o---o featureX
           /
o---o---o---o master

Ejecuta:

git rebase --onto master featureX featureY

Aquí featureX es el <oldbase>, master es el <newbase>, y featureY es la rama que se rebasea. Git reproduce solo los commits propios de featureY en master, separándolos de featureX:

                  o'--o'--o' featureY
                 /
o---o---o---o---o master
           \
            o---o---o---o featureX

Resolución de conflictos durante un rebase

Dado que rebase vuelve a aplicar tus commits de uno en uno, un conflicto puede detener el proceso en cualquier commit. Cuando eso ocurre, Git hace una pausa y te indica qué archivos tienen conflictos.

El flujo de trabajo para resolverlos:

# 1. fix the conflicted files in your editor, then stage them
git add <resolved-file>

# 2. continue replaying the remaining commits
git rebase --continue

Otros controles mientras un rebase está en pausa:

  • git rebase --skip — descarta el commit actual y continúa (úsalo con cuidado; perderás los cambios de ese commit).
  • git rebase --abort — detiene todo y restaura la rama exactamente como estaba antes de empezar.

Una rama de larga duración que ha divergido mucho de master produce los más conflictos, a veces el mismo conflicto en varios commits. Dos hábitos reducen el problema: haz rebase contra master con frecuencia en lugar de hacerlo una sola vez al final, y mantén tus commits pequeños y enfocados.

Opciones de configuración

Algunos valores predeterminados de rebase se pueden configurar con git config. Estos cambian el comportamiento de git rebase y lo que imprime:

  • rebase.stat — un boolean (valor predeterminado false) que activa o desactiva un diffstat visual de los cambios desde el último rebase.
  • rebase.autoSquash — un boolean que activa o desactiva el comportamiento --autosquash (reordenamiento automático de commits fixup!/squash! durante el rebase interactivo).
  • rebase.missingCommitsCheck — controla lo que ocurre cuando se eliminan commits de la lista todo. Acepta uno de los siguientes valores:
ValorComportamiento
ignoreEl valor predeterminado. Los avisos sobre commits faltantes se ignoran.
warnSe imprime una advertencia en modo interactivo sobre los commits eliminados.
errorEl rebase se detiene e imprime mensajes de advertencia sobre commits eliminados.
  • rebase.instructionFormat — un string en formato git log utilizado para dar formato a cada línea de commit que se muestra en la lista todo interactiva.

Recuperarse de un rebase fallido

Un rebase puede parecer destructivo: con squash o drop, los commits desaparecen del registro de tu rama y parece que se han ido para siempre. Pero no es así. Git mantiene un registro de dónde apuntaba tu rama antes de cada operación en el git reflog.

Para deshacer un rebase, encuentra la entrada justo antes de que ocurriera y restablece tu rama a ese punto:

# see where the branch was before the rebase
git reflog

# example output:
# a23db19 HEAD@{0}: rebase (finish): returning to refs/heads/feature
# 31d332c HEAD@{5}: rebase (start): checkout master
# c0ffee1 HEAD@{6}: commit: the state you want back

# move the branch back to the pre-rebase commit
git reset --hard HEAD@{6}

Esta es tu red de seguridad — mientras puedas leer el reflog, ningún rebase es verdaderamente irreversible.

Recuperarse de un rebase remoto

Si un compañero de equipo hace rebase y realiza un force-push de una rama en la que tú también estás trabajando, un simple git pull intentará reconciliar tus commits con el extremo remoto reescrito y puede dejarte con un historial duplicado o enredado.

La solución usa el reflog de la rama de seguimiento remoto para encontrar dónde apuntaba antes del rebase, y luego reproduce tu trabajo en el nuevo extremo con --onto:

# find the old tip of origin/feature in the remote-tracking reflog
git reflog show origin/feature

# replay your local commits from the old base onto the new one
git rebase --onto origin/feature <old-origin-tip> feature

Esto mueve solo tus commits al extremo del force-push, sin arrastrar los commits antiguos ahora reescritos.

Temas relacionados

  • git merge — la alternativa sin reescritura para integrar ramas.
  • git reflog — tu registro de deshacer para recuperarte de un rebase fallido.
  • git reset — mueve un puntero de rama, incluso de vuelta a un estado anterior al rebase.
  • git cherry-pick — reproduce commits individuales sin hacer rebase de toda una rama.

Práctica

Práctica
¿Cuáles son las afirmaciones correctas sobre el comando `git rebase` tal como se describe en el Tutorial Git de W3Docs?
¿Cuáles son las afirmaciones correctas sobre el comando `git rebase` tal como se describe en el Tutorial Git de W3Docs?
Was this page helpful?