git rebase

Ramas y merges

Volver a aplicar commits sobre otra base


git rebase mueve o combina una serie de commits sobre un nuevo commit base. Se usa mucho para mantener un historial lineal reproduciendo los commits de una rama de funcionalidad encima del último main. A diferencia de merge, rebase reescribe el historial creando commits nuevos con SHA distintos. El rebase interactivo (-i) te permite combinar, reordenar, editar o quitar commits antes de reproducirlos.

Sinopsis

git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase> | --keep-base] [<upstream> [<branch>]]
git rebase [-i | --interactive] [<options>] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
git rebase (--continue | --skip | --abort | --quit | --edit-todo | --show-current-patch)
[ ] opcional| o<arg> valor a sustituir... repetible--flag opción

Opciones

Haz clic en una opción para desplegarla

Principiante (3)

Continuar el rebase tras resolver los conflictos. Ejecútalo después de preparar los archivos resueltos con git add.

RecomendadoLa forma normal de continuar tras resolver y preparar los conflictos

Ejemplo

git rebase --continue

Cancelar el rebase y volver al estado original, antes de que empezara. Úsalo cuando los conflictos sean demasiado complejos o hayas cambiado de idea.

RecomendadoSalida de emergencia segura: devuelve tu rama al estado previo al rebase

Ejemplo

git rebase --abort

Saltar el commit actual y seguir con el siguiente. Úsalo cuando un commit queda vacío o sus cambios ya no hacen falta.

Cuando haga faltaDescarta el commit actual: solo cuando sus cambios de verdad no se quieren

Ejemplo

git rebase --skip

Intermedio (3)

Abrir un editor para reordenar, combinar, editar o quitar commits antes de reproducirlos. La forma más potente de limpiar el historial.

RecomendadoLa mejor forma de ordenar commits locales antes de compartirlos

Ejemplo

git rebase -i HEAD~3

Guardar automáticamente los cambios sin commit antes del rebase y restaurarlos después. Te ahorra hacer stash a mano.

Cuando haga faltaÚtil con el árbol sucio, pero volver a aplicar el stash puede dar conflictos

Ejemplo

git rebase --autostash main

Forzar el rebase aunque la rama esté al día. Crea commits nuevos con hashes nuevos para todos los commits del rango.

Cuando haga faltaSolo cuando necesites hashes nuevos para commits que ya están al día

Ejemplo

git rebase -f main

Avanzado (5)

Trasplantar commits a otra base. Te permite hacer rebase de los commits de una rama sobre otro commit concreto.

Cuando haga faltaMueve un rango preciso de commits a una base nueva: revisa bien el rango

Ejemplo

git rebase --onto main feature~3 feature

Mover y combinar automáticamente los commits fixup!/squash! (creados con git commit --fixup o --squash) con sus destinos. Funciona con o sin -i desde Git 2.44.

RecomendadoVa de la mano de commit --fixup para limpiar el historial sin esfuerzo

Ejemplo

git rebase -i --autosquash main

Conservar los commits de merge y la estructura de ramas durante el rebase. Recrea la topología de los merges en lugar de aplanar el historial.

Cuando haga faltaCuando la estructura de merges de una rama debe sobrevivir al rebase

Ejemplo

git rebase -r main

Ejecutar un comando de shell tras aplicar cada commit. Útil para pasar los tests y comprobar que cada commit compila.

Cuando haga faltaEjecuta pruebas en cada commit reaplicado; lento en ramas largas

Ejemplo

git rebase -i -x 'npm test' main

Hacer rebase de todos los commits alcanzables desde HEAD, incluido el commit raíz. Te permite reescribir todo el historial.

No recomendadoReescribe todo el historial desde el primer commit; casi nunca hace falta

Ejemplo

git rebase -i --root

Elegir la opción adecuada

git rebase frente a git merge

Saber cuándo usar rebase o merge para integrar cambios

ComandoAspectoGit RebaseGit Merge
HistoryLineal (reescribe los commits)Conserva el historial divergente
CommitsCrea commits NUEVOS con SHA distintosConserva los commits originales + un commit de merge
DestructiveSí: cambia los hashes de los commitsNo: solo añade un commit de merge
ConflictsSe resuelven commit a commit durante la reproducciónSe resuelven una vez en el punto de merge
Best forRamas locales/privadas antes del pushRamas públicas/compartidas
TraceabilityCuesta más ver los puntos de integraciónRegistro claro de la integración de ramas

