Git con IA: worktrees, puntos de control y revisar diffs
Cómo usar Git con IA cuando el agente escribe el código: worktrees para paralelismo, commits como red de seguridad y revisar diffs sin fiarte.
El agente estuvo cuarenta minutos haciendo un trabajo decente. Refactorizó un servicio, añadió tests, tocó tres archivos con criterio. Y entonces, en la última tanda, decidió “simplificar” el router y se llevó por delante media hora de cambios buenos que aún no estaban en ningún commit. Sin un punto de guardado por el medio, no hay vuelta atrás limpia: o rescatas los buenos a mano del working tree destrozado, o lo pierdes todo.
Ese es el problema nuevo. No es que el agente escriba mal. Es que escribe rápido, y cuando se equivoca lo hace con la misma velocidad con la que acertaba diez segundos antes. Git deja de ser burocracia de fin de jornada y pasa a ser lo único que te separa de perder trabajo bueno. Si ya usas Claude Code, Cursor o Codex a diario, tu flujo de Git tiene que cambiar. Aquí explico cómo lo estructuro cuando quien teclea es el modelo y yo soy el que decide qué se queda.
¿Por qué Git es tu red de seguridad cuando la IA escribe el código?
Porque el coste de equivocarse deja de ser simétrico. Cuando escribes tú, generas cambios al ritmo al que los entiendes, y un error es local: sabes qué acabas de tocar. Cuando el agente escribe, puede tocar quince archivos en una sola respuesta, y el error que introduce en el archivo doce queda enterrado bajo los aciertos de los otros catorce. La asimetría es esta: descartar trabajo malo es gratis si tienes un commit al que volver, pero recuperar trabajo bueno que se perdió sin commitear cuesta horas o es directamente imposible.
La disciplina que lo resuelve es aburrida y funciona: commit antes de soltar al agente, commit después de cada tanda que deja el árbol en un estado que compila y pasa los tests. Antes, para tener un ancla conocida. Después, para congelar lo que acaba de funcionar. Si la siguiente tanda la rompe, git reset --hard HEAD te devuelve al último buen estado sin ceremonia.
Y cuando la caques de verdad, cuando hagas el reset a la referencia equivocada, está el reflog. El reflog registra cada movimiento de HEAD, incluidos los que un reset --hard parece haber borrado. Ojo con la ventana de retención, porque no es la que la gente asume: el commit que un reset --hard dejó huérfano (sin ninguna rama apuntándolo) se rige por gc.reflogExpireUnreachable, cuyo default son treinta días, no noventa. Los noventa días de gc.reflogExpire son solo para los movimientos de HEAD que siguen siendo alcanzables. En la práctica tienes un mes de margen para rescatar lo que un reset se llevó por delante, hasta que pase el git gc. Esa es la red debajo de la red.
# Deshacer un reset --hard equivocado: el commit "perdido" sigue en el reflog
git reflog # localiza la posición previa, p.ej. HEAD@{2}
git reset --hard HEAD@{2} # HEAD vuelve a esa entrada del reflog
HEAD@{2} significa “dónde estaba HEAD hace dos movimientos”. Mientras el commit tenga una entrada en el reflog, es recuperable. Interioriza esto y sueltas el miedo a experimentar con lo que el agente propone, porque ninguna cagada del modelo es definitiva mientras tú hagas commits.
¿Qué es un punto de control y por qué cada tanda del agente merece uno?
Un punto de control es un commit cuyo único propósito es marcar “hasta aquí funcionaba”. No tiene que ser bonito, ni tener un mensaje que pasaría una revisión, ni representar una feature completa. Representa un estado del árbol al que quieres poder volver. Cuando el agente trabaja en tandas, cada tanda que deja el código verde merece uno.
En la práctica esto son commits WIP desechables. Un git add -A && git commit -m con un mensaje sincero de lo que estabas haciendo, y a seguir:
git add -A && git commit -m "wip: punto de control antes de que el agente toque el router"
Fíjate que es git add -A, no git commit -am. El -a solo captura modificaciones y borrados de archivos ya trackeados; los archivos nuevos sin trackear no los toca. Y el agente crea archivos nuevos todo el rato (tests, módulos), que es justo el trabajo que este punto de control tiene que proteger. Con -am, esos archivos quedan fuera del commit, y el reset --hard de dentro de diez minutos se los lleva por delante.
Ese commit vale exactamente hasta que decides que la línea de trabajo es buena. Nadie más lo va a ver. Su valor está en existir: es el ancla del reset --hard de dentro de diez minutos.
Lo importante es no confundir el historial de trabajo con el historial que compartes. Mientras iteras con el agente, generas quince commits WIP llenos de “esto no”, “vuelta atrás”, “ahora sí”. Antes de que eso salga en un PR, lo colapsas. git commit --amend para el último, o git rebase -i para aplastar toda la serie en los pocos commits atómicos que de verdad cuentan la historia. El agente iteró quince veces; tu rama compartida cuenta dos cambios conceptuales, cada uno funcionando por separado.
Esta separación entre “commits de trabajo” y “commits publicables” no es nueva. Lo que cambia con la IA es la frecuencia. Antes squasheabas una tarde de trabajo tuyo. Ahora squasheas una ráfaga de veinte respuestas del modelo, y sin puntos de control por el medio no tienes de dónde tirar cuando la respuesta doce era mejor que la veinte.
¿Cómo corren varios agentes en paralelo sin pisarse?
Con git worktrees. Un worktree es un working tree adicional, con su propio HEAD, su propio index y sus propios archivos en disco, pero enganchado al mismo .git del repositorio original. Todos los worktrees comparten la misma base de datos de objetos. No clonas nada: creas una segunda carpeta donde una rama distinta está checked out, y ahí lanzas al segundo agente.
# Un working tree nuevo en ../repo-agente-b con la rama feature/pagos checked out
git worktree add ../repo-agente-b feature/pagos
El problema que resuelve es concreto. Si lanzas dos agentes en la misma carpeta, se pisan los archivos: uno guarda mientras el otro lee, el index se convierte en un campo de minas y acabas con cambios entremezclados que ninguno de los dos entiende. Con un worktree por agente, cada uno tiene su carpeta y su rama, aislados en disco, pero sin duplicar el historial ni tener que sincronizar dos repos. Git ni siquiera te deja hacer checkout de la misma rama en dos worktrees a la vez sin -f, precisamente para que no te dispares en el pie.
Cuándo usar cada cosa depende de qué estés aislando:
| Forma de aislar | Coste en disco | Aísla el working tree | Comparte historia/objetos | Ideal para |
|---|---|---|---|---|
Rama (checkout) | Cero | No: un solo working tree | Sí | Un agente cada vez, cambiando de contexto en serie |
Worktree (git worktree add) | Bajo: solo los archivos del árbol | Sí | Sí, mismo .git | Varios agentes en paralelo sobre el mismo repo |
| Clon completo | Alto: copia entera del .git | Sí | No: repos independientes | Aislamiento total, experimentos destructivos, otra máquina |
La rama sola no aísla el working tree: solo tienes una copia de los archivos, así que dos agentes se estorban. El clon completo aísla del todo pero duplica el .git entero y te obliga a sincronizar cambios entre repos como si fueran remotos distintos. El worktree es el punto medio que casi siempre quieres para paralelismo: aislamiento de archivos con historia compartida.
Limpiar es tan importante como crear. Cuando el agente termina y mergeas su rama, no dejes la carpeta muerta ahí:
git worktree list # ver todos los working trees y en qué commit está cada uno
git worktree remove ../repo-agente-b # eliminar uno de forma ordenada
git worktree prune # limpiar referencias a worktrees cuya carpeta ya borraste a mano
git worktree remove es la forma limpia. git worktree prune es para cuando borraste la carpeta con rm -rf como un animal y Git todavía cree que existe: elimina los metadatos huérfanos. El how-to concreto de montar esto con Claude Code, con nombres de rama y estructura de carpetas, lo tienes en la guía de worktrees con Claude Code, y una profundización paso a paso en cómo configurar worktrees para agentes.
¿Cómo se revisa un diff generado por IA sin fiarte?
Leyéndolo. Entero. Esta es la habilidad que más te cunde del oficio cuando el modelo escribe el código: no confías en el diff, lo lees. Y la lees con la sospecha calibrada, porque el agente no miente pero tampoco tiene tu criterio sobre qué está fuera de alcance.
Los tests pasando no te salvan de esto. Un test verde te dice que lo que cubre sigue funcionando. No te dice que el agente no borró la función que nadie testeaba, no metió una dependencia nueva en el package.json para resolver algo que ya tenías resuelto, no dejó una API key hardcodeada en un archivo de config “temporal”, ni tocó cinco archivos cuando la tarea era de uno. Eso solo lo ve un par de ojos sobre el diff.
Hay cuatro cosas que busco siempre, y ninguna te la marca el linter:
- Cambios fuera de alcance. Pediste tocar el servicio de pagos y hay un cambio en el módulo de auth. A veces es correcto. Casi siempre merece una pregunta.
- Dependencias nuevas. Un
importde un paquete que no estaba, o una línea nueva en el lockfile. El agente resuelve problemas trayendo librerías; tú decides si esa librería entra en tu árbol. - Secretos y credenciales. Una key, un token, una URL con contraseña. Los modelos las generan como placeholders plausibles y las dejan ahí. Un secreto commiteado no se borra con revertir: hay que rotarlo.
- Borrados silenciosos. Lo más peligroso, porque no salta a la vista. Una guarda que desaparece, un caso del
switchque ya no está, unifde validación que el agente consideró “redundante”.
La herramienta para esto es git add -p. En vez de aceptar el diff entero, Git te lo presenta hunk a hunk y decides sobre cada uno: lo apruebas, lo saltas, lo divides en trozos más pequeños. Revisar por hunks te obliga a mirar cada cambio en lugar de dar un git add . de fe.
git add -p # revisa y stagea hunk a hunk; y=incluir, n=saltar, s=dividir
Es lento a propósito. Ese es el punto. Si el agente generó trescientas líneas y tú las revisas en veinte segundos, no las has revisado: las has aceptado. Empieza por los archivos que no esperabas ver en el diff, que son donde vive lo raro. El tratamiento en profundidad de qué buscar y cómo estructurar la revisión de código generado por IA lo desarrollo en revisar código generado por IA con Git.
Antes de seguir, ponlo a prueba. En este ejercicio revisas un diff generado por IA poniéndote en la piel de Carlos ante el commit de un compañero: compila, los tests pasan, pero hay algo en el diff que no deberías dejar entrar. Encuéntralo.
Cargando ejercicio...
¿Cómo mantienes los diffs revisables?
Con commits atómicos: un cambio conceptual por commit. Un diff solo es revisable si cada commit hace una cosa y la hace entera. En cuanto un commit mezcla un refactor con una feature con un cambio de formato, la revisión se vuelve imposible, porque no puedes distinguir el cambio que importa del ruido que lo rodea.
El anti-patrón es el mega-commit “cambios del agente” con ochocientas líneas. Es indefendible por dos motivos. No se puede revisar, porque nadie lee ochocientas líneas mezcladas con atención real. Y no se puede revertir por partes, porque si el refactor era bueno pero la feature estaba rota, git revert sobre ese commit se lleva las dos cosas. Un historial de commits atómicos es reversible con bisturí; un mega-commit es reversible con hacha.
Separa siempre el refactor de la feature. Si el agente hace ambas cosas en una tanda, esa es justo la situación donde git add -p gana su sueldo: staged el refactor en un commit, la feature en otro. Y cuando la serie de commits WIP del agente sea un desastre, git rebase -i sobre la base de tu rama te deja reordenar, aplastar y reescribir mensajes hasta que el historial cuente la historia que quieres que se revise, no la secuencia de tanteos que el modelo fue dejando.
La regla mental es simple: el historial que compartes lo escribes tú, aunque el código lo escribiera el agente. El modelo produce el contenido; tú produces la narrativa del diff. Esa narrativa es lo que hace posible que otro humano, o tú dentro de seis meses, entienda por qué el código es como es.
Los commits atómicos y la revisión por hunks se entrenan haciéndolos, no leyéndolos. Si quieres pasar de “add, commit, push y rezar” a un Git que aguanta el ritmo de un agente, el curso Git de cero a profesional recorre este flujo de forma visual e interactiva, del reflog al rebase.
¿Cómo publicas sin destruir el trabajo del equipo?
Con git push --force-with-lease, nunca con --force a secas. En cuanto reescribes historia (y con IA reescribes constantemente: amends, squashes, rebases para limpiar los commits WIP), el push normal falla porque tu rama ya no es descendiente de lo que hay en el remoto. La tentación, la que el propio agente te sugerirá encantado, es resolverlo con --force. Y --force en una rama compartida es cómo pisas el trabajo de otra persona sin enterarte.
La diferencia es una línea de flag, y es la que decide si pisas o no el trabajo de otro:
git push --force # pisa el remoto sin mirar qué hay ahí. Peligroso en rama compartida
git push --force-with-lease # solo pisa si el remoto está donde tú crees. Aborta si no
--force-with-lease, sin argumentos, compara el estado actual del remoto contra tu remote-tracking ref, tu idea local de dónde está la rama remota. Si coinciden, el push procede. Si no coinciden, significa que alguien subió algo que tú no has visto, y el push se aborta en vez de sobrescribirlo. Es la diferencia entre “sobrescribe esto pase lo que pase” y “sobrescribe esto solo si nadie lo ha tocado desde la última vez que miré”.
Hay un matiz que conviene conocer. La garantía depende de que tu remote-tracking ref refleje la realidad. Si tienes un git fetch corriendo en un cronjob o en segundo plano, ese fetch actualiza tu remote-tracking ref sin que tú integres nada, y --force-with-lease compara contra ese valor ya actualizado en lugar de contra lo que tú realmente has visto. El lease se queda desactualizado y la protección se evapora. Para eso existe --force-if-includes como opción complementaria: verifica que las actualizaciones que llegaron por detrás estén integradas en tu rama local antes de permitir el push forzado. En un flujo normal, sin fetches automáticos raros, --force-with-lease a secas te cubre.
Un concepto nuevo cada semana
Un flujo de trabajo Git con IA de principio a fin
Junta todo lo anterior y el flujo se ordena solo. Rama por tarea, worktree si hay paralelismo, puntos de control por tanda, revisión del diff, squash a commits atómicos, push con lease, PR. En una sesión real se ve así:
git switch -c feature/checkout # rama por tarea, siempre
git worktree add -b feature/precios ../repo-agente-b # segundo agente en paralelo (-b crea la rama)
# ... por cada tanda verde del agente: git add -p para revisar, luego commit del punto de control ...
git add -p # revisas hunk a hunk lo que la tanda tocó
git commit -m "wip: checkout con el nuevo gateway"
git diff main...HEAD # relees el acumulado de la rama antes de reescribir historia
git rebase -i main # squash de los WIP a commits atómicos
git push --force-with-lease -u origin feature/checkout
El orden importa. Los puntos de control van durante el trabajo, no al final, porque su valor es servir de red mientras iteras. La revisión va antes del squash, porque quieres leer lo que el agente hizo de verdad, no la versión ya reordenada. Y el --force-with-lease va al final, cuando ya reescribiste historia con el rebase y necesitas actualizar la rama remota sin pisar a nadie.
Nada de esto depende de la herramienta. Es Git plano. Funciona igual con Claude Code, con Cursor, con Codex o con el agente que salga el mes que viene, porque la práctica vive en Git, no en el agente. Si dominas Git de verdad, ninguno de estos flujos te resulta nuevo: solo cambia la frecuencia con la que los usas. Si tu Git es de “add, commit, push y rezar”, la IA va a exponer ese hueco muy rápido, y merece la pena cerrarlo antes de dejar que un modelo escriba por ti a toda velocidad.
Errores comunes
Dejar que el agente acumule 300 líneas sin un solo commit
El más caro y el más común. El agente entra en racha, tú te distraes revisando otra cosa, y cuando vuelves hay trescientas líneas sin un punto de control por el medio. Si algo de eso está mal (y algo suele estarlo) no tienes ningún estado bueno al que volver con reset --hard. Toca rescatar a mano. Regla: si el agente lleva más de una tanda sin que hayas commiteado, para y commitea antes de seguir.
Aceptar el diff porque “los tests pasan”
Los tests verdes son una condición necesaria, no suficiente. No cubren el borrado silencioso de una guarda que nadie testeaba, ni el secreto hardcodeado, ni la dependencia nueva que no hacía falta. El verde solo garantiza que no rompiste lo que ya estaba cubierto; lo que el agente metió de nuevo no lo ve ningún test. Lee el diff igualmente.
git push --force en una rama compartida
Un rebase del agente sobre una rama que comparten cuatro personas, seguido de --force, se lleva por delante los commits que los otros subieron mientras tú iterabas. Y lo peor es que no te enteras: el push tiene éxito, el desastre se descubre después. Usa --force-with-lease y el push se aborta solo cuando iba a pisar algo.
El mega-commit “cambios del agente”
Ochocientas líneas en un commit con un mensaje genérico. No se puede revisar y no se puede revertir por partes. Es el equivalente Git de un try/catch que se traga todos los errores: parece que funciona hasta que necesitas saber qué pasó exactamente. Parte el trabajo en commits atómicos antes de compartir, aunque el agente lo generara todo de golpe.
Dos agentes en el mismo working tree
Dos procesos escribiendo archivos en la misma carpeta corrompen el index y entremezclan cambios que luego nadie sabe separar. Para eso están los worktrees: una carpeta y una rama por agente. Si vas a paralelizar, paraleliza bien.
Checklist de implementación
- Haces un commit antes de soltar al agente y otro después de cada tanda que deja el árbol verde
- Sabes recuperar un
reset --hardequivocado desde el reflog congit reset --hard HEAD@{n} - Cada agente en paralelo tiene su propio worktree, nunca dos en la misma carpeta
- Limpias los worktrees terminados con
git worktree remove(yprunesi borraste la carpeta a mano) - Revisas el diff generado por IA hunk a hunk con
git add -pantes de stagear - Los commits que compartes son atómicos: un cambio conceptual cada uno, refactor separado de feature
- Publicas reescrituras de historia con
git push --force-with-lease, nunca con--force
Preguntas Frecuentes
¿Un worktree es lo mismo que un clon?
No. Un clon copia la base de datos de objetos entera y crea un repositorio independiente que tienes que sincronizar como un remoto aparte. Un worktree comparte el mismo .git y los mismos objetos del repositorio original: solo añade un working tree nuevo con su propio HEAD e index. Ocupa mucho menos disco y no hay nada que sincronizar, porque el historial es literalmente el mismo.
¿Debo commitear cada respuesta del agente?
Durante el trabajo, sí: commits WIP desechables como puntos de control, para tener siempre un estado bueno al que volver. No te preocupes por el mensaje ni por la limpieza en esa fase. Antes de compartir la rama, colapsas toda esa serie con git rebase -i en los pocos commits atómicos que cuentan la historia de verdad. El historial de trabajo y el historial que publicas son dos cosas distintas.
¿--force-with-lease es siempre seguro?
Es mucho más seguro que --force, pero no es infalible. Aborta el push si el remoto no está donde tu remote-tracking ref cree que está, lo cual te protege de pisar el trabajo de otros en el caso normal. El matiz: si algo actualiza tu remote-tracking ref por detrás (un git fetch en un cronjob, por ejemplo) el lease se compara contra ese valor ya movido y pierde su garantía. Para blindarlo del todo en esos escenarios:
git push --force-with-lease --force-if-includes
¿Cómo reviso un diff enorme del agente de forma realista?
Por partes, nunca de una sentada. Empieza por los archivos que no esperabas ver en el diff, que es donde suele esconderse lo raro. Usa git add -p para ir hunk a hunk en lugar de leer un muro de texto, y trata cada archivo como una unidad. Si el diff es demasiado grande para revisarlo con atención, casi siempre es señal de que el agente hizo demasiado en una tanda y deberías haberlo partido en commits más pequeños antes.
¿Sirve esto con cualquier agente: Claude Code, Cursor, Codex?
Sí. Todo lo de aquí es Git plano y la práctica es agnóstica de la herramienta. Worktrees, puntos de control, git add -p y --force-with-lease funcionan igual sin importar qué agente esté escribiendo el código, porque operan sobre el repositorio, no sobre el modelo. Lo único que cambia entre herramientas es cómo lanzas al modelo; lo que haces con Git alrededor es lo mismo.
Dominar Git es lo que separa “dejar que el agente escriba” de “dirigir al agente con red de seguridad”. El curso Git de cero a profesional construye ese criterio paso a paso, ejercicio a ejercicio, para que le des las llaves del teclado a un modelo sin miedo a perder trabajo bueno.