1 puntos por GN⁺ 9 시간 전 | 1 comentarios | Compartir por WhatsApp
  • 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

 
GN⁺ 9 시간 전
Opiniones en Lobste.rs
  • Creo que la configuración autocrlf de Git y la posterior configuración de .gitattributes fueron un error desde el principio y lo seguirán siendo
    Un 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

    • Estoy más o menos del lado de quienes creen que fue un error desde el principio, pero ahora creo que hay que declararlo claramente como un error y, en particular, marcar core.{eol, safecrlf, autocrlf} como obsoleto
      La 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
    • Yo siento más bien lo contrario: el control de versiones no debería hacer menos de estas cosas, sino más
      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 .bat si no es en Windows
    Tambié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

    • Creo que el objetivo del artículo no es solo hablar de jj en Gradle, sino mostrar que este problema es relevante para todas las personas y proyectos que quieran usar jj en el ecosistema JVM
      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.bat sin darse cuenta
      Yo 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.bat a gradlew.ps1, es decir, de cmd.exe/archivos batch a PowerShell?

    • No participé en Gradle, pero hace unos años intenté hacer eso en Leiningen y en ese momento era un desastre total, nada práctico
      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 ksh con bash4