Ejemplos de flujos de trabajo

1
git fetch origin

Traer los últimos cambios del remoto

2
git checkout feature

Cambiarse a tu rama de funcionalidad

3
git rebase origin/main

Reproducir tus commits encima del último main

4
git push --force-with-lease

Subir la rama rebasada (solo si no es compartida)

1
git log --oneline

Contar cuántos commits combinar

2
git rebase -i HEAD~5

Rebase interactivo de los 5 últimos commits

3
(in editor) pick, squash, squash...

Conservar el primero y combinar el resto con él

4
(edit combined message)

Escribir un mensaje de commit limpio

1
(make your fix)

Arreglar el problema en el código

2
git commit --fixup=<commit-hash>

Crear un commit fixup para el destino

3
git rebase --autosquash main

Autosquash ordena y combina automáticamente (desde Git 2.44 no hace falta -i)

1
git rebase -i HEAD~4

Rebase interactivo de los 4 últimos commits

2
(in editor) reorder lines

Mover las líneas de los commits a otras posiciones

3
save and close editor

Git los reproduce en el nuevo orden

1
git rebase -i HEAD~3

Empezar un rebase interactivo

2
(mark commit with 'edit')

Detenerse en el commit que se va a dividir

3
git reset HEAD^

Deshacer el commit y dejar los cambios sin preparar en el directorio de trabajo

4
git add file1 && git commit -m 'Part 1'

Crear el primer commit pequeño

5
git add file2 && git commit -m 'Part 2'

Crear el segundo commit pequeño

6
git rebase --continue

Continuar con los commits restantes

1
git reflog

Encontrar el commit anterior al rebase

2
git reset --hard HEAD@{n}

Volver al estado previo al rebase

Solución de problemas

CONFLICTO durante el rebase

Causa: Los cambios de tu commit chocan con la rama base

Edita los archivos para resolver los conflictos, haz git add y luego git rebase --continue

git add <resolved-files> && git rebase --continue

Cannot rebase: You have unstaged changes

Causa: El directorio de trabajo tiene modificaciones sin commit

Haz commit, stash o usa --autostash para gestionar los cambios sin commit

git rebase --autostash main

Un commit queda vacío tras el rebase

Causa: Los cambios de este commit ya existen en la rama de destino

Un rebase normal lo quita automáticamente; el interactivo se detiene: ejecuta --skip para quitarlo o 'git commit --allow-empty' para conservarlo

git rebase --skip

El rebase provoca muchos conflictos

Causa: La rama se ha separado mucho de la base

Plantéate usar merge o cancela y haz el rebase en pasos más pequeños

git rebase --abort

Commits perdidos tras el rebase

Causa: Los commits originales quedan huérfanos (no se borran, solo dejan de estar referenciados)

Usa el reflog para encontrar y recuperar los commits originales

git reflog && git branch recovery HEAD@{n}

Push rechazado (non-fast-forward) tras el rebase

Causa: El rebase reescribió los commits, así que tu rama ya no contiene el historial de la copia remota

Usa --force-with-lease para un force push más seguro (se niega si otra persona subió algo mientras tanto)

git push --force-with-lease

git rebase dice «Current branch is up to date»

Causa: No hay commits que reproducir: la rama ya está basada en el destino

Usa -f para forzar el rebase igualmente si necesitas hashes nuevos

git rebase -f main

La lista de tareas del rebase interactivo está vacía

Causa: No hay commits en el rango indicado

Revisa el rango: usa HEAD~n o indica el upstream correcto

git log --oneline main..HEAD

Consejos y trampas

