- El equipo de Gradle intentó adoptar Jujutsu(jj), un VCS compatible con Git, para el desarrollo diario, pero pospuso su adopción por cómo se manejan los saltos de línea en
gradlew.bat para Windows
- El problema central es que
jj no puede leer las reglas EOL por archivo de .gitattributes, por lo que no puede aplicar *.bat text eol=crlf
- Git normaliza
gradlew.bat como LF en el repositorio y lo hace checkout como CRLF en el directorio de trabajo, pero jj lo interpreta como un cambio y genera continuamente una modificación fantasma
- Se puede evitar haciendo commit de
gradlew.bat tal cual en CRLF y cambiando a *.bat -text, pero si un editor lo cambia a LF, Git ya no lo corrige automáticamente, lo que puede afectar la estabilidad de ejecución en Windows
- Por ahora, el equipo de Gradle operará múltiples directorios de trabajo con
git worktree y planea volver a intentarlo cuando se implemente el soporte de eol en jj
La causa real que impidió la adopción: gradlew.bat y .gitattributes
- Jujutsu(
jj) es un sistema de control de versiones compatible con Git que, al usar el backend de Git, almacena commits y archivos en Git y funciona junto con repositorios Git existentes
jj elimina el área de staging y representa la copia de trabajo como un commit real que se actualiza continuamente
- Después de editar o reordenar commits, hace rebase automáticamente de los commits posteriores; los conflictos pueden quedar registrados en vez de detener el trabajo, y también es posible deshacer desde el log de operaciones
- El intento de un ingeniero de Gradle de reemplazar
git por jj no se frenó por el modelo de jj, sino por un pequeño detalle en la forma en que el proyecto Gradle queda en disco
En Windows, gradlew.bat necesita CRLF
- Un repositorio Gradle típico incluye
gradlew.bat junto con Gradle Wrapper
gradlew.bat usa labels y goto, y cmd.exe puede procesar incorrectamente archivos batch que solo tienen saltos de línea LF
- Para ejecutarse de forma estable en Windows,
gradlew.bat debe estar checkout en CRLF
- El proyecto Gradle fuerza este requisito con
.gitattributes
*.bat text eol=crlf
- En Git, este comportamiento es un flujo estándar
- El archivo se almacena en el índice en forma normalizada
- El atributo
eol=crlf materializa CRLF en la copia de trabajo al hacer checkout
- Los cambios editados se normalizan nuevamente al guardarse
La falta de soporte de .gitattributes en jj produce phantom diff
jj no lee .gitattributes, por lo que no puede aplicar reglas de saltos de línea por archivo
- La funcionalidad relacionada se rastrea en el issue antiguo jj-vcs/jj#53, y se está discutiendo al menos el soporte del atributo
eol
- Actualmente, el único mecanismo disponible en
jj es la configuración global working-copy.eol-conversion
- Se aplica de una sola vez a todos los archivos, como
core.autocrlf de Git
- No permite especificar por separado solo los archivos
.bat
- En un repo colocated, el problema aparece de inmediato
- Git guarda
gradlew.bat como LF y lo hace checkout como CRLF
- Como
jj no respeta ese atributo, registra el CRLF en disco como un cambio frente al blob LF de Git
- En los proyectos Gradle afectados, aparece un cambio falso persistente en
gradlew.bat
- Este problema no es exclusivo del equipo de Gradle
- Un usuario abandonó el uso de
jj después de sufrir phantom changes en una cantidad de archivos de seis cifras
Soluciones alternativas propuestas y sus límites
- El autor de
jj propuso en el issue un workspace non-colocated como una ruta posible
- Ejemplo: usar
jj git init --git-repo=<path> fuera de la copia de trabajo de Git
- El ingeniero de Gradle considera que este enfoque tampoco resuelve el problema
- Si
jj materializa gradlew.bat desde el blob LF almacenado, en disco bajará como LF
- Un
gradlew.bat con LF no se ejecuta en Windows
- Se puede forzar CRLF con la configuración global
working-copy.eol-conversion = input-output
- Pero se convierte en otro problema, porque reescribe todos los archivos de texto como CRLF al hacer checkout
Una solución que funciona, pero es frágil
- El método que realmente funciona es hacer commit de
gradlew.bat en el repositorio tal cual en CRLF y dejar de normalizarlo
- La regla cambia así
- Antes:
*.bat text eol=crlf
- Después:
*.bat -text
- Con esto, Git y
jj coinciden en usar CRLF tanto en el árbol almacenado como en la copia de trabajo
- Desaparece la conversión
- También desaparece el phantom diff
- Y se mantiene la ejecución en Windows
- A cambio,
*.bat -text renuncia a la recuperación automática que ofrecía text eol=crlf
- Antes, aunque entrara LF por error, al hacer commit se volvía a normalizar silenciosamente
- Ahora, el LF puede quedar tal cual y hacer que
gradlew.bat se rompa silenciosamente en Windows
- Esta conversión solo debe hacerse una vez, y las ediciones erróneas normalmente pueden detectarse en revisión
- Aun así, el equipo de Gradle consideró difícil apostar el ecosistema a la premisa de que todos los proyectos Gradle hagan commit directo de CRLF y que ningún editor lo cambie silenciosamente
La alternativa que mantendrán por ahora: git worktree
- Los workspaces de
jj ofrecen múltiples directorios de trabajo que comparten un mismo repositorio, de modo que una ejecución larga de CI o cambios en curso no bloqueen la siguiente tarea
- El equipo de Gradle ya cubre suficientemente esa misma necesidad con
git worktree
git worktree add ../gradle-feature-x feature-x
git worktree add ../gradle-hotfix hotfix-7.6
- Este enfoque usa dos checkouts y un solo almacén de objetos
- Permite operar en paralelo y mantener un checkout de
main de larga vida sin stash ni un segundo clone
- El equipo de Gradle lleva años usando este patrón
Posibilidad de reevaluar jj
- Este caso no es un veredicto definitivo sobre
jj en el proyecto Gradle
- Solo el operation log de
jj ya merece una evaluación más profunda
- El soporte de
eol se trata como un problema resoluble, y en el issue también se discute una implementación mediante gix-filter
- Cuando ese soporte llegue, el equipo de Gradle planea volver a intentarlo
- Hasta entonces, mantendrán Git por
gradlew.bat, y git worktree cubrirá el rol que necesitan
1 comentarios
Opiniones en Lobste.rs
Creo que la configuración
autocrlfde Git y la posterior configuración de.gitattributesfueron un error desde el principio y lo seguirán siendoUn sistema de control de versiones no debería almacenar los archivos dentro del repositorio de forma distinta a los archivos en disco
Si se quiere imponer un formato de archivo, eso es trabajo de un linter, hooks de commit o hooks de merge, no del sistema de control de versiones
core.{eol, safecrlf, autocrlf}como obsoletoLa conversión de finales de línea se agregó solo por Windows; el uso exclusivo de ␍ ya había muerto antes de Git, y todos salvo Windows se consolidaron en el uso exclusivo de ␊
Hoy, probablemente el principal caso que realmente se preocupa por ␍␊ sean los archivos batch
Todos los lenguajes se esfuerzan por admitir ␍␊, pero ya casi nada lo exige ni lo genera
Si Notepad todavía usa ␍␊ como valor predeterminado o solo admite eso, quienes usen Notepad deberían hacerse cargo de las consecuencias
Un área más interesante en la compatibilidad con Windows son los modos de archivo
Git adoptó los modos de Unix y quedan rastros de eso, así que al trabajar en Windows a veces aparecen errores de modo involuntarios
Si las herramientas fueran lo bastante buenas como para usar espacios automáticamente al hacer checkout, o ajustar el archivo a una cantidad de espacios extrañamente específica que alguien defiende con firmeza, podríamos dejar de discutir sobre tabs y espacios
Más aún, incluso se podría pensar que el sistema de control de versiones debería guardar un árbol de sintaxis abstracta (AST) en lugar del texto original
Hoy sería difícil convencer a alguien, pero es posible imaginar otra línea temporal o un futuro donde este enfoque resulte muy natural
No entiendo dónde se supone que se ejecutaría un archivo
.batsi no es en WindowsTambién me pregunto en qué caso sería deseable que se comportara de forma aunque sea un poco distinta
No me convence el argumento de que no se deban commitear los archivos con el formato de finales de línea que necesitan
El punto parece ser que “alguien podría arruinar los finales de línea, commitearlo, pasar la revisión de código y terminar metiendo un script roto”
Todo eso es cierto, pero ¿no es para eso que existen las verificaciones de lint en CI?
Si se escanean esos archivos en busca de finales de línea que no sean CRLF y se hace fallar la CI cuando se encuentran, un cambio que arruine los finales de línea no puede pasar la etapa de pull request
La respuesta de “solo agrega CI” no alcanza
El proyecto relacionado podría ser un mod de Minecraft hecho por alguien relativamente principiante, y cualquiera de ellos podría romper
gradle.batsin darse cuentaYo también tengo cierta responsabilidad por haber promovido jj como una alternativa mucho mejor que CLI Git para quienes están empezando
¿Sería posible pasar de
gradlew.batagradlew.ps1, es decir, decmd.exe/archivos batch a PowerShell?Ahora TAL VEZ se pueda asumir que el 90% de la base instalada de Windows tiene una versión suficientemente reciente de PowerShell
Pero todavía hay casos en los que hay que elevar los permisos de ejecución predeterminados, y en entornos corporativos puede estar bloqueado
No sé cuánto has usado PowerShell, pero no se sentía como “hay bash, así que ejecuta el archivo
.sh”Cada versión de Windows tenía un conjunto distinto de funciones, como comparar
kshconbash4