Los equipos gastan horas debatiendo rebase vs. merge como si fuera cuestión moral. No lo es. Son herramientas con trade-offs distintos. El problema no es elegir una. Es no entender cuándo usar cada una.

Qué hace cada uno

Merge crea un nuevo commit que combina el historial de dos branches. Preserva el historial exactamente como ocurrió, con todos sus commits de feature y el commit de merge.

# merge
* merge branch 'feature' into main
|\
| * add user validation
| * create user model
|/
* main commit

Rebase reaplica los commits de un branch sobre el otro. El historial queda lineal, como si los commits hubieran sido hechos en secuencia.

# rebase
* add user validation
* create user model
* main commit

Cuándo usar merge

Merge es la elección correcta cuando:

  • El branch tiene múltiples autores y el historial de contribución importa
  • Quieres preservar el contexto de cuándo ocurrieron los cambios
  • El branch es long-lived (ej: release branch que vive semanas)
  • Estás trabajando con Git Flow o modelo similar

En GitHub/GitLab, merge vía pull request es el estándar. Crea un merge commit que documenta cuándo entró el código, quién lo revisó y el contexto del cambio.

Cuándo usar rebase

Rebase es la elección correcta cuando:

  • El branch es corto (1-3 commits) y quieres mantener el historial limpio
  • Necesitas actualizar tu branch con cambios de main sin crear merge commits innecesarios
  • El equipo prefiere un historial lineal para navegar con git log --oneline
  • Estás preparando commits para squash merge
# Actualiza branch con cambios de main vía rebase
git fetch origin
git rebase origin/main

# Si hay conflicto, resuélvelo y continúa
git rebase --continue

La regla de oro: no hagas rebase de branches públicos

Rebase reescribe historial. Si otro desarrollador está trabajando en el mismo branch, rebase crea un historial divergente que fuerza force push y causa confusión.

Haz rebase solo de branches que sean solo tuyos. Si el branch es compartido, usa merge.

Squash merge: lo mejor de ambos mundos

Muchos equipos adoptaron squash merge como estándar: los commits de la feature se comprimen en un único commit antes del merge a main.

# En GitHub, configúralo en repo Settings > General > Pull Requests
# ☑ Allow squash merging

# O vía CLI:
git checkout main
git merge --squash feature-branch
git commit -m "feat(auth): add JWT refresh token rotation"

Ventajas: el historial de main queda limpio con un commit por feature. Desventajas: el historial detallado de la feature (decisiones, discusiones, intentos) se pierde.

El flujo que funciona en la práctica

# 1. Crea branch a partir de main
git checkout main && git pull
git checkout -b feature/user-validation

# 2. Trabaja, haz commits localmente
git add . && git commit -m "feat: add user model"
git commit -m "feat: add validation logic"

# 3. Antes de abrir PR, rebase sobre main
git fetch origin
git rebase origin/main

# 4. Resuelve conflictos si hay
git rebase --continue

# 5. Abre PR: squash merge en GitHub

Esto da lo mejor de ambos mundos: rebase mantiene tu branch actualizado sin merge commits, y squash merge mantiene el historial de main limpio.

Lo que no hacer

No hagas force push en branches compartidos. No hagas rebase de commits que ya fueron pusheados y que otras personas están usando. No hagas rebase interactivo en branches públicos. Y no discutas esto en Slack como si fuera religión. Elige la herramienta que funciona para el contexto de tu equipo.