Flujo de trabajo con Git: ramas, merges y pull requests paso a paso

D
DanisCh
• 17 min de lectura
Flujo de trabajo con Git: ramas, merges y pull requests paso a paso
Git y GitHub Python

Sabes hacer commits. Sabes hacer push. Pero cuando abres el repositorio de un proyecto real en GitHub y ves decenas de ramas, pull requests abiertos y merges en progreso, todo parece más complicado de lo que esperabas.

No lo es. El flujo de trabajo con Git tiene una lógica muy clara: nunca trabajas directamente en la rama principal. Creas una rama para tu funcionalidad, desarrollas en ella, la compartes con el equipo para revisión y, una vez aprobada, la integras. Eso es todo en esencia.

En este artículo vas a entender ese flujo de principio a fin: qué son las ramas, cómo crearlas, cómo hacer merges, cómo resolver conflictos y cómo abrir y gestionar pull requests en GitHub. Con los comandos exactos y diagramas visuales en cada paso.

Si aún no tienes claros los fundamentos de Git — qué es un commit, cómo funciona el staging area — te recomendamos leer primero qué es Git y por qué todo programador debe aprenderlo antes de continuar aquí.


¿Qué es una rama (branch) y para qué sirve?

Una rama en Git es una línea independiente de desarrollo. Piénsala como una copia del proyecto en la que puedes trabajar sin afectar al código principal. Los cambios que haces en tu rama no aparecen en la rama principal hasta que tú decides integrarlos.

Sin ramas — todos trabajando en main (peligroso):

main: A → B → C → D → E → F
                   ↑
            Si E rompe algo, todos se ven afectados
            No hay forma de revisar antes de integrar

Con ramas — cada desarrollador en su propio espacio:

main:         A → B → C ────────────────── M (merge)
                         \               /
feature/login:            D → E → F → G
                         (trabajo aislado, sin afectar a main)

La rama principal (tradicionalmente llamada master, ahora main en la mayoría de proyectos modernos) siempre debe contener código estable y funcional. Todo el trabajo nuevo — funcionalidades, correcciones, experimentos — se hace en ramas separadas.


Los comandos esenciales para trabajar con ramas

Ver ramas

# Ver ramas locales (la activa aparece con * y en verde)
git branch
# * main
#   feature/login
#   bugfix/navbar

# Ver ramas locales Y remotas
git branch -a
# * main
#   feature/login
#   remotes/origin/main
#   remotes/origin/feature/login

# Ver la última confirmación de cada rama
git branch -v
# * main          a3f2c1d Actualizar README
#   feature/login 9b4e2a1 Añadir formulario de login

Crear una rama

# Forma 1: crear la rama (sin cambiar a ella)
git branch feature/login

# Forma 2: crear Y cambiar a ella en un solo comando (la más usada)
git checkout -b feature/login

# Forma moderna (Git 2.23+) — equivalente pero más claro
git switch -c feature/login

# Crear rama a partir de otra rama (no desde la actual)
git checkout -b feature/nueva-func origin/develop

Cambiar entre ramas

# Cambiar a una rama existente
git checkout main
git checkout feature/login

# Forma moderna:
git switch main
git switch feature/login

# Volver a la rama anterior (el equivalente a "cd -" en la terminal)
git checkout -
git switch -

Convenciones de nombres de ramas

# Las convenciones más usadas en equipos profesionales:

feature/nombre-de-la-funcionalidad    # nueva funcionalidad
feature/login-con-google
feature/carrito-de-compras

bugfix/descripcion-del-bug            # corrección de bug
bugfix/navbar-no-carga-en-mobile
bugfix/error-500-en-checkout

hotfix/descripcion-urgente            # corrección urgente en producción
hotfix/vulnerabilidad-sql-injection

release/1.2.0                         # preparación de versión
chore/actualizar-dependencias         # mantenimiento (sin nueva func)
docs/mejorar-readme                   # documentación
test/añadir-tests-auth                # solo tests

El flujo de trabajo completo — paso a paso

Paso 1: Actualiza main antes de crear la rama

# Antes de empezar cualquier tarea:
# 1. Ve a main
git checkout main

# 2. Trae los últimos cambios del remoto
git pull origin main

# Ahora tu main local está sincronizado con el remoto
# Tu rama nueva partirá del estado más actualizado

Paso 2: Crea tu rama de trabajo

