Herramientas de usuario

Herramientas del sitio


clase:daw:daw-proyecto:tema03

3. Git en el proyecto

Objetivo del tema: que nadie vuelva a escribir a mano un comando de git del día a día. Al acabar, todo lo de git pasa por la skill /git-flow.

Lo que ya sabéis y aquí no se repite:

  • Git básico, .gitignore y el formato de los mensajes de commit type(#issue):título: Despliegue 1.
  • Ramas, los tipos de merge, conflictos y, sobre todo, worktrees y por qué uno por rama cuando se trabaja con agentes: Despliegue 2.
  • Scrum y los tableros de GitHub: Proyecto 4.

Git en Windows

En Linux y Mac, git viene con bash. En Windows, el instalador de Git (Git for Windows) trae también Git Bash, un bash completo con los comandos de Linux (ls, cat, grep…). Hace falta porque el script de este proyecto (app.sh) es de bash, y así los mismos ficheros valen para todos. Dos pantallas del instalador importan:

Elegir «Use Git and optional Unix tools from the Command Prompt»: git y los comandos de Linux quedan disponibles desde cualquier terminal (Git Bash, cmd y PowerShell), que es lo que necesita el agente para ejecutar ./app.sh start o un ls. El aviso de que tapa el find y el sort de Windows no afecta a nada de lo que se hace aquí.

Elegir «Only ever fast-forward». En este módulo nunca se hace git pull (abajo, en las reglas para la IA), pero si alguien lo hace por costumbre, con esta opción el pull falla cuando las historias han divergido en vez de crear un commit de merge sin avisar. Es lo mismo que hace la skill con git merge –ff-only. En Linux y Mac se consigue con git config –global pull.ff only.

GitHub CLI: gh

gh es la línea de comandos de GitHub. Es lo que usa la skill /git-flow para leer el issue (gh issue view N) y lo que se usa en el tema 7 para mirar los workflows, así que tiene que estar instalado y con sesión iniciada en el equipo de cada uno.

  • Windows: winget install GitHub.cli y cerrar y abrir la terminal.
  • Linux (Ubuntu, Debian, Mint): sudo apt install gh.
  • Mac: brew install gh.

Después, iniciar sesión una vez, eligiendo GitHub.com, HTTPS y autenticar en el navegador:

gh auth login
gh auth status      # tiene que decir «Logged in to github.com»

Con eso gh guarda también la credencial de git para HTTPS: el push deja de pedir usuario y contraseña, sin configurar nada más.

El repositorio y el agente

Antes de la primera skill hay que dejar el repositorio del tema 1 preparado para trabajar con el agente: su estructura, sus dos ramas, las instrucciones que lee el agente al empezar y los permisos que tiene.

El repositorio del proyecto

Un único repositorio en GitHub, creado desde cero por cada alumno, con el backend y el frontend dentro:

mi-proyecto/
├── backend/                 <- Spring Boot (Maven)
├── frontend/                <- Angular
├── compose.yaml             <- la aplicación en Docker (tema 4)
├── compose.override.yaml    <- lo que cambia en desarrollo (tema 4)
├── app.sh                   <- start, stop, status y logs de la aplicación (tema 4)
├── .kiro/                   <- la spec de cada issue y el steering (tema 5)
├── docs/                    <- detalle para el agente, si hace falta
├── AGENTS.md                <- instrucciones para el agente
├── CLAUDE.md                <- solo contiene @AGENTS.md
├── .claude/
│   ├── skills/              <- las skills del proyecto
│   └── settings.json        <- permisos del agente, compartidos
└── .github/workflows/       <- CI y despliegue (tema 7)

La estructura interna de backend/ y frontend/ es la de Proyecto 2.

El esqueleto del backend nace sin JPA ni Flyway. Spring Boot con JPA no arranca si no encuentra una base de datos, y la base de datos llega con Docker en el tema 4. Hasta entonces, el backend es el @RestController del tema 1 que responde a /api/hola.

Las dos ramas

El proyecto tiene solo dos ramas oficiales:

  • develop: donde se integra el trabajo. Es la rama por defecto del repositorio en GitHub, y hace falta que lo sea: GitHub solo cierra un issue desde un commit cuando ese commit llega a la rama por defecto (ver abajo).
  • main: lo que hay en producción. Nunca se trabaja en ella; solo recibe lo que ya está en develop (tema 7).

Se crean las dos en el primer commit y se cambia la rama por defecto en GitHub (Settings → General → Default branch) o con gh:

git switch -c develop
git push -u origin develop
gh repo edit --default-branch develop

Instrucciones para el agente: AGENTS.md

AGENTS.md es el fichero que el agente lee al empezar cada conversación (qué es y cómo se escribe: AGENTS.md en OpenCode). Claude Code lee CLAUDE.md, así que CLAUDE.md contiene una sola línea que importa al otro:

@AGENTS.md

Así los dos agentes leen exactamente lo mismo y solo hay un fichero que mantener.

Lo que va dentro, y nada más:

  • Qué es el proyecto, en dos líneas.
  • Cómo se compila, arranca y prueba: los comandos exactos. En el tema 4 se añade dónde corren Maven y Playwright.
  • Reglas del proyecto que el agente tiene que respetar siempre: las ramas, que git se maneja con /git-flow, que .kiro/specs/ es histórico (tema 5).
Corto. AGENTS.md viaja en todas las peticiones, así que cada línea de relleno se paga en cada prompt. Lo que sea largo (una explicación de la arquitectura, un procedimiento) va en docs/ y AGENTS.md dice cuándo leerlo. Es la divulgación progresiva: el agente carga el detalle solo cuando lo necesita.

Skills en este proyecto

Qué es una skill y cómo se escribe está en el tema 2. La primera skill del proyecto, /git-flow, se escribe en este tema; lo que hay que saber además para este proyecto:

  • Dónde van: en .claude/skills/<nombre>/SKILL.md. OpenCode también lee esa carpeta, así que hay una sola copia para los dos agentes.
  • Dos tipos. Las de tarea se invocan escribiendo /nombre y hacen algo (/git-flow, /test). Las de conocimiento no se invocan: el agente las carga solo cuando la tarea encaja con su description (convenciones-backend). Las dos se escriben igual; la diferencia está en la descripción y en que las de tarea tienen un procedimiento.
  • Un subcomando por llamada: todas las skills de tarea del proyecto reciben familia, sustantivo y verbo, como en el tema 1 (/git-flow start 12, /test e2e run). Dentro de SKILL.md los argumentos llegan en $ARGUMENTS y la skill hace una cosa u otra según la primera palabra.
  • allowed-tools: en la cabecera de la skill, la lista blanca de lo que puede ejecutar. Una skill de git que solo puede lanzar git y gh no puede borrar una carpeta por error aunque se confunda:

---
name: estado
description: Resume el estado del proyecto. Usar cuando se pida qué hay pendiente, cómo va el repositorio o al empezar a trabajar.
allowed-tools: Bash(git status:*), Bash(git worktree list:*), Bash(gh issue list:*)
---

allowed-tools lo entiende Claude Code; OpenCode lo ignora y aplica sus propios permisos (abajo).

Permisos del agente: lo básico

El agente pide permiso antes de ejecutar comandos o escribir ficheros. Aceptar uno a uno no es sostenible, y aceptar todo a ciegas tampoco. Lo justo para trabajar:

  • Claude Code: /permissions muestra lo permitido. Lo que se permita para el proyecto se guarda en .claude/settings.json, que va al repositorio, para que valga en todos los worktrees:

{
  "permissions": {
    "allow": [
      "Bash(git status:*)", "Bash(git diff:*)", "Bash(git log:*)",
      "Bash(gh issue list:*)", "Bash(gh issue view:*)",
      "Bash(mvn:*)", "Bash(npm:*)", "Bash(npx:*)",
      "Bash(docker compose:*)"
    ],
    "deny": ["Bash(git push --force:*)", "Bash(rm -rf:*)"]
  }
}

  • OpenCode: en opencode.json en la raíz del proyecto, la clave permission.

En el tema 5 se completa la lista con lo que haga falta para programar.

Issues y tablero

Todo trabajo empieza por un issue. El issue es la unidad: de él sale la rama, su número va en los commits y al fusionar se cierra solo.

El tablero es un GitHub Project del repositorio con tres columnas: Todo, In progress y Done. De sus automatizaciones (Workflows del proyecto) solo hace falta una, que viene activada de serie: issue cerrado → Done. El resto se hace a mano: al crear el issue se añade al tablero, y al empezar a trabajarlo se arrastra a In progress. Automatizar eso por línea de comandos no compensa.

Etiquetas: feature y bug. No hacen falta más.

Los issues se crean desde la web o con gh:

gh issue create --title "Alta de clientes" --label feature --body "Formulario de alta con nombre, NIF y email."
gh issue list
gh issue view 12

Las ramas de este proyecto

El modelo de ramas de Despliegue 2 tiene develop, release y master. Aquí, al ser un proyecto individual con un solo entorno, se recorta a dos:

  • develop: la integración. Solo existe de verdad en local, en la carpeta principal del repositorio; en GitHub está porque es la rama por defecto y es donde se cierran los issues. Nunca se despliega.
  • main: producción. Cada push a main despliega en el servidor (tema 7). Nunca se trabaja en ella.

Y por cada issue, una rama de trabajo en su propio worktree:

Cosa Nombre Ejemplo
Rama <alumno>-<N>-<slug> <alumno>-12-alta-clientes
Worktree ../<repo>-wt-<N>-<slug> ../mi-proyecto-wt-12-alta-clientes

El número del issue en el nombre de la rama hace que la skill sepa siempre a qué issue pertenece, sin preguntarlo.

Sin pull requests. En un proyecto individual nadie va a revisar el PR, así que es un trámite que no aporta nada: la rama se fusiona en develop en local y se hace push. La revisión del código la hará la IA antes de fusionar (tema 6).

El ciclo de una rama

  1. Iniciar: desde la carpeta principal (siempre en develop, actualizada), crear la rama y su worktree.
  2. Trabajar en el worktree. Commits pequeños con type(#N):título.
  3. Actualizar: si develop ha avanzado (otro issue que se ha terminado antes), traer develop a la rama con git merge develop. Los conflictos se resuelven en la rama, nunca en develop.
  4. Finalizar: fusionar en develop con –squash: todos los commits de la rama se funden en uno con el mensaje feat(#N): título. Push de develop. Borrar el worktree y la rama.
  5. Publicar: cuando hay algo que sacar, develop → main con –ff-only y push.

Por qué –squash hacia develop y –ff-only hacia main está en Usos de merge.

El commit que cierra el issue

GitHub cierra un issue automáticamente cuando llega a la rama por defecto un commit cuyo mensaje contiene Closes #N. Por eso develop es la rama por defecto, y por eso el commit de la fusión lleva el Closes en el cuerpo:

feat(#12): Alta de clientes

Closes #12

Al hacer push de develop, el issue 12 se cierra y el tablero lo pasa a Done. No hay que tocar nada en la web.

Reglas para que la IA toque git

Una skill que ejecuta git puede hacer daño si improvisa. Estas reglas van en la skill y no se negocian:

  • Nunca git pull. Siempre git fetch –prune y git merge –ff-only origin/<rama>. Así, si las historias han divergido, falla en vez de hacer un merge sorpresa.
  • Nunca –force, ni –no-verify, ni stash, ni borrar worktrees con –force.
  • Nunca resolver un conflicto. Si un merge da conflicto, la skill se para, enseña git status y dice qué comandos tiene que ejecutar el alumno. Resolver conflictos es trabajo humano.
  • La carpeta principal siempre en develop y limpia. Si no lo está, la skill se para antes de hacer nada.
  • Una operación por invocación: /git-flow start 12, no «start y luego finish».
  • allowed-tools como lista blanca: solo git y gh. Nada de rm, nada de escribir ficheros del proyecto.

La skill /git-flow

Se parte de la skill que ya enlaza Despliegue 2, git-flow-skill.md, que es casi lo que hace falta. Se adapta con estos cambios:

  • Quitar release, bajar y subir y poner las operaciones en inglés, con la gramática del tema 1. Quedan cinco:
Operación Qué hace
start N Lee el issue N con gh issue view, deriva el slug del título, crea la rama <alumno>-<N>-<slug> desde develop actualizada y su worktree hermano, y hace push de la rama
commit Mira git status y git diff del worktree actual y agrupa los cambios por intención, no por fichero: un commit por cosa hecha, cada uno con type(#N):título. El N lo saca del nombre de la rama. Enseña los commits que va a hacer antes de hacerlos
update Trae develop a la rama del worktree actual
finish update, –squash en develop con el mensaje feat(#N): <título del issue> y Closes #N en el cuerpo, push de develop, borrar rama remota, worktree y rama local
publish develop → main con –ff-only y push de main
  • El nombre del alumno para las ramas se lee de AGENTS.md (una línea: «las ramas empiezan por <alumno>-»).
  • Añadir Bash(gh issue view:*) a allowed-tools.
  • finish y update deducen la rama del worktree en el que se ejecutan, como ya hace la skill original.

En el tema 4 start crece para escribir el .env con los puertos del worktree y finish para parar los contenedores, y en el tema 6 finish comprueba antes de fusionar que las tareas, la revisión y los tests están en orden.

AGENTS.md tiene que decir desde ahora: «git se maneja solo con /git-flow; no ejecutes comandos de git que cambien el repositorio fuera de esa skill». Si no, el agente hará git commit por su cuenta a mitad de una tarea.

Ejercicios

Todos los ejercicios se hacen en el repositorio del tema 1. Desde el ejercicio 5, ningún comando de git se escribe a mano.

Ejercicio 1

  • Instala gh, inicia sesión y comprueba con gh auth status y gh repo view sobre tu repositorio que funciona.
  • Escribe AGENTS.md con: qué es el proyecto, cómo se arranca cada parte y cómo se compila.
  • Crea CLAUDE.md con una sola línea: @AGENTS.md.
  • Abre una conversación nueva en Claude Code y otra en OpenCode y pregunta al agente cómo se arranca el backend. Tiene que responder con lo que has escrito.

Ejercicio 2

  • Crea la rama develop desde main y súbela.
  • Ponla como rama por defecto en GitHub con gh repo edit –default-branch develop.
  • Comprueba en la web del repositorio que al entrar se ve develop.

Ejercicio 3

  • Escribe la skill /estado en .claude/skills/estado/SKILL.md: resume git status, git worktree list y los issues abiertos con gh issue list.
  • Pon en allowed-tools solo esos tres comandos.
  • Pruébala en Claude Code y en OpenCode.
  • Crea .claude/settings.json con los permisos básicos de este tema y comprueba que /estado ya no pide permiso.
  • Haz commit de todo y push a develop.

Ejercicio 4

  • Crea el tablero del repositorio (GitHub Project) con las columnas Todo, In progress y Done.
  • Comprueba en los Workflows del proyecto que está activo «Item closed → Done».
  • Crea las etiquetas feature y bug.
  • Crea con gh issue create dos issues: «Página de inicio con el nombre del proyecto» y «README con cómo arrancar». Añádelos al tablero.

Ejercicio 5

  • Descarga git-flow-skill.md y guárdala como .claude/skills/git-flow/SKILL.md.
  • Pide al agente que la adapte a este proyecto: quitar release, bajar y subir; renombrar las operaciones a start, commit, update, finish y publish; que start reciba el número del issue, lo lea con gh issue view y nombre la rama <alumno>-<N>-<slug>; que finish ponga Closes #N en el cuerpo del commit; y que publish lleve develop a main con –ff-only.
  • Pide que añada la operación commit: agrupar los cambios por intención y proponer los commits antes de hacerlos.
  • Añade a AGENTS.md el nombre con el que empiezan tus ramas y la regla «git solo con /git-flow».
  • Lee la skill entera y comprueba que allowed-tools solo tiene git y gh.

Ejercicio 6

  • /git-flow start con el primer issue y, sin cerrarlo, /git-flow start con el segundo. Comprueba con git worktree list que hay dos worktrees hermanos.
  • Pasa los dos issues a In progress en el tablero.
  • En el primer worktree pide al agente la página de inicio; en el segundo, el README.
  • En cada worktree, /git-flow commit. Mira los mensajes: tienen que llevar type(#N):título.
  • /git-flow finish en el primero. Comprueba en GitHub que el issue está cerrado y en Done y que el worktree y la rama han desaparecido.
  • /git-flow update en el segundo y después /git-flow finish.

Ejercicio 7

  • /git-flow publish. Comprueba que main tiene los mismos commits que develop.
  • Con la carpeta principal en develop, modifica un fichero sin hacer commit y lanza /git-flow start con un issue nuevo. La skill tiene que pararse y decirte por qué. Deshaz el cambio.
clase/daw/daw-proyecto/tema03.txt · Última modificación: por claude