Buenas prácticas

  • ✓Nunca hagas rebase de commits subidos a un repositorio compartido: obliga a todos a arreglar sus ramas a mano
  • ✓Usa siempre 'git rebase --abort' si los conflictos se complican demasiado: podrás probar otro enfoque
  • ✓Usa el rebase interactivo (-i) para limpiar los commits antes de subirlos o de crear un pull request
  • ✓Plantéate usar '--autostash' para gestionar automáticamente los cambios sin commit durante el rebase
  • ✓Usa 'git commit --fixup=<hash>' con '--autosquash' para un flujo de correcciones más limpio
  • ✓Ejecuta los tests después del rebase para asegurarte de que los commits reproducidos siguen funcionando
  • ✓Usa '-x npm test' para ejecutar los tests automáticamente tras cada commit reproducido
  • ✓Mantén los rebases pequeños: haz rebase a menudo en lugar de dejar que las ramas se separen mucho
  • ✓Usa 'git reflog' para recuperarte si un rebase sale mal: los commits originales nunca se borran al momento
  • ✓En ramas públicas/compartidas, mejor merge que rebase para conservar el historial
  • ✓Usa '--force-with-lease' en lugar de '--force' al subir ramas con rebase, por seguridad
  • ✓Al combinar commits, escribe un mensaje nuevo y descriptivo que resuma todos los commits combinados

Errores comunes

  • ✗Hacer rebase de commits ya subidos a un repositorio compartido. Reescribe el historial y obliga a todos los demás a arreglar sus ramas a mano.
  • ✗Olvidar preparar los conflictos resueltos antes de --continue. Ejecuta siempre 'git add' sobre los archivos resueltos primero.
  • ✗Usar --skip a la ligera, lo que quita los cambios del commit de la rama rebasada. Úsalo solo cuando quieras eliminar un commit a propósito.
  • ✗No entender que rebase crea commits NUEVOS con hashes distintos. Los commits originales quedan huérfanos.
  • ✗Hacer rebase con cambios sin commit en el directorio de trabajo. Usa --autostash o haz stash antes a mano.

Preguntas frecuentes

A:

Merge conserva el historial creando un commit de merge y deja intactos los historiales de ambas ramas. Rebase reescribe el historial reproduciendo tus commits encima de otra rama, lo que da un historial lineal. Usa merge en ramas compartidas y rebase para la limpieza local.

A:

El rebase cambia los hashes de los commits. Si otras personas basaron su trabajo en tus commits originales, su historial se separará del tuyo. Tendrán que arreglar sus ramas a mano, algo pesado y propenso a errores.

A:

Usa 'git reflog' para encontrar el commit anterior al rebase (busca la punta antigua de la rama) y luego 'git reset --hard HEAD@{n}' para restaurarlo. Por defecto, los commits originales siguen alcanzables desde el reflog durante 30 días. Justo después de un rebase, también funciona 'git reset --hard ORIG_HEAD'.

A:

En el rebase interactivo: 'pick' conserva el commit tal cual, 'squash' lo combina con el commit anterior (conservando ambos mensajes), 'fixup' lo combina pero descarta su mensaje, y 'reword' conserva el commit pero te deja editar el mensaje.

A:

Ejecuta 'git rebase -i HEAD~n', donde n es el número de commits. En el editor, deja 'pick' en el primero y cambia el resto por 'squash' o 'fixup'. Guarda y edita el mensaje del commit combinado.

A:

--onto te permite trasplantar commits a otra base. Por ejemplo, «git rebase --onto main feature~3 feature» mueve solo los 3 últimos commits de feature sobre main y se salta los anteriores.

A:

Edita los archivos en conflicto para resolverlos, ejecuta 'git add' sobre los resueltos y luego 'git rebase --continue'. Si se complica demasiado, 'git rebase --abort' te devuelve al estado previo al rebase.

A:

--preserve-merges se eliminó en Git 2.34 (ahora git falla con «--preserve-merges was replaced by --rebase-merges»). Usa --rebase-merges (-r) para conservar los commits de merge y la estructura de ramas durante el rebase. Recrea la topología de los merges sobre la nueva base.

A:

Sí: usa 'git rebase -i' y marca con 'pick' los commits que quieras conservar y con 'drop' los que quieras quitar. O usa --onto para indicar exactamente qué rango de commits mover.

A:

El hash de un commit se calcula a partir de su contenido, mensaje, autor, fecha Y commit padre. Como el rebase cambia el padre, todos los commits siguientes reciben hashes nuevos aunque su contenido sea idéntico.