# Crear y cambiar a la nueva rama
git checkout -b feature/sistema-login

# Verificar que estás en la rama correcta
git branch
# * feature/sistema-login
#   main

Paso 3: Trabaja en tu rama — commits frecuentes

# Hacer cambios en tu código...
# (editas archivos, creas nuevos, etc.)

# Ver qué archivos han cambiado
git status

# Añadir al staging los cambios
git add src/auth/login.py
git add src/auth/login.html
git add tests/test_login.py

# O añadir todos los cambios:
git add .

# Crear el commit con un mensaje descriptivo
git commit -m "feat: añadir formulario de login con validación"

# Continúas trabajando...
git add src/auth/session.py
git commit -m "feat: implementar manejo de sesiones"

git add src/auth/logout.py
git commit -m "feat: añadir función de logout y limpieza de sesión"

💡 Commits frecuentes y descriptivos: es mejor hacer muchos commits pequeños con mensajes claros que un único commit enorme con "cambios varios". Si algo sale mal, es mucho más fácil revertir un commit pequeño.

Paso 4: Sube tu rama al repositorio remoto

# Subir tu rama local a GitHub/GitLab por primera vez git push -u origin feature/sistema-login # -u establece el "upstream" — de ahora en adelante puedes usar solo git push # Pushes posteriores en la misma rama: git push # Ver el estado del push git status # On branch feature/sistema-login # Your branch is up to date with 'origin/feature/sistema-login'

Paso 5: Mantén tu rama actualizada con main

Mientras trabajas en tu rama, otros compañeros pueden haber mergeado cambios a main. Es buena práctica mantener tu rama actualizada para evitar conflictos grandes al final.

# Opción 1: merge de main en tu rama (más sencillo)
git checkout feature/sistema-login
git fetch origin
git merge origin/main

# Opción 2: rebase de tu rama sobre main (historial más limpio)
git checkout feature/sistema-login
git fetch origin
git rebase origin/main

# ¿merge o rebase?
# merge → crea un commit de merge, preserva el historial exacto
# rebase → reaplica tus commits sobre main, historial más lineal y limpio
# En equipos pequeños: rebase es más popular para ramas de feature
# En proyectos grandes con muchos colaboradores: merge es más seguro

git merge — integrar ramas

Un merge (fusión) integra los cambios de una rama en otra. Hay varios tipos según la situación.

Fast-forward merge — el más simple

# Ocurre cuando main no ha cambiado desde que creaste tu rama
# Git simplemente "avanza" el puntero de main hasta el último commit de tu rama

main:         A → B → C
                       \
feature:                D → E → F

# Después del merge fast-forward:
main:         A → B → C → D → E → F
                             (sin commit de merge extra)

# Comandos:
git checkout main
git merge feature/sistema-login
# Fast-forward (si es posible)

Merge con commit de merge (3-way merge)

# Ocurre cuando main también ha avanzado mientras trabajabas
# Git crea un commit especial "de merge" que une los dos historiales

main:         A → B → C → G → H
                       \         \
feature:                D → E → F → M (commit de merge)

# Después del merge:
main:         A → B → C → G → H → M
                       \           /
feature:                D → E → F

# Comandos:
git checkout main
git pull origin main           # actualizar main primero
git merge feature/sistema-login
# Se crea un commit de merge automáticamente

# Con --no-ff: fuerza el commit de merge incluso si sería fast-forward
git merge --no-ff feature/sistema-login

¿Cuándo usar --no-ff?

# Sin --no-ff (fast-forward):
# main: A → B → C → D → E → F
# Los commits de la feature se mezclan con los de main
# Más difícil ver qué cambios pertenecen a qué funcionalidad

# Con --no-ff:
# main: A → B → C ──────────── M
#                 \           /
# feature:         D → E → F
# Queda claro que D, E, F son una funcionalidad completa
# Fácil de revertir: git revert M deshace toda la feature

# Muchos equipos usan --no-ff como estándar para features

Resolver conflictos de merge

Un conflicto ocurre cuando dos ramas han modificado las mismas líneas del mismo archivo de forma diferente. Git no puede decidir automáticamente cuál versión es la correcta — te pide que decidas tú.

# Situación que genera conflicto: main modificó archivo.py:  def saludar():      return "Hola, mundo"   ← versión de main feature modificó archivo.py:  def saludar():      return "¡Hola!"        ← versión de feature # Al hacer merge → Git no sabe cuál de las dos usar → CONFLICTO

