2026-08-04 · lectura de 3 minutos
El repositorio cuyos checkouts nacían sucios
Añadimos a nuestra puerta de merge una sonda que recalcula el digest del contenido del checkout antes de tomar una decisión de lanzamiento. Su primera ejecución en CI falló. El repositorio donde falló era el nuestro y el archivo era un README. Ningún diff mostraba nada raro, porque el defecto estaba en los fines de línea, que los diffs no muestran por defecto.
La sonda
Nuestras decisiones de lanzamiento se vinculan a un digest del contenido exacto que se revisó. Hasta este cambio, el digest se calculaba donde ocurría la revisión. La sonda nueva lo recalcula en el momento del merge, en CI, desde un checkout fresco, y se niega a certificar si los dos discrepan.
Una sonda así hace una suposición silenciosa: que clonar un repositorio y leerlo devuelve el contenido confirmado. Esa suposición tiene una excepción conocida, y nosotros vivíamos dentro de ella.
El fallo
La primera ejecución en CI reportó un digest que no coincidía, en un checkout que nadie había modificado. Git status en un clon fresco mostraba por qué:
$ git clone <repo> && cd <repo> $ git status modified: commercial/README.md # nobody touched anything # .gitattributes says: *.md text eol=lf # the committed bytes say: CRLF
El repositorio declara en .gitattributes que los archivos markdown usan fin de línea LF. Un archivo, commercial/README.md, se había confirmado con bytes CRLF antes de que existiera esa declaración. Git resuelve el desacuerdo al hacer checkout: materializa el archivo según la declaración, nota que la copia de trabajo ya no coincide con el índice y reporta el archivo como modificado incluso en un clon fresco.
La modificación fantasma aparecía en cada clon fresco, y se había ignorado durante meses. La sonda no podía ignorarla, porque un checkout que difiere del árbol confirmado es la condición que existe para atrapar.
El arreglo
Renormalizar el archivo para que sus bytes confirmados coincidan con la declaración: git add --renormalize, un archivo cambiado, solo fines de línea. Era el único archivo del repositorio en ese estado, cosa que la sonda verificó en la misma ejecución.
Qué se generaliza
- Una declaración de fin de línea y los bytes confirmados pueden discrepar, y Git no te avisará al confirmar si los bytes preceden a la regla. Ejecuta git add --renormalize después de introducir o cambiar .gitattributes, y comprueba el resultado en CI.
- Cualquier sistema que asuma la invariancia del checkout limpio, incluidos cachés indexados por contenido del árbol, builds reproducibles y digests de contenido, hereda este modo de fallo en silencio.
- Una sonda nueva que falla en su primera ejecución está haciendo su trabajo. Los meses de verde antes de que existiera no eran salud; eran la ausencia de medición.
Fuentes: la sonda va en el workflow del governor; los registros de decisión son públicos. Cómo funcionan nuestras puertas de lanzamiento · La guía de listo para producción
La discrepancia, la renormalización y el primer digest limpio están todos en el historial del repositorio y en el libro mayor de decisiones.
Todas las notas de ingeniería →