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:
- Escribir commits que cuentan la intención (y usar
git add -ppara separarlos bien). - Elegir entre merge y rebase con criterio y resolver conflictos sin pánico.
- Recuperar cualquier desastre con
reflog(nada se pierde de verdad). - Encontrar el commit que rompió con
git bisect. - 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: #123Tipos 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: #42fix(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
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 merge | git rebase | |
|---|---|---|
| Historia | fiel: bifurcación y unión | lineal: "parece que siempre fue así" |
| Commits | añade uno de merge | reescritos con nuevos hashes |
| Cuándo | integrar 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.
# 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 --abort4. Conflictos: mecánica, no drama
Un conflicto dice "dos ramas tocaron las mismas líneas y Git no puede adivinar". El proceso:
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 mergeConflictos 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í:
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:
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 resetCon 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) → deployReglas que lo hacen funcionar:
- main siempre desplegable: si está rota, revertir es la primera herramienta (no "arreglar en caliente").
- PR pequeña (< 400 líneas): el review real pasa por ahí; una PR de 3.000 líneas nadie la lee.
- CI obligatorio antes de merge: pruebas, lint (Lección 41 lo construye).
- Squash al fusionar: la rama puede tener "wip, fix typo, otra vez"; en main queda un commit con intención (sección 1).
- 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)
- ¿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? - Tu compañero rebasó una rama que ya habías clonado y ahora
pullte da historia duplicada. ¿Qué pasó y cómo se evita en equipo? - ¿Cuál es la diferencia real entre
git merge mainygit rebase maindesde tu feature? ¿Cuál elegirías para PRs del proyecto y por qué? - Un bug aparece en producción; el último tag bueno es de hace 300 commits. Describe el flujo exacto de
bisectpara localizarlo. - ¿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.