# Cuando hay un conflicto, git merge muestra:
# CONFLICT (content): Merge conflict in src/auth/login.py
# Automatic merge failed; fix conflicts and then commit the result.

# Ver qué archivos tienen conflictos:
git status
# both modified: src/auth/login.py  ← archivo en conflicto

El aspecto de un archivo con conflicto

# Al abrir el archivo conflictivo verás marcadores como estos:

def autenticar_usuario(email, password):
<<<<<<< HEAD
    # Versión de main
    usuario = db.buscar_por_email(email)
    if usuario and check_password(password, usuario.hash):
        return usuario
=======
    # Versión de feature/sistema-login
    usuario = Usuario.query.filter_by(email=email).first()
    if usuario and usuario.verificar_password(password):
        return crear_sesion(usuario)
>>>>>>> feature/sistema-login

# <<<<<<< HEAD → lo que hay en la rama actual (main)
# ======= → separador entre las dos versiones
# >>>>>>> feature/... → lo que viene de la rama que estás mergeando

Resolver el conflicto

# Tienes tres opciones:

# Opción 1: quedarte con la versión de main (HEAD)
def autenticar_usuario(email, password):
    usuario = db.buscar_por_email(email)
    if usuario and check_password(password, usuario.hash):
        return usuario

# Opción 2: quedarte con la versión de feature
def autenticar_usuario(email, password):
    usuario = Usuario.query.filter_by(email=email).first()
    if usuario and usuario.verificar_password(password):
        return crear_sesion(usuario)

# Opción 3: combinar lo mejor de ambas (la más común)
def autenticar_usuario(email, password):
    usuario = Usuario.query.filter_by(email=email).first()
    if usuario and check_password(password, usuario.hash):
        return crear_sesion(usuario)

# Elimina TODOS los marcadores (<<<<<<<, =======, >>>>>>>)
# El archivo debe quedar como código Python válido
# Después de resolver el conflicto manualmente:

# 1. Añadir el archivo resuelto al staging
git add src/auth/login.py

# 2. Completar el merge con un commit
git commit
# Git abrirá el editor con un mensaje de merge pre-generado
# Puedes aceptarlo o modificarlo

# Para abortar el merge y volver al estado anterior:
git merge --abort

Herramientas para resolver conflictos

# VS Code: resalta los conflictos con botones visuales
# "Accept Current Change" → elige HEAD
# "Accept Incoming Change" → elige feature
# "Accept Both Changes" → combina ambas versiones

# Desde la terminal con la herramienta de merge configurada:
git mergetool

# Configurar VS Code como herramienta de merge:
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'

Pull Requests en GitHub — la revisión de código

Un Pull Request (PR) — o Merge Request en GitLab — es una solicitud formal para integrar los cambios de tu rama en otra. Es el corazón del trabajo en equipo con Git: antes de que tu código entre en main, el equipo lo revisa, comenta y aprueba.

Crear un Pull Request en GitHub

  1. Sube tu rama a GitHub: git push -u origin feature/sistema-login
  2. Ve a GitHub → tu repositorio
  3. GitHub mostrará un banner amarillo: "feature/sistema-login had recent pushes → Compare & pull request" → haz clic
  4. O ve manualmente a Pull requests → New pull request
  5. Selecciona la base (normalmente main) y tu rama como "compare"
  6. Rellena el formulario del PR:
# Título del PR — claro y descriptivo
✅ feat: implementar sistema de login con Google OAuth
✅ fix: corregir error 500 en el checkout al pagar con tarjeta
❌ cambios
❌ arreglé cosas

# Descripción del PR — explica qué, por qué y cómo
## ¿Qué hace este PR?
Implementa el sistema de autenticación con Google OAuth 2.0.

## ¿Por qué?
Muchos usuarios piden poder iniciar sesión con su cuenta de Google
en lugar de crear una contraseña nueva.

## ¿Cómo lo hace?
- Integra la librería google-auth-oauthlib
- Añade el endpoint /auth/google y /auth/google/callback
- Crea el modelo de usuario con soporte para OAuth

## Testing
- [x] Tests unitarios: pytest tests/test_auth.py (12 tests, todos verdes)
- [x] Probado manualmente en Firefox, Chrome y Safari

## Screenshots (si aplica)
[captura del formulario de login con el botón de Google]

Revisar un Pull Request

