🌿 Rebase y Buenas Prácticas en Git

Hasta ahora has visto cómo crear ramas con git branch, unirlas con git merge y solucionar conflictos directos. En proyectos profesionales y equipos de alto rendimiento, la forma en que integras tus cambios y documentas tu trabajo determina la mantenibilidad del código a largo plazo.

En esta lección aprenderás:

  1. Qué es git rebase y cómo funciona internamente su modelo mental.
  2. Cuándo conviene usar merge y cuándo rebase (criterio técnico y tradeoffs).
  3. Cómo resolver conflictos durante un rebase paso a paso.
  4. La Regla de Oro del Rebase (y por qué romperla perjudica a tu equipo).
  5. Buenas prácticas profesionales: commits atómicos, Conventional Commits y gestión rigurosa de .gitignore.

🔄 ¿Qué es Git Rebase?

El comando git merge une dos historias creando un commit de unión (merge commit). Esto conserva la cronología exacta, pero genera ramas cruzadas en el árbol del historial.

git rebase ofrece un enfoque alternativo: replantear la base de tu rama. En lugar de crear un commit de fusión, Git toma los commits que hiciste en tu rama, los “despega” momentáneamente, avanza tu rama hasta la punta de la rama principal (main), y vuelve a aplicar (replay) tus commits uno a uno sobre esa nueva base.

Modelo mental (antes y después)

Imagina esta situación: creaste una rama feature a partir del commit B. Mientras trabajabas, tus compañeros agregaron el commit C a main.

Modelo mental de git rebase: replay de commits para un historial lineal

1. ANTES DEL REBASE (Ramas divergentes)

main feature A B C D E Replay de commits

2. DESPUÉS DE git rebase main (Historial lineal)

main A B C D' E' feature (punta) Nuevos hashes SHA-1 · Historial 100% lineal
Modelo mental de git rebase: los commits de la rama de trabajo se despegan y se vuelven a aplicar secuencialmente sobre el último commit de main, logrando un historial limpio y sin bifurcaciones.

Observa que los commits ahora se llaman D' y E': sus identificadores (hashes SHA-1) cambiaron, porque ahora tienen un commit padre diferente (C en lugar de B).

El resultado es un historial completamente lineal, como si hubieses empezado a programar tu función justo después del commit C.


⚖️ Merge vs Rebase: Criterio Técnico

Ningún comando es intrínsecamente mejor que el otro; resuelven problemas diferentes con prioridades distintas:

Criteriogit mergegit rebase
Forma del historialÁrbol con bifurcaciones y uniones explícitasLínea recta continua y sin bifurcaciones
Contexto históricoConserva con exactitud cuándo se bifurcó y unió la ramaReescribe la historia para simular trabajo secuencial
Resolución de conflictosSe resuelve una sola vez en el merge commitSe resuelve commit por commit durante la reaplicación
Trazabilidad de Pull RequestsExcelente para documentar la integración de un feature completoExcelente para actualizar ramas locales antes de integrar

Regla práctica de la industria:

  • Usa git rebase para mantener tu rama local actualizada con los últimos cambios de main antes de enviar un Pull Request.
  • Usa git merge (o el botón de merge de GitHub) para integrar formalmente una funcionalidad terminada a la rama principal compartida.

🛠️ Cómo ejecutar un Rebase paso a paso

Supongamos que estás trabajando en la rama feature/carrito y quieres sincronizarla con las novedades que llegaron a main.

1. Actualiza tu rama principal local

# 1. Cambiar a la rama main
git switch main

# 2. Descargar las novedades del remoto asegurando fast-forward
git pull --ff-only

2. Vuelve a tu rama de trabajo

# Cambiar a tu rama de funcionalidad activa
git switch feature/carrito

3. Aplica el rebase sobre la punta de main

# Reaplicar los commits de feature/carrito encima de la punta actual de main
git rebase main

Si tus cambios no tocan las mismas líneas que main, Git aplicará cada commit automáticamente y verás:

Successfully rebased and updated refs/heads/feature/carrito.

💥 Manejo de conflictos durante un Rebase

A diferencia de un merge (donde todos los conflictos se resuelven en un solo paso al final), durante un rebase Git reaplica los commits uno a uno. Si el commit D entra en conflicto con los cambios de main, el proceso se detiene en ese commit específico.

Pasos para resolver el conflicto:

1. Inspecciona qué archivos entraron en conflicto

# Ver qué archivos tienen conflictos en este commit específico
git status

Verás los archivos marcados como both modified.

2. Abre el archivo y resuelve los marcadores

Al igual que en un merge tradicional, verás los marcadores de conflicto:

<<<<<<< HEAD (novedades provenientes de main)
const TAX_RATE = 0.21;
=======
const TAX_RATE = 0.18;
>>>>>>> D (tu commit que se está reaplicando)

Edita el archivo, elige la versión correcta y borra los marcadores (<<<<<<<, =======, >>>>>>>).

