git rebase
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)Opciones
Elegir la opción adecuada
git rebase frente a git merge
Saber cuándo usar rebase o merge para integrar cambios
| Comando | Aspecto | Git Rebase | Git Merge |
|---|---|---|---|
History | Lineal (reescribe los commits) | Conserva el historial divergente | |
Commits | Crea commits NUEVOS con SHA distintos | Conserva los commits originales + un commit de merge | |
Destructive | Sí: cambia los hashes de los commits | No: solo añade un commit de merge | |
Conflicts | Se resuelven commit a commit durante la reproducción | Se resuelven una vez en el punto de merge | |
Best for | Ramas locales/privadas antes del push | Ramas públicas/compartidas | |
Traceability | Cuesta más ver los puntos de integración | Registro claro de la integración de ramas |
Ejemplos de flujos de trabajo
git fetch originTraer los últimos cambios del remoto
git checkout featureCambiarse a tu rama de funcionalidad
git rebase origin/mainReproducir tus commits encima del último main
git push --force-with-leaseSubir la rama rebasada (solo si no es compartida)
git log --onelineContar cuántos commits combinar
git rebase -i HEAD~5Rebase interactivo de los 5 últimos commits
(in editor) pick, squash, squash...Conservar el primero y combinar el resto con él
(edit combined message)Escribir un mensaje de commit limpio
(make your fix)Arreglar el problema en el código
git commit --fixup=<commit-hash>Crear un commit fixup para el destino
git rebase --autosquash mainAutosquash ordena y combina automáticamente (desde Git 2.44 no hace falta -i)
git rebase -i HEAD~4Rebase interactivo de los 4 últimos commits
(in editor) reorder linesMover las líneas de los commits a otras posiciones
save and close editorGit los reproduce en el nuevo orden
git rebase -i HEAD~3Empezar un rebase interactivo
(mark commit with 'edit')Detenerse en el commit que se va a dividir
git reset HEAD^Deshacer el commit y dejar los cambios sin preparar en el directorio de trabajo
git add file1 && git commit -m 'Part 1'Crear el primer commit pequeño
git add file2 && git commit -m 'Part 2'Crear el segundo commit pequeño
git rebase --continueContinuar con los commits restantes
git reflogEncontrar el commit anterior al rebase
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 --continueCannot 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 mainUn 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 --skipEl 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 --abortCommits 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-leasegit 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 mainLa 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..HEADConsejos 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
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.
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.
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'.
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.
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.
--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.
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.
--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.
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.
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.