# Como revisor, en GitHub puedes:
# 1. Ver los cambios → pestaña "Files changed"
# 2. Comentar líneas específicas → haz clic en el + junto a la línea
# 3. Sugerir cambios directamente (el autor puede aplicarlos con un clic)
# 4. Aprobar el PR → "Review changes" → "Approve"
# 5. Solicitar cambios → "Review changes" → "Request changes"

# Hacer revisión local del PR para probar el código:
git fetch origin
git checkout feature/sistema-login
# Ejecutar tests, probar la funcionalidad, etc.

Actualizar un PR después de comentarios

# El revisor pidió cambios → los haces en tu rama local
git checkout feature/sistema-login

# Haces los cambios solicitados...
git add .
git commit -m "fix: manejar caso de email no verificado en OAuth"

# Subes los cambios
git push
# El PR se actualiza automáticamente en GitHub ✅

Hacer el merge del PR en GitHub

Una vez que el PR tiene las aprobaciones necesarias, puedes integrarlo. GitHub ofrece tres opciones:

# Opción 1: "Create a merge commit" (--no-ff)
# → Crea un commit de merge explícito
# → Preserva todo el historial de la rama
# → Recomendado para la mayoría de casos

# Opción 2: "Squash and merge"
# → Combina todos los commits de la rama en UNO solo
# → Historial de main más limpio
# → Pierdes el detalle de los commits individuales
# → Bueno cuando la rama tiene commits muy pequeños o "WIP"

# Opción 3: "Rebase and merge"
# → Reaplica los commits de la rama uno a uno sobre main
# → Historial lineal sin commits de merge
# → Requiere que los commits sean limpios y descriptivos

Estrategias de branching — Git Flow vs GitHub Flow

GitHub Flow — simple y efectivo para la mayoría

# La estrategia más simple. Solo dos tipos de ramas:
# main → siempre desplegable, siempre estable
# feature/xxx → todo lo que no es main

# Flujo completo:
git checkout main && git pull
git checkout -b feature/nueva-funcionalidad
# ... trabaja, haz commits ...
git push -u origin feature/nueva-funcionalidad
# Abre PR en GitHub
# Revisión y aprobación
# Merge a main → despliegue automático

# Ideal para:
# ✅ Proyectos con despliegue continuo (CD)
# ✅ Equipos pequeños y medianos
# ✅ SaaS con una sola versión en producción

Git Flow — para proyectos con versiones

# Ramas permanentes:
main    → solo código en producción (etiquetado con versiones)
develop → rama de integración de features

# Ramas temporales:
feature/xxx  → nuevas funcionalidades (desde develop, merge a develop)
release/x.y.z → preparación de versión (desde develop, merge a main y develop)
hotfix/xxx   → correcciones urgentes en producción (desde main, merge a main y develop)

# Flujo de una feature:
git checkout develop
git checkout -b feature/carrito-compras
# ... trabaja ...
git checkout develop
git merge --no-ff feature/carrito-compras
git branch -d feature/carrito-compras

# Flujo de un hotfix urgente:
git checkout main
git checkout -b hotfix/error-pago-critico
# ... corrige el bug ...
git checkout main
git merge --no-ff hotfix/error-pago-critico
git tag -a v1.2.1
git checkout develop
git merge --no-ff hotfix/error-pago-critico
git branch -d hotfix/error-pago-critico

# Ideal para:
# ✅ Apps móviles con versiones (1.0, 1.1, 2.0...)
# ✅ Proyectos con múltiples versiones en producción simultáneas
# ✅ Ciclos de release planificados

Comandos de limpieza — gestionar ramas usadas

# Eliminar rama local (después del merge)
git branch -d feature/sistema-login    # seguro: falla si no está mergeada
git branch -D feature/sistema-login    # forzado: elimina aunque no esté mergeada

# Eliminar rama remota
git push origin --delete feature/sistema-login

# GitHub también puede eliminar la rama automáticamente después del merge
# (configuración del repositorio: "Automatically delete head branches")

# Ver ramas ya mergeadas (candidatas a eliminar)
git branch --merged main
# * main
#   feature/sistema-login     ← ya mergeada, se puede borrar
#   feature/carrito           ← ya mergeada, se puede borrar

# Limpiar referencias a ramas remotas eliminadas
git fetch --prune
# o equivalente:
git remote prune origin

Comandos del día a día — resumen rápido

