Módulo 1 · Fundamentos que sostienen todo

Lección 06 — Git profesional

Ramas, rebase, conflictos y flujos de equipo: la herramienta de colaboración del oficio.

Publicada
En esta lección
  1. Objetivos
  2. 1. El commit es la unidad de intención
  3. 2. Ramas: baratas, desechables, con propósito
  4. 3. Merge vs rebase: la decisión con criterio
  5. 4. Conflictos: mecánica, no drama
  6. 5. Reflog: la red de seguridad que nadie conoce
  7. 6. Bisect: la búsqueda binaria de bugs
  8. 7. Flujo de equipo: GitHub Flow (el que usaremos en el curso)
  9. Autoevaluación (respóndeme en el chat)

Stack: Git en Linux · Proyecto: TicketFlow Estado: Publicada — impartición tras corregir la 05 Prerrequisito: Lección 05 — Linux y terminal


Objetivos

Al terminar esta lección podrás:

  1. Escribir commits que cuentan la intención (y usar git add -p para separarlos bien).
  2. Elegir entre merge y rebase con criterio y resolver conflictos sin pánico.
  3. Recuperar cualquier desastre con reflog (nada se pierde de verdad).
  4. Encontrar el commit que rompió con git bisect.
  5. Trabajar con un flujo de equipo (GitHub Flow / trunk-based) con PRs revisables.

1. El commit es la unidad de intención

Un commit no es "guardar": es contar un paso del proyecto. La regla del mensaje (conventional commits, la convención de facto):

<tipo>: descripción corta en imperativo

cuerpo opcional: el porqué, no el qué (el qué lo muestra el diff)
refs: #123

Tipos que usarás: feat, fix, refactor, test, docs, chore, perf. Ejemplos reales de TicketFlow:

feat(reservations): bloquea asientos con select_for_update al reservar

El check-then-act de la versión anterior permitía una carrera entre
CHECK y ACT. Ahora la fila del asiento se bloquea dentro de la
transacción, y la constraint única queda como red de seguridad.

refs: #42
fix(payments): responde 409 cuando la pasarela reintenta un pago confirmado

El webhook duplicado creaba un segundo Payment para la misma reserva.
Con la idempotency_key como unique, el reintente devuelve el resultado
original en vez de fallar.

Y la herramienta que separa el "todo junto" en commits limpios: git add -p (patch mode) — decide hunk a hunk qué entra. Si un commit mezcla "refactor + fix + feature", ninguno se puede revertir solo: el desastre llega en el revert.

2. Ramas: baratas, desechables, con propósito

bash
git switch -c feat/reserva-con-bloqueo     # crear y cambiar (el checkout moderno)
git branch -m viejo-nombre nuevo-nombre    # renombrar
git branch -d feature-terminada            # borrar fusionada (-D si forceas)

Un cambio = una rama = una PR. Si tu rama vive más de dos días sin PR, se está pudriendo: el merge se complica, el conflicto crece y el review nadie lo hace. Rama corta, PR pequeña, feedback rápido.

3. Merge vs rebase: la decisión con criterio

Ambos integran cambios; difieren en la historia que dejan:

git mergegit rebase
Historiafiel: bifurcación y uniónlineal: "parece que siempre fue así"
Commitsañade uno de mergereescritos con nuevos hashes
Cuándointegrar a la rama principal (historia honesta)actualizar TU rama de trabajo (historia limpia)

La regla de oro de equipo: rebase tus propias ramas sobre main; merge (o squash) al integrar a main. Y la ley que no se rompe jamás: nunca rebase lo que ya está compartido (commits que otros ya tienen) — reescribes historia ajena y el equipo te lo factura.

bash
# actualizar tu rama con lo nuevo de main (rebase correcto)
git switch feat/reserva-con-bloqueo
git fetch origin
git rebase origin/main

# si el rebase se tuerce: abortar es gratis y seguro
git rebase --abort

4. Conflictos: mecánica, no drama

Un conflicto dice "dos ramas tocaron las mismas líneas y Git no puede adivinar". El proceso:

bash
git status                        # lista los ficheros en conflicto
# editar: elegir entre <<<<<<< tuyo / ======= / >>>>>>> suyo
git add fichero_resuelto
git rebase --continue             # o git commit si era un merge

Conflictos evitables: PRs pequeñas, ramas cortas, y git fetch + rebase frecuente (cuanto más fresca tu rama, menos conflicto acumulado). Conflictos estructurales (dos personas cambian la misma función): eso es una señal de comunicación del equipo, no un problema de Git.

5. Reflog: la red de seguridad que nadie conoce

git reflog registra todos los movimientos de HEAD (commits, resets, rebases) durante ~90 días. El commit "borrado" casi siempre vive aquí:

bash
git reflog
# a1b2c3d HEAD@{0}: reset: moving to origin/main
# e4f5g6h HEAD@{1}: commit: feat: bloquea asientos...
git reset --hard HEAD@{1}         # vuelve al commit "perdido"

Antes de cualquier reset --hard destructivo: git branch backup-antes (una rama cuesta nada). El reflog convierte "he perdido mi trabajo" en "he perdido 30 segundos".

6. Bisect: la búsqueda binaria de bugs

"Esto funcionaba la semana pasada." Git lo localiza solo:

bash
git bisect start
git bisect bad                    # el commit actual está roto
git bisect good v1.2.0            # este tag funcionaba
# Git hace checkout del commit intermedio; pruebas:
#   si falla: git bisect bad
#   si funciona: git bisect good
# ...en ~log2(n) pasos señala el commit culpable
git bisect reset

Con 1.000 commits son ~10 pruebas. Y se automatiza: git bisect run make test ejecuta tus pruebas por ti.

7. Flujo de equipo: GitHub Flow (el que usaremos en el curso)

El más común en equipos pequeños/medianos:

main (siempre desplegable)
  └── feat/rama-corta → PR → review → CI verde → merge (squash) → deploy

Reglas que lo hacen funcionar:

  1. main siempre desplegable: si está rota, revertir es la primera herramienta (no "arreglar en caliente").
  2. PR pequeña (< 400 líneas): el review real pasa por ahí; una PR de 3.000 líneas nadie la lee.
  3. CI obligatorio antes de merge: pruebas, lint (Lección 41 lo construye).
  4. Squash al fusionar: la rama puede tener "wip, fix typo, otra vez"; en main queda un commit con intención (sección 1).
  5. Delete branch tras merge: la historia vive en main; las ramas muertas confunden.

Clave: el flujo no es religión; es el acuerdo de cómo la historia del repositorio refleja el trabajo. El equipo decide (GitHub Flow, trunk-based con feature flags, GitFlow en versiones empaquetadas) y lo escribe — exactamente lo que hicimos con los ADRs del curso.


Autoevaluación (respóndeme en el chat)

  1. ¿Por qué un commit que mezcla refactor y fix es un problema operativo (revert, bisect, review)? Sepáralo con git add -p: ¿qué hunk iría a cada commit?
  2. Tu compañero rebasó una rama que ya habías clonado y ahora pull te da historia duplicada. ¿Qué pasó y cómo se evita en equipo?
  3. ¿Cuál es la diferencia real entre git merge main y git rebase main desde tu feature? ¿Cuál elegirías para PRs del proyecto y por qué?
  4. Un bug aparece en producción; el último tag bueno es de hace 300 commits. Describe el flujo exacto de bisect para localizarlo.
  5. ¿Por qué "main siempre desplegable" exige revert como primera respuesta y no hotfix? ¿Qué política de squash/merge hace viable ese revert?

Continúa con los ejercicios. Las soluciones solo tras intentarlo.