Diff gigante de tu agente: separa formato de lógica
Un diff de agente inflado es un diff en el que la mayoría de las líneas cambiadas no tocan el comportamiento: sangría, finales de línea, comillas, imports reordenados o bloques movidos de sitio. Para diagnosticarlo, compara git diff con y sin -w, --ignore-cr-at-eol y --color-moved. Para arreglarlo, separa un commit de estilo y otro de lógica.
Síntoma: cientos de líneas para un cambio de diez
Si el diff que te deja el agente es mucho más grande que la tarea que le pediste, el problema no es solo estético: estás aprobando código que no has podido leer. Cuando el 90 % del diff es ruido, la línea que de verdad cambia una condición queda enterrada entre cambios de sangría.
Supongamos que le pides a tu agente que corrija una comparación en src/billing/invoice.ts. Lo esperable son unas pocas líneas. En cambio, git diff --stat muestra tres archivos y varios cientos de líneas cambiadas. Lo primero que te pide el cuerpo es aprobar si los tests pasan, o rechazarlo todo y repetir. Ninguna de las dos cosas es buena: en el primer caso revisas a ciegas y en el segundo pierdes un cambio que quizá era correcto.
La salida es averiguar qué parte del diff es formato y qué parte es lógica. Con git se hace en un par de minutos.
Las cinco causas habituales y cómo se reconocen
Casi todo el ruido de un diff de agente cae en una de estas categorías, y cada una tiene un comando que la deja en evidencia. La tabla te sirve de referencia rápida.
| Causa | Cómo se ve en el diff | Comando que la confirma |
|---|---|---|
| Sangría o espacios cambiados | Bloques enteros en rojo y verde que parecen idénticos | git diff -w |
| Finales de línea CRLF frente a LF | Todas las líneas del archivo cambiadas | git diff --ignore-cr-at-eol y git ls-files --eol |
| Código movido de sitio | Una función borrada arriba y añadida abajo | git diff --color-moved |
| Formateador con otra configuración | Comillas, comas finales o imports reordenados | Formatear la base y comparar (script más abajo) |
| El diff es grande de verdad | Ningún comando lo reduce | Ninguno: la tarea era demasiado grande |
La última fila importa: si ningún filtro reduce el diff, el problema no está en el formato sino en el tamaño de la tarea. En ese caso lo que toca es dividirla, no limpiar el diff.
Confirma la causa con cuatro comandos de git
Comparar el tamaño del diff con y sin cada filtro te dice qué proporción es ruido. Todos los comandos son de solo lectura y no tocan tu directorio de trabajo. Este bloque mide el diff completo, el diff ignorando espacios y el diff ignorando el retorno de carro final:
# Archivos tocados y volumen por archivo
git diff --stat
# Líneas de diff: tal cual, sin espacios y sin CR al final de línea
git diff | wc -l
git diff -w | wc -l
git diff --ignore-cr-at-eol | wc -l
# Finales de línea de cada archivo modificado (índice frente a directorio)
git ls-files --eol $(git diff --name-only)
Según la documentación de git diff, -w ignora los espacios en blanco al comparar líneas "incluso si una línea tiene espacios donde la otra no tiene ninguno", y --ignore-cr-at-eol ignora el retorno de carro al final de cada línea. Si el número de líneas cae de cientos a unas decenas con alguno de los dos, ya sabes de dónde viene el ruido.
En la salida de git ls-files --eol, el prefijo i/ describe el archivo en el índice y w/ en el directorio de trabajo, tal como explica la documentación de git ls-files. Si ves i/lf junto a w/crlf o w/mixed, el agente (o su herramienta de escritura) ha cambiado los finales de línea.
Para el código movido, usa la detección de bloques movidos permitiendo cambios de sangría:
git diff --color-moved=dimmed-zebra --color-moved-ws=allow-indentation-change
Con dimmed-zebra, git colorea de forma distinta los bloques movidos y atenúa sus partes "poco interesantes", así que solo te quedan resaltados los bordes. La opción allow-indentation-change agrupa un bloque como movido aunque haya cambiado de sangría, siempre que el cambio sea el mismo en cada línea.
Por qué -w puede ocultarte un bug en Python o YAML
Usa -w para medir el ruido, nunca para dar el diff por revisado si el lenguaje depende de la sangría. En Python, YAML o un Makefile, mover una línea un nivel a la izquierda cambia lo que hace el programa: una sentencia sale de un if o una clave pasa a otro nivel del documento.
Por ejemplo, si el agente saca un return fuera de un bucle en Python, git diff -w puede no enseñarte nada en esa línea, porque solo ha cambiado el espacio inicial. En estos lenguajes:
- Mide con
-wpara saber cuánto ruido hay. - Revisa con
--color-moved-ws=allow-indentation-change, que sigue mostrando los cambios de sangría. - Haz la revisión final sobre el diff sin filtros, después de separar el formato (siguiente sección).
Lo mismo vale para el botón de ocultar espacios en blanco de la revisión de pull requests en GitHub. La documentación de GitHub explica que esa elección se aplica a esa pull request y se recuerda la próxima vez que la abras. Es cómodo, pero en Python te puede esconder justo la línea que importa.
Separa el cambio en un commit de estilo y otro de lógica
La forma más limpia de quedarte solo con la lógica es formatear primero la versión original con el formateador del proyecto, hacer commit, y luego recuperar la versión del agente y formatearla igual. Como el formateador produce siempre la misma salida para el mismo código, lo que quede en el diff es lógica.
El script aparta el cambio del agente con git stash, formatea la base, la commitea y recupera los archivos del agente desde el stash. Usa Prettier como ejemplo; requiere que esté instalado en el proyecto y lo puedes cambiar por tu formateador (Black, Ruff, gofmt...):
FILES=$(git diff --name-only)
git stash push -m "cambio-agente" -- $FILES
npx prettier --write --ignore-unknown $FILES
git commit -am "style: formatear archivos antes del cambio"
git restore --source=stash@{0} -- $FILES
npx prettier --write --ignore-unknown $FILES
git diff
Por qué funciona cada paso, según la documentación oficial:
git stash push -- <rutas>guarda solo esos archivos y los devuelve al estado deHEAD, dejando el resto intacto (git stash).- Una entrada de stash es un commit cuyo árbol registra el estado del directorio de trabajo, por eso
git restore --sourceaceptastash@{0}como origen igual que acepta cualquier commit (git restore). --writereescribe los archivos en su sitio e--ignore-unknownhace que Prettier se salte los tipos de archivo que no reconoce (CLI de Prettier).
Cuando hayas revisado el git diff final y hecho commit de la lógica, borra el stash con git stash drop. No lo borres antes: es tu copia de seguridad del trabajo del agente.
Limitaciones del script: no funciona con nombres de archivo que contengan espacios, ignora los archivos nuevos sin seguimiento (que no tienen ruido que separar) y git commit -am incluye cualquier otro cambio rastreado que tuvieras sin commitear. Ejecútalo con el resto del árbol limpio.
Si en el segundo paso git responde que no hay nada que commitear, la base ya estaba formateada. Eso te dice algo: el ruido no venía de una configuración distinta, sino de que el agente reescribió el estilo a mano. El tercer paso lo normaliza igualmente.
Qué hacer según lo que hayas encontrado
Cada causa tiene un arreglo distinto y casi todos van en un commit propio, separado de la lógica. Este árbol de decisión resume la respuesta:
- El diff baja mucho con
--ignore-cr-at-eol. Normaliza los finales de línea en un commit aparte. La documentación de gitattributes recomienda añadir* text=autoen.gitattributesy ejecutargit add --renormalize .desde un directorio limpio. - El diff baja mucho con
-w. Usa el script de la sección anterior, o pide al agente que deshaga los cambios en las líneas que no necesitaba tocar. --color-movedmarca bloques grandes. Revisa solo los bordes de cada bloque. Si el movimiento no era parte de la tarea, pide que vaya en un commit separado.- Ves comillas, comas o imports cambiados y nada reduce el diff. El agente aplicó otro estilo. Usa el script: el formateador del proyecto lo deshace.
- Nada reduce el diff y todo son cambios reales. La tarea era demasiado grande. Descarta o guarda el cambio y repítela en trozos más pequeños.
Si el commit de estilo toca muchos archivos, añade su hash a .git-blame-ignore-revs en la raíz del repositorio y configura git config blame.ignoreRevsFile .git-blame-ignore-revs. Con esto, git blame atribuye esas líneas al commit anterior (git blame), y la vista blame de GitHub también lee ese archivo. Añade el hash cuando el commit ya esté en la rama principal: un rebase o un squash lo cambian.
Evita que el ruido vuelva en la siguiente sesión
Una instrucción concreta en el archivo de instrucciones del proyecto reduce el ruido, pero no lo garantiza. AGENTS.md es el formato abierto que leen herramientas como Codex, Copilot, Cursor o Gemini CLI según agents.md. Claude Code lo lee directamente desde la v2.1.277 si no hay CLAUDE.md en el proyecto, como explica su documentación de memoria. Esta plantilla de sección puedes copiarla tal cual:
## Formato y alcance del diff
- No cambies sangría, comillas ni orden de imports en líneas que la tarea no exige tocar.
- No muevas funciones ni bloques de sitio salvo que se te pida.
- Para formatear, ejecuta `npx prettier --write` solo sobre los archivos que hayas modificado.
- Mantén los finales de línea LF que ya usa el repositorio.
- Si un archivo necesita reformatearse entero, propónlo como tarea aparte.
La propia documentación de Claude Code avisa de que trata esos archivos como contexto y no como configuración obligatoria. Si necesitas que el formateo se aplique siempre después de cada edición, escríbelo como hook, que se ejecuta pase lo que pase. Añade también .gitattributes con los finales de línea fijados: así el problema de CRLF desaparece del todo.
Como referencia práctica, no como dato: antes de lanzar una tarea, comprueba que npx prettier --check (o el equivalente de tu formateador) pasa sobre los archivos que va a tocar el agente. Si la base ya está formateada, cualquier ruido posterior será culpa del agente y se detecta enseguida.
Cuándo no merece la pena separar el diff
Separar estilo y lógica tiene un coste, y no siempre compensa. Puedes saltártelo en estos casos:
- Tu equipo hace squash merge. Los dos commits acaban fundidos en uno en la rama principal. Si quieres conservar la separación, abre una pull request solo de estilo y otra de lógica.
- El archivo es nuevo o lo reescribes entero a propósito. No hay versión anterior que comparar, así que no hay ruido que separar.
- El proyecto no tiene formateador. El script no tiene a qué normalizar. Pide al agente que revierta las líneas que no necesitaba tocar, o adopta un formateador en una tarea aparte antes de seguir.
- El ruido es de unas pocas líneas. Si
git diff -w | wc -lygit diff | wc -lcasi coinciden, revisa el diff tal cual.
Preguntas frecuentes
¿Puedo configurar git para que siempre muestre el código movido?
Sí. La documentación de git diff indica que el modo de --color-moved se puede fijar con la opción de configuración diff.colorMoved y el tratamiento de espacios con diff.colorMovedWS. Por ejemplo, git config --global diff.colorMoved dimmed-zebra lo activa en todos tus diffs.
¿Y si el diff ya está commiteado por el agente?
Aplica el mismo análisis sobre el rango de commits, por ejemplo git diff -w main...HEAD | wc -l. Si quieres separar, la forma más sencilla es deshacer el commit con git reset --soft HEAD~1 en tu rama local, quitar los cambios del índice y ejecutar el script desde ahí. No lo hagas si ya has publicado la rama y otra persona trabaja sobre ella.
¿-w y --ignore-cr-at-eol modifican mis archivos?
No. Son opciones de comparación de git diff: cambian lo que ves, no lo que hay en disco ni en el índice. Solo el script de separación y git add --renormalize escriben cambios.