# ── Inicio de tarea ──────────────────────────────────────────
git checkout main
git pull origin main
git checkout -b feature/mi-tarea

# ── Durante el desarrollo ─────────────────────────────────────
git status                    # ver qué cambió
git add .                     # añadir al staging
git commit -m "descripción"   # guardar punto de control
git push                      # subir al remoto

# ── Mantener la rama actualizada ─────────────────────────────
git fetch origin
git rebase origin/main        # o: git merge origin/main

# ── Fin de tarea — crear PR ───────────────────────────────────
git push -u origin feature/mi-tarea
# → Abre el PR en GitHub

# ── Después del merge del PR ──────────────────────────────────
git checkout main
git pull origin main
git branch -d feature/mi-tarea           # eliminar rama local
git push origin --delete feature/mi-tarea # eliminar rama remota

Errores comunes y cómo evitarlos

❌ Trabajar directamente en main

# ❌ Nunca hagas commits directamente en main
git checkout main
git add .
git commit -m "nueva funcionalidad"  # ← peligroso

# ✅ Siempre crea una rama
git checkout -b feature/nueva-funcionalidad
git add .
git commit -m "nueva funcionalidad"

❌ No actualizar main antes de crear la rama

# ❌ Si main local está desactualizado, tu rama parte de código viejo
git checkout -b feature/login     # ← parte de main desactualizado

# ✅ Siempre actualiza primero
git checkout main
git pull origin main              # ← actualizar
git checkout -b feature/login     # ← ahora parte del código más reciente

❌ Commits gigantes sin mensajes descriptivos

# ❌ Un solo commit con todo
git add .
git commit -m "cosas"   # ← inútil para el equipo

# ✅ Commits pequeños y descriptivos
git commit -m "feat: añadir componente LoginForm"
git commit -m "feat: implementar validación de email"
git commit -m "test: añadir tests para LoginForm"

❌ Ignorar los conflictos de merge

# ❌ Hacer commit con los marcadores de conflicto sin resolver
git add .
git commit -m "merge"
# El código tiene <<<<<<< HEAD en medio — no compilará

# ✅ Resolver todos los conflictos antes del commit
# Busca en todos los archivos: <<<<<<, =======, >>>>>>>
grep -r "<<<<<<" .   # si no aparece nada → todos resueltos

Resumen: lo que aprendiste hoy

  • ✅ Una rama es una línea independiente de desarrollo — los cambios no afectan a main hasta el merge
  • ✅ git checkout -b nombre-rama crea y cambia a la nueva rama en un solo comando
  • ✅ Siempre actualiza main con git pull antes de crear una nueva rama
  • ✅ Convención de nombres: feature/, bugfix/, hotfix/, release/
  • ✅ Fast-forward merge: main no cambió mientras trabajabas — Git avanza el puntero directamente
  • ✅ 3-way merge: main también cambió — Git crea un commit de merge que une los dos historiales
  • ✅ Los conflictos ocurren cuando dos ramas modificaron las mismas líneas — hay que resolverlos manualmente eliminando los marcadores
  • ✅ Un Pull Request es una solicitud de revisión antes del merge — el equipo revisa, comenta y aprueba
  • ✅ GitHub Flow (solo main + features) para proyectos con despliegue continuo
  • ✅ Git Flow (main + develop + features/releases/hotfixes) para proyectos con versiones
  • ✅ Después del merge: elimina la rama local con git branch -d y la remota con git push origin --delete

🧪 ¿Tienes los fundamentos de Git bien sólidos?

El flujo de trabajo con ramas es Git intermedio. Para dominarlo necesitas tener claros los conceptos básicos: commits, push, pull y cómo funciona el repositorio remoto. Comprueba dónde estás:

👉 Test: Git y GitHub Básico 👉 Test: APIs REST Diseño y Consumo 

¿Ya trabajabas con ramas en tus proyectos o era la primera vez que veías el flujo completo? ¿Tu equipo usa GitHub Flow, Git Flow o una estrategia propia? ¿Has tenido algún conflicto de merge especialmente complicado? Cuéntanos en los comentarios 👇 — respondemos todos. 🚀

¿Te ha gustado esta entrada?

Compártela con tus compañeros para que también sigan aprendiendo.

Comunidad y Comentarios

0 COMENTARIOS

No hay comentarios todavía. Sé el primero en compartir tu opinión.

Escribe tu opinión
Respondiendo a