Fusionar & Rebase
Dos estrategias para combinar ramas — merge preserva el historial tal cual es, rebase lo reescribe para una línea de tiempo lineal.
Receta
Tarjeta de referencia rápida — lista para copiar y pegar.
# Fusionar una rama de características en main
git checkout main
git merge feature/auth-page
# Rebase de una rama de características sobre el último main
git checkout feature/auth-page
git rebase main
# Rebase interactivo — comprime, reordena, edita commits
git rebase -i main
# Abortir un merge o rebase en conflicto
git merge --abort
git rebase --abortCuándo usarlo: Cada vez que necesites integrar cambios de una rama en otra.
Ejemplo Funcional
Flujo de Merge
# Actualiza main
git checkout main
git pull origin main
# Fusiona la rama de características
git merge feature/user-profile
# Si ocurren conflictos, resuélvelos:
# 1. Abre los archivos en conflicto (marcados con <<<<<<< / ======= / >>>>>>>)
# 2. Edita para mantener el código correcto
# 3. Coloca los archivos resueltos en staging
git add src/components/UserProfile.tsx
# 4. Completa el merge
git commitLo que esto demuestra:
- Siempre tira (
pull) el últimomainantes de fusionar - Git marca conflictos con los marcadores
<<<<<<<,=======,>>>>>>> - Después de resolver conflictos, coloca en staging y confirma para completar el merge
Flujo de Rebase
# Comienza en tu rama de características
git checkout feature/dashboard
# Rebase sobre el último main
git fetch origin
git rebase origin/main
# Si ocurren conflictos durante rebase:
# 1. Resuelve el conflicto en el archivo
# 2. Coloca el archivo resuelto en staging
git add src/app/dashboard/page.tsx
# 3. Continúa el rebase
git rebase --continue
# Force push después de rebase (requerido porque el historial cambió)
git push --force-with-leaseLo que esto demuestra:
git fetch+git rebase origin/mainevita necesitar hacer checkout de main primero- Rebase reproduce tus commits uno a la vez encima de la rama destino
--force-with-leasees más seguro que--force— se rehúsa a hacer push si el remoto tiene commits que no has visto
Análisis Profundo
Merge vs Rebase — Cuándo Usar Cada Uno
| Merge | Rebase | |
|---|---|---|
| Historial | Preserva todos los commits y crea un merge commit | Reescribe commits para un historial lineal |
| Conflictos | Resuelve una vez en el merge commit | Resuelve por-commit durante la reproducción |
| Ramas compartidas | Seguro — nunca reescribe historial publicado | Peligroso en ramas compartidas — reescribe commits en los que otros pueden haber basado trabajo |
| Mejor para | Integraciones de rama principal, ramas de lanzamiento | Mantener ramas de características actualizadas, limpiar antes de PR |
Regla de oro: Haz rebase de tus propias ramas de características. Fusiona en ramas compartidas.
Rebase Interactivo
El rebase interactivo te permite limpiar commits antes de fusionar un PR.
# Rebase de los últimos 4 commits
git rebase -i HEAD~4Esto abre un editor con tus commits:
pick abc1234 feat: add dashboard layout
pick def5678 fix: typo in dashboard
pick ghi9012 feat: add chart component
pick jkl3456 fix: chart responsive issue
Operaciones comunes:
# Comprime una corrección en el commit anterior
pick abc1234 feat: add dashboard layout
squash def5678 fix: typo in dashboard
pick ghi9012 feat: add chart component
squash jkl3456 fix: chart responsive issue
# Reescribe un mensaje de commit
reword abc1234 feat: add dashboard layout
# Reordena commits (simplemente mueve líneas)
pick ghi9012 feat: add chart component
pick abc1234 feat: add dashboard layout
# Elimina un commit completamente
drop def5678 fix: typo in dashboard
Resolviendo Conflictos de Merge
Cuando Git no puede hacer auto-merge, marca el archivo:
<<<<<<< HEAD
export function Header({ title }: { title: string }) {
return <h1 className="text-2xl font-bold">{title}</h1>;
=======
export function Header({ title, subtitle }: HeaderProps) {
return (
<header>
<h1 className="text-3xl font-bold">{title}</h1>
<p className="text-muted-foreground">{subtitle}</p>
</header>
);
>>>>>>> feature/header-redesign
}Pasos para resolver:
- Elimina todos los marcadores de conflicto (
<<<<<<<,=======,>>>>>>>) - Mantén el código que quieres (o combina ambos cambios)
- Coloca el archivo en staging y continúa el merge/rebase
Fast-Forward vs No-Fast-Forward
# Merge de fast-forward (sin merge commit, historial lineal)
git merge --ff-only feature/small-fix
# Fuerza un merge commit incluso cuando fast-forward es posible
git merge --no-ff feature/auth-page- Fast-forward mueve el puntero de rama hacia adelante — sin merge commit, historial limpio. Solo es posible cuando no hay commits divergentes en la rama destino.
- No-fast-forward siempre crea un merge commit — útil para preservar el hecho de que una rama de características existió.
Trampas
Cosas que te pueden causar problemas. Cada trampa incluye qué sale mal, por qué sucede, y la solución.
-
Hacer rebase de una rama compartida — Si otros han basado trabajo en tu rama y haces force-push de un rebase, su historial diverge. Solución: Solo haz rebase de ramas que son solo tuyas. Usa merge para ramas compartidas.
-
Force push destruyendo trabajo —
git push --forcesobrescribe el remoto incondicionalmente. Solución: Siempre usagit push --force-with-leaseque se rehúsa si alguien más hizo push desde tu último fetch. -
Fatiga de conflictos durante rebase — Un rebase a través de muchos commits puede exponer el mismo conflicto repetidamente. Solución: Usa
git rerere(reusa resolución registrada) para resolver automáticamente conflictos repetidos:git config rerere.enabled true. -
Commits perdidos después de rebase — Los commits parecen desaparecer después de un rebase fallido. Solución:
git reflogmuestra todas las posiciones recientes de HEAD. Encuentra el hash del commit ygit checkout -b recovery <hash>. -
Ruido de merge commit — Los merge commits frecuentes de tirar (
pull)mainen tu rama de características ensucian el historial. Solución: Usagit pull --rebaseen su lugar, o configúralo globalmente:git config pull.rebase true.
Alternativas
Otras formas de resolver el mismo problema — y cuándo cada una es la mejor opción.
| Alternativa | Usa Cuando | No Uses Cuando |
|---|---|---|
git cherry-pick | Necesitas un commit específico de otra rama | Necesitas todos los cambios de una rama |
Squash merge (gh pr merge --squash) | La rama de características tiene commits WIP desordenados | Quieres preservar el historial de commits individuales |
git merge --squash | Combina todos los cambios en un commit localmente | La rama tiene commits atómicos significativos que vale la pena mantener |
FAQs
¿Cuándo debo usar merge vs rebase?
- Haz rebase de tus propias ramas de características para mantenerte actualizado con
main - Fusiona en ramas compartidas como
mainodevelop - Nunca hagas rebase de una rama en la que otros hayan basado trabajo
¿Qué hace --force-with-lease, y por qué es más seguro que --force?
--force-with-leasese rehúsa a hacer push si la rama remota tiene commits que no has descargado--forcesobrescribe el remoto incondicionalmente, potencialmente destruyendo el trabajo de tus compañeros- Siempre usa
--force-with-leasedespués de un rebase
¿Cómo comprimo múltiples commits en uno usando rebase interactivo?
git rebase -i HEAD~4
# Cambia "pick" a "squash" (o "s") en los commits que quieres plegar
# Guarda y cierra el editor, luego edita el mensaje de commit combinado¿Qué significan los marcadores <<<<<<<, =======, y >>>>>>>?
<<<<<<< HEADmarca el inicio de la versión de tu rama actual=======separa las dos versiones en conflicto>>>>>>> branch-namemarca el final de la versión de la rama entrante- Elimina los tres marcadores y mantén el código que quieres
Trampa: ¿Por qué sigo resolviendo el mismo conflicto durante un rebase?
- Rebase reproduce commits uno a la vez, así que el mismo conflicto puede aparecer múltiples veces
- Habilita
git rererepara resolver automáticamente conflictos repetidos:
git config --global rerere.enabled true¿Cuál es la diferencia entre merges --ff-only y --no-ff?
--ff-onlymueve el puntero de rama hacia adelante sin un merge commit (historial lineal)--no-ffsiempre crea un merge commit, preservando que una rama de características existió--ff-onlyfalla si las ramas han divergido
¿Puedo recuperar commits perdidos después de un rebase fallido?
git reflog
# Encuentra el hash del commit antes de que comenzara el rebase
git checkout -b recovery <hash>- El reflog mantiene un registro de todos los movimientos de HEAD durante ~90 días
Trampa: Hice rebase de una rama compartida y el historial de mi compañero divergió. ¿Qué ahora?
- Tu compañero debe ejecutar
git fetch originluegogit reset --hard origin/branch-name(si no tiene commits solo-locales) - En adelante, solo haz rebase de ramas que son solo tuyas
¿Cómo aborto un merge o rebase que ha salido mal?
git merge --abort
# o
git rebase --abort- Ambos comandos retornan tu rama al estado antes de que la operación comenzara
En un monorepo TypeScript, ¿debo hacer squash merge de PRs de características?
- El squash merge es ideal cuando la rama de características tiene muchos commits WIP o de corrección
- Produce un commit limpio en
main, haciendogit bisectmás fácil - Evita squash si los commits individuales tienen cambios atómicos significativos que merecen ser preservados
¿En qué difiere git pull --rebase de un git pull regular?
git pull=git fetch+git merge(crea un merge commit si las ramas divergieron)git pull --rebase=git fetch+git rebase(reproduce tus commits locales encima del remoto)- Usa
--rebasepara evitar ruidosos merge commits cuando actualizas una rama de características
¿Cómo resuelvo un conflicto TypeScript donde ambas ramas agregaron diferentes definiciones de tipo?
- Abre el archivo
.tsen conflicto y combina ambas definiciones de tipo si son compatibles - Ejecuta
npx tsc --noEmitdespués de resolver para verificar que los tipos fusionados compilen - Coloca el archivo en staging y continúa el merge o rebase
Relacionado
- Essential Git Commands — Staging, committing, branching basics
- GitHub CLI — Merge PRs and manage branches from the terminal
- Git Utilities — Cherry-pick, stash, bisect, and reflog