¿Git Rebase o Merge? Elige la estrategia correcta para tu equipo
1. Introducción
git merge y git rebase se usan para integrar cambios entre dos ramas, pero crean un historial de commits muy diferente. Elegir la estrategia incorrecta hace que el log sea confuso, difícil de revisar, o peor aún, daña el historial de tus compañeros. Este artículo te ayuda a comprender el mecanismo de cada comando a través de ejemplos concretos, saber cuándo hacer merge, cuándo hacer rebase, y evitar los errores más comunes al usar rebase en equipo.
2. Contexto del ejemplo
Supongamos que estás trabajando en la rama feature/payment basada en main. Mientras codificas, tus compañeros han fusionado algunos commits en main.
git log --oneline --graph --all
* c3f9a1d (main) Agrega PaymentValidator
* 7b2e4aa (main) Corrige bug en OrderService
| * 9d1a0ef (feature/payment) Agrega PaymentService.processPayment()
| * 4f3c2bb (feature/payment) Inicializa PaymentService
|/
* a1b2c3d Commit base compartido
Las dos ramas se han separado desde a1b2c3d. Ahora quieres actualizar feature/payment al mismo nivel que main antes de crear una pull request. Hay dos formas: merge o rebase.
3. Git Merge: preservar el historial, crear un merge commit
3.1. Cómo usarlo
git checkout feature/payment
git merge main
Este comando crea un nuevo merge commit, que tiene dos padres: el último commit de feature/payment y el último commit de main.
* e5f6a7b (HEAD -> feature/payment) Merge branch 'main' into feature/payment
|\
| * c3f9a1d (main) Agrega PaymentValidator
| * 7b2e4aa (main) Corrige bug en OrderService
* | 9d1a0ef Agrega PaymentService.processPayment()
* | 4f3c2bb Inicializa PaymentService
|/
* a1b2c3d Commit base compartido
3.2. Características
- El historial original de ambas ramas se conserva 100%, ningún commit es modificado.
- Se añade un merge commit “innecesario” que registra el momento en que las dos ramas se encuentran.
- Completamente seguro con ramas ya publicadas — nadie se ve afectado negativamente.
Nota:
git mergeno reescribe el SHA de ningún commit existente. Esta es la razón por la que siempre es seguro con ramas compartidas.
4. Git Rebase: reescribir el historial, crear una rama lineal
4.1. Cómo usarlo
git checkout feature/payment
git rebase main
Rebase toma cada commit en feature/payment desde el punto de bifurcación (a1b2c3d), luego los “reproduce” (replay) sobre main. El resultado son commits completamente nuevos con SHA diferente.
* 2a9e1cc (HEAD -> feature/payment) Agrega PaymentService.processPayment()
* d8b4f0a Inicializa PaymentService
* c3f9a1d (main) Agrega PaymentValidator
* 7b2e4aa (main) Corrige bug en OrderService
* a1b2c3d Commit base compartido
El historial ahora es una línea recta (historial lineal), sin merge commits.
4.2. Características
9d1a0efy4f3c2bbya no existen — se reemplazan con2a9e1ccyd8b4f0acon SHA nuevo.- Log limpio, fácil de leer, fácil para
git bisect. - Peligroso si la rama ha sido descargada por otros, porque están guardando commits antiguos que han “desaparecido”.
5. Manejo de conflictos: la mayor diferencia al operar
Con merge, los conflictos (si los hay) solo necesitan resolverse una vez en el merge commit:
git merge main
# CONFLICT (content): Merge conflict in PaymentService.java
# ... edita el archivo, resuelve ...
git add PaymentService.java
git commit
Con rebase, si hay muchos commits, los conflictos pueden ocurrir en cada commit durante el proceso de reproducción:
git rebase main
# CONFLICT en commit 4f3c2bb
# edita el archivo, resuelve:
git add PaymentService.java
git rebase --continue
# si el siguiente commit también tiene conflicto, repite
Si quieres detener y volver al estado anterior a rebase:
git rebase --abort
Nota: Hacer rebase de una rama con 10 commits a veces significa que tienes que resolver conflictos 10 veces — una razón por la que
squashantes de rebase (ver sección 7) es muy útil.
6. Regla de Oro: nunca hagas rebase de una rama compartida
Esta es la regla más importante al usar rebase.
# La rama main ha sido descargada por 5 personas
git checkout main
git rebase feature/old-stuff # ❌ EXTREMADAMENTE PELIGROSO
git push --force origin main
Porque rebase crea nuevo SHA para cada commit, después de push --force, cualquiera que haya descargado el main antiguo tendrá un historial divergente (diverged history) con remote. Su próximo pull fallará o creará una serie de commits duplicados.
error: failed to push some refs to 'origin'
hint: Updates were rejected because the tip of your current branch is behind
Si necesitas obligatoriamente hacer force-push después de rebase, usa --force-with-lease en lugar de --force para evitar sobrescribir commits de otros:
git push --force-with-lease origin feature/payment
--force-with-lease comprobará que remote no haya recibido nuevos commits desde tu último fetch. Si los hay, el comando fallará en lugar de sobrescribir.
Regla de Oro: haz rebase solo en ramas personales, no compartidas o que solo tú estés usando. Nunca hagas rebase en main, develop, o cualquier rama compartida.
7. Rebase interactivo: limpiar el historial antes de merge
Una aplicación muy práctica de rebase es limpiar commits “basura” en tu rama personal antes de crear una pull request.
git checkout feature/payment
git rebase -i HEAD~3
Se abre un editor:
pick 4f3c2bb Inicializa PaymentService
pick 9d1a0ef Agrega PaymentService.processPayment()
pick 1c8f3aa fix typo
Cambia las últimas dos líneas a squash para fusionarlas en el primer commit:
pick 4f3c2bb Inicializa PaymentService
squash 9d1a0ef Agrega PaymentService.processPayment()
squash 1c8f3aa fix typo
Guarda, Git te pedirá que escribas un nuevo mensaje de commit. Resultado: 3 commits fragmentados se convierten en 1 commit limpio y significativo — muy útil antes de abrir un PR para que el revisor no tenga que leer commits como “fix typo”, “wip”, “olvidé import”.
8. Estrategia práctica: combinar ambas
La mayoría de equipos eficientes no eligen absolutamente una de las dos, sino que las combinan según el contexto:
| Situación | Usar |
|---|---|
Actualizar rama personal con la versión más nueva de main |
rebase |
| Limpiar commits fragmentados antes de abrir PR | rebase -i (squash) |
Fusionar rama feature ya revisada en main |
merge (usualmente vía PR, puede ser squash-merge) |
| Rama que múltiples personas están usando/descargando | merge, absolutamente nunca rebase |
| Necesitas mantener registro del momento de integración para auditoría | merge |
El flujo típico en la práctica:
# 1. Antes de abrir PR: rebase sobre main para actualizar, evitar conflictos tardíos
git checkout feature/payment
git rebase main
# 2. Limpiar commits si es necesario
git rebase -i main
# 3. Publicar rama personal (seguro porque solo tú la usas)
git push --force-with-lease origin feature/payment
# 4. Fusionar en main vía PR (merge commit o squash-merge, nunca rebase main)
Este enfoque mantiene el log de feature/payment limpio, lineal, fácil de revisar en el PR, mientras que main permanece seguro porque solo recibe merge commits (o squash commits) de PRs ya aprobados.
9. Squash merge: una opción intermedia
Muchas plataformas (GitHub, GitLab) tienen un botón “Squash and merge” — fusiona todos los commits del PR en un único commit y luego hace merge en main.
git checkout main
git merge --squash feature/payment
git commit -m "Agrega funcionalidad de procesamiento de pagos (#42)"
Resultado: main tiene exactamente un nuevo commit, sin merge commit, el historial de feature/payment (en remote) se conserva si no borras la rama. Esta es la opción más común para equipos que usan PR workflow, porque mantiene el log en main limpio, sin tocar el historial original.
10. Errores comunes
- Hacer rebase y luego push normal (sin
--force): Git reportará error porque el historial ha divergido. No usesgit pullnormal para “reparar” — creará un merge commit no deseado, arruinando el historial lineal que acabas de rebase. - Olvidar que la rama que estás rebaseando es compartida: siempre verifica
git log origin/<branch>para ver si otros ya se basaron en los commits antiguos. - Olvidar
--continuedespués de resolver conflicto durante rebase: el estado de rebase permanece “suspendido”,git statuste lo recordará claramente. - Usar
mergecontinuamente en rama personal llena el log de merge commits pequeñitos, difícil de leer — mejor usar rebase para actualizar en lugar de hacer merge inverso demainen tu rama múltiples veces. - Hacer squash excesivamente: fusionar múltiples cambios no relacionados en un commit hace que
git bisectygit revertparcial se vuelvan inútiles.
11. Conclusión
merge es seguro, preserva el historial fidedigno, es apropiado para ramas compartidas. rebase crea un historial limpio y lineal, pero reescribe SHA así que solo funciona en ramas personales no compartidas. En la práctica, no necesitas elegir un bando absolutamente: usa rebase para actualizar y limpiar tu rama personal, usa merge (o squash-merge) para integrar en main vía PR. Recuerda la Regla de Oro — nunca hagas rebase de una rama en la que otros se están basando — y evitarás la mayoría de problemas de Git en equipo.