3. Marca el archivo como resuelto

# Marcar el conflicto de este paso como resuelto
git add src/config.js

4. Continúa el rebase (¡NO hagas commit!)

⚠️ Punto crítico: En un rebase no ejecutas git commit. El commit original ya existe; Git simplemente necesita que le indiques que el conflicto de ese paso fue resuelto para continuar con el siguiente commit:

# Indicar a Git que continúe aplicando los siguientes commits
git rebase --continue

Git continuará aplicando los siguientes commits. Si otro commit presenta conflicto, repite estos pasos hasta que finalice.

¿Algo salió mal o quieres cancelar?

Si te equivocaste o prefieres volver exactamente al estado en que estabas antes de iniciar el rebase, puedes abortar la operación limpiamente:

# Abortar el rebase por completo y restaurar la rama al estado previo
git rebase --abort

🚫 La Regla de Oro del Rebase

NUNCA hagas rebase sobre ramas públicas o compartidas.

Aplica rebase únicamente a tus ramas privadas locales que nadie más ha descargado.

¿Por qué?

Como vimos, el rebase altera los hashes de los commits. Si haces rebase en una rama que tus compañeros de equipo ya clonaron (como main o develop), sus historiales locales quedarán desconectados del remoto. Si intentas forzar la subida con git push --force, reescribirás la historia de todos tus colaboradores, generando errores de sincronización y potenciales pérdidas de código.


🏆 Buenas Prácticas Profesionales con Git

Aprender los comandos no basta para trabajar en equipo. Los estándares de la industria exigen disciplina en el flujo de trabajo:

1. Commits atómicos y enfocados

Un commit atómico realiza un único cambio lógico indivisible.

  • ❌ Mal: Un commit que agrega una pantalla de login, arregla un bug del carrito y refactoriza estilos de 15 archivos ("varios arreglos y login").
  • ✔️ Bien: Tres commits separados con propósito único.

¿Por qué son fundamentales?

  • Hacen que el código sea fácil de revisar en un Pull Request.
  • Permiten revertir una falla puntual con git revert sin deshacer trabajo sano.
  • Facilitan encontrar el origen exacto de un bug usando git bisect.

2. Conventional Commits (El estándar de mensajes)

Los equipos profesionales usan el estándar Conventional Commits para mantener un historial legible por humanos y automatizable por herramientas de CI/CD:

<tipo>(<alcance opcional>): <descripción corta en imperativo>

Tipos comunes:

  • feat: Una nueva característica o funcionalidad.
    • Ejemplo: feat(auth): add google oauth login
  • fix: Corrección de un error o bug.
    • Ejemplo: fix(cart): prevent negative item quantities
  • docs: Cambios exclusivamente en documentación o README.
    • Ejemplo: docs(api): update endpoints table
  • refactor: Cambio en el código que no agrega funcionalidad ni arregla un bug (reestructuración, limpieza).
    • Ejemplo: refactor(db): extract query builder helper
  • test: Añadir o corregir pruebas unitarias o de integración.
    • Ejemplo: test(auth): add unit test for token validation
  • chore: Tareas auxiliares, actualización de dependencias o configuración del proyecto.
    • Ejemplo: chore(deps): bump astro to version 6.0

3. Uso riguroso de .gitignore

El archivo .gitignore le indica a Git qué archivos o carpetas deben ser ignorados y nunca formar parte del repositorio:

# Dependencias (se descargan con el gestor de paquetes)
node_modules/
vendor/

# Archivos de compilación y empaquetado
dist/
build/
*.class

# Secretos y variables de entorno (¡CRÍTICO!)
.env
.env.local
*.pem
*.key

# Archivos temporales del sistema operativo y editores
.DS_Store
Thumbs.db
.vscode/
.idea/

🔒 Regla fundamental de seguridad:

Nunca subas contraseñas, tokens, llaves de API ni archivos .env a Git. Si un secreto llega a un commit, borrarlo en el siguiente commit no lo elimina del historial: cualquier persona con acceso al repositorio puede ver los commits anteriores. Si esto ocurre, debes revocar o rotar el secreto inmediatamente.


4. Inspección constante antes de confirmar

Antes de hacer git add y git commit, adquiere el hábito de revisar qué vas a incluir:

# Revisar de forma resumida el estado de archivos modificados y no rastreados
git status --short

# Comparar las líneas modificadas en el working tree contra el último commit
git diff

No confirmes archivos a ciegas con git add . sin antes comprobar que no estás agregando archivos temporales, logs o cambios no deseados.


📌 Resumen de Comandos de esta Lección

AcciónComando
Replantear la base de la rama actual sobre maingit rebase main
Continuar el rebase tras resolver un conflictogit rebase --continue
Cancelar el rebase y volver al estado previogit rebase --abort
Ver el estado conciso de los archivosgit status --short
Ver diferencias en archivos modificadosgit diff