Cuando alguien me pregunta si sé Git, la respuesta útil es enseñar qué reglas sigo, cuáles me costó escribir y dónde no las cumplo. Casi todo sale de agente-telegram, un proyecto personal con un solo autor (yo): cómodo para tener disciplina, mal ejemplo de trabajo en equipo.
Una rama por fase
En agente-telegram cada fase del plan vive en su propia rama, con nombres como fase/f12-interfaz-y-redactor. Cuando la fase termina y el arnés de pruebas está entero en verde, la mergeo a main con --no-ff, para que el historial conserve un commit de merge que diga "aquí se cerró F12".
Entre el 08/08/2026 y el 22/09/2026 el repo suma 211 commits en main, 24 de ellos merges. No todos son cierres de fase: algunos son arreglos sueltos y un par, sincronizaciones con origin/main dentro de una rama. Aun así la lista de merges se lee como un índice del proyecto, de F1 a F13. Lo que no es una fase entera (documentación, guías, ajustes de instrucciones) va directo a main, sin ceremonia.
Requisito, ADR, tarea, código
El orden importa más que la rama. El ADR-010 (08/08/2026) fija esta cadena:
Requisito (E1) → Arquitectura (E2) → Decisión (ADR) → Tarea (TAREAS.md) → CódigoSi algo no tiene requisito, primero lo escribo. Si el código y la especificación no coinciden, manda la especificación, y si estaba mal se cambia con fecha y motivo, no en silencio.
En el historial se ve: antes del código de F12 hay un commit con solo la especificación ("F12.1: especificacion aprobada por Jordi, con sus decisiones escritas"), y en F13 igual con /tools.
Casi todos los ADR llevan contexto, decisión, alternativas descartadas y consecuencias a favor y en contra; los más recientes añaden qué haría cambiar la decisión o cómo se comprueba que se cumple. Lo que más valor me da son las alternativas: dentro de dos meses no recordaré por qué no elegí la otra.
Y se enmiendan con fecha en lugar de reescribirse. El ADR-002 elegía LangGraph y PydanticAI, y dejaba escrito el plan B (pydantic a secas). El 02/09/2026 lo activé: PydanticAI no llegó a entrar en el proyecto. No porque se rompiera, sino porque había construido controles de seguridad en mi propio cliente de modelo y PydanticAI se los saltaría. La enmienda está debajo del texto original: se lee qué pensaba el 08/08 y por qué cambié el 02/09.
Lo que aprendí por las malas
29 commits que no existían en GitHub
Entre el 15/08 y el 01/09 hubo 29 commits, con F1 entera dentro, que no estaban en GitHub: el push era un paso aparte y dependía de mí. Lo descubrí al copiar el proyecto a un segundo portátil, que no tenía nada de lo construido desde el 20/08.
No se perdió trabajo: estaba en el .git local. Pero un commit que solo existe en una máquina está a un disco duro de desaparecer. De ahí salieron las normas actuales, escritas en el propio proyecto:
- Cada commit se empuja a
originen el mismo turno en que se hace. - No se reescribe historial ya publicado.
- No se usa
push --force. - No se borra una rama sin que yo lo pida en ese momento.
Autorizar el ciclo normal no autoriza lo irreversible.
Cuatro repos, cuatro maneras de trabajar
No todos mis proyectos funcionan igual:
| Repo | Flujo | Mensajes de commit | Automatización |
|---|---|---|---|
| agente-telegram | Rama por fase, merge --no-ff | Prefijo FN.M: en 94 de 187 commits sin contar merges | Arnés de pruebas en cada tarea |
| multi-agente | Merges a main desde ramas de trabajo | Conventional Commits en 75 de 82 commits sin merge | CI, escaneo de secretos, Dependabot semanal |
| tutor-clinico | Commits pequeños sobre main | Conventional Commits en 59 de 60 commits sin merge | CI con make check y hooks pre-commit |
| pullsight | GitFlow en equipo | Pull requests hasta el #74 | CODEOWNERS |
En multi-agente hay un tag v1.0.0 y Dependabot revisa cada lunes a las 09:00 pip y las GitHub Actions. La entrada de Actions existe porque estaban fijadas a un SHA, lo cual es correcto, pero sin Dependabot quedaban congeladas para siempre. El historial también conserva el merge de cierre de una rama de auditoría de seguridad (hardening/auditoria-seg) con sus correcciones.
En tutor-clinico (el repo se llama mis-especialistas) los hooks pre-commit impiden commitear un .env (salvo .env.example), la carpeta .privado/ o ficheros .pem y .key. Es un error que no quiero poder cometer:
- id: forbid-env-files
stages: [pre-commit, manual]
name: no commitear .env (usa .env.example)
language: fail
entry: ".env contiene secretos: nunca se commitea. La plantilla es .env.example."
files: '(^|/)\.env(\..+)?$'
exclude: '(^|/)\.env\.example$'Su CHANGELOG sigue Keep a Changelog: cada entrada cuenta qué cambia para quien opera el sistema, no cada commit, porque para eso está git log.
Trabajar en equipo: pullsight
En pullsight trabajé con otras personas, con una variante de GitFlow: develop como rama por defecto, ramas feature por tarea (algunas con número de issue, como feature/21-... y feature/35-...) y pull requests hasta el #74. No vi ramas de release. El CODEOWNERS tiene una sola línea que asigna todo el repositorio a una persona.
En pullsight aprendí a coordinar ramas, issues y pull requests con otras personas, algo que en un proyecto propio no te enseña nadie: ahí la disciplina depende de que varias personas se pongan de acuerdo.
Una anécdota de .gitignore
Mi vault de notas tiene un .gitignore con una negación que parece rara:
!08-GUIAS/auditoria-seguridad-guia-completa.mdMi ~/.gitignore_global tiene la regla AUDITORIA-SEGURIDAD-*.md para no versionar los informes de mi skill de auditoría. Pero Windows no distingue mayúsculas, y esa regla se tragaba también una guía del vault que sí quería versionar. La negación la rescata, porque el .gitignore del repo tiene prioridad sobre el global. Lo dejé comentado para que nadie la borre por "limpiar".