1 points par GN⁺ 9 시간 전 | 1 commentaires | Partager sur WhatsApp
  • L’équipe Gradle a voulu adopter Jujutsu (jj), un VCS compatible avec Git, dans son développement quotidien, mais a reporté cette adoption à cause de la gestion des fins de ligne dans gradlew.bat sous Windows
  • Le problème central est que jj ne sait pas lire les règles EOL par fichier de .gitattributes, et ne peut donc pas appliquer *.bat text eol=crlf
  • Git normalise gradlew.bat en LF dans le dépôt et le checkout en CRLF dans le répertoire de travail, mais jj considère cela comme une modification et crée en permanence des modifications fantômes
  • Il serait possible de contourner le problème en committant gradlew.bat directement en CRLF et en remplaçant par *.bat -text, mais si un éditeur le repasse en LF, Git ne le réparera plus automatiquement, ce qui peut fragiliser la fiabilité d’exécution sous Windows
  • L’équipe Gradle prévoit donc, pour l’instant, de continuer à utiliser git worktree pour gérer plusieurs répertoires de travail, puis de réessayer lorsque la prise en charge de eol sera implémentée dans jj

La vraie cause qui a bloqué l’adoption : gradlew.bat et .gitattributes

  • Jujutsu (jj) est un système de gestion de versions compatible avec Git qui, lorsqu’il utilise un backend Git, stocke les commits et les fichiers dans Git et fonctionne aux côtés de dépôts Git existants
  • jj supprime la zone de staging et représente la copie de travail comme un vrai commit continuellement mis à jour
  • Après l’édition ou la réorganisation de commits, il rebase automatiquement les commits suivants ; les conflits peuvent être conservés dans l’historique au lieu d’interrompre le travail, et il est aussi possible de revenir en arrière via le journal des opérations
  • La tentative d’un ingénieur Gradle de remplacer git par jj n’a pas été bloquée par le modèle de jj, mais par un petit détail de la manière dont le projet Gradle est matérialisé sur disque

Pourquoi gradlew.bat a besoin de CRLF sous Windows

  • Un dépôt Gradle classique inclut gradlew.bat avec le Gradle Wrapper
  • gradlew.bat utilise des labels et goto, et cmd.exe peut mal interpréter les fichiers batch qui n’emploient que des fins de ligne LF
  • Pour s’exécuter de manière fiable sous Windows, gradlew.bat doit être checkouté en CRLF
  • Les projets Gradle imposent cette contrainte via .gitattributes
*.bat text eol=crlf
  • Avec Git, ce comportement suit le flux standard
    • le fichier est stocké sous forme normalisée dans l’index
    • l’attribut eol=crlf matérialise des fins de ligne CRLF dans la copie de travail au moment du checkout
    • les modifications sont de nouveau normalisées lors de leur enregistrement

Les diff fantômes provoqués par l’absence de prise en charge de .gitattributes dans jj

  • jj ne lit pas .gitattributes et ne peut donc pas appliquer de règles de fins de ligne par fichier
  • Cette fonctionnalité est suivie depuis longtemps dans jj-vcs/jj#53, où la prise en charge de l’attribut eol est notamment discutée
  • À l’heure actuelle, le seul mécanisme disponible dans jj est le réglage global working-copy.eol-conversion
    • comme core.autocrlf de Git, il s’applique à tous les fichiers d’un coup
    • il ne permet pas de cibler uniquement les fichiers .bat
  • Dans un dépôt colocalisé, le problème apparaît immédiatement
    • Git stocke gradlew.bat en LF et le checkout en CRLF
    • comme jj ne respecte pas cet attribut, il enregistre le CRLF présent sur disque comme une modification par rapport au blob Git en LF
    • dans les projets Gradle concernés, cela produit des faux changements persistants sur gradlew.bat
  • Ce problème ne concerne pas uniquement l’équipe Gradle
    • un utilisateur explique avoir renoncé à jj après des changements fantômes sur un nombre de fichiers à six chiffres

Les contournements proposés et leurs limites

  • Dans l’issue, l’auteur de jj propose comme piste un workspace non colocalisé
    • par exemple : utiliser jj git init --git-repo=<path> en dehors de la copie de travail Git
  • L’ingénieur Gradle estime que cette approche ne résout pas non plus le problème
    • lorsque jj matérialise gradlew.bat à partir du blob LF stocké, le fichier est écrit sur disque en LF
    • or un gradlew.bat en LF ne fonctionne pas sous Windows
  • Il est possible de forcer CRLF avec le réglage global working-copy.eol-conversion = input-output
    • mais cela réécrit aussi tous les fichiers texte en CRLF au checkout, ce qui crée d’autres problèmes

Une solution qui fonctionne, mais fragile

  • La seule méthode réellement fonctionnelle consiste à committer gradlew.bat directement en CRLF dans le dépôt et à arrêter la normalisation
  • La règle devient alors
    • avant : *.bat text eol=crlf
    • après : *.bat -text
  • Dans ce cas, Git et jj s’accordent tous deux sur l’usage du CRLF, à la fois dans l’arbre stocké et dans la copie de travail
    • il n’y a plus de conversion
    • les diff fantômes disparaissent
    • l’exécution sous Windows continue de fonctionner
  • En contrepartie, *.bat -text abandonne la réparation automatique qu’apportait text eol=crlf
    • auparavant, si du LF était introduit par erreur, il était renormalisé discrètement au commit
    • désormais, le LF reste tel quel et gradlew.bat peut se casser silencieusement sous Windows
  • Cette conversion n’est à faire qu’une seule fois, et les mauvaises modifications peuvent souvent être repérées en revue de code
  • Malgré cela, l’équipe Gradle estime qu’il est difficile de faire reposer tout l’écosystème sur l’hypothèse que tous les projets Gradle vont committer explicitement du CRLF et qu’aucun éditeur ne le modifiera discrètement

L’alternative conservée pour l’instant : git worktree

  • Les workspaces de jj permettent de disposer de plusieurs répertoires de travail partageant un même dépôt, afin que de longues exécutions de CI ou des changements en cours ne bloquent pas le travail suivant
  • L’équipe Gradle couvre déjà correctement ce besoin avec git worktree
git worktree add ../gradle-feature-x feature-x
git worktree add ../gradle-hotfix    hotfix-7.6
  • Cette approche utilise deux checkouts et un seul magasin d’objets
  • Elle permet de mener du travail en parallèle et de conserver un checkout main de long terme, sans stash ni second clone
  • L’équipe Gradle utilise ce schéma depuis des années

Possibilité de réévaluer jj

  • Ce cas ne constitue pas un jugement définitif sur jj pour les projets Gradle
  • Rien que le journal des opérations de jj mérite un examen plus approfondi
  • La prise en charge de eol est traitée comme un problème solvable, et l’issue évoque aussi une implémentation via gix-filter
  • Si cette prise en charge arrive, l’équipe Gradle prévoit de réessayer
  • D’ici là, Gradle reste sur Git à cause de gradlew.bat, et git worktree remplit le rôle nécessaire

1 commentaires

 
GN⁺ 9 시간 전
Avis sur Lobste.rs
  • Je pense que la configuration autocrlf de Git, puis la configuration .gitattributes, ont été une erreur dès le départ et le resteront
    Un système de gestion de versions ne devrait pas stocker les fichiers du dépôt différemment des fichiers sur disque
    Si l’on veut imposer un format de fichier, c’est le rôle des linters, des hooks de commit et des hooks de merge, pas celui du système de gestion de versions

    • Je penche aussi du côté de ceux qui pensent que c’était une erreur dès le départ, mais aujourd’hui il faudrait clairement déclarer que c’en est une et mettre notamment core.{eol, safecrlf, autocrlf} sur la voie de la dépréciation
      La conversion des fins de ligne n’a été ajoutée qu’à cause de Windows ; le ␍ seul était déjà mort avant Git, et tout le monde sauf Windows s’est stabilisé sur le ␊ seul
      Aujourd’hui, le principal cas qui se soucie réellement de ␍␊ est probablement celui des fichiers batch
      Tous les langages font des efforts pour prendre en charge ␍␊, mais presque plus rien ne l’exige ni ne le produit
      Si Notepad utilise encore ␍␊ par défaut, ou ne prend en charge que cela, les personnes qui utilisent Notepad doivent en assumer les conséquences
      Le domaine le plus intéressant pour la compatibilité Windows, ce sont plutôt les modes de fichier
      Git a repris les modes Unix et il en reste des traces, si bien qu’en travaillant sous Windows on peut parfois introduire des erreurs de mode involontaires
    • Au contraire, j’ai plutôt l’impression que la gestion de versions devrait faire davantage ce genre de choses, pas moins
      Si les outils devenaient assez bons pour utiliser automatiquement des espaces au checkout, ou pour ajuster le code au nombre d’espaces étrangement précis auquel quelqu’un tient absolument, on pourrait mettre fin aux débats tabs vs spaces
      On pourrait même aller plus loin et considérer qu’un système de gestion de versions devrait stocker non pas le texte brut, mais l’arbre syntaxique abstrait (AST)
      Aujourd’hui ce serait difficile à défendre, mais on peut imaginer une autre ligne temporelle, ou un futur, où cette approche paraîtrait très naturelle
  • Je ne vois pas où l’on exécuterait des fichiers .bat ailleurs que sous Windows
    Je me demande aussi dans quel cas un autre comportement serait un tant soit peu souhaitable

  • L’argument selon lequel il ne faudrait pas committer les fichiers exactement avec le format de fin de ligne requis ne me convainc pas
    Le point central semble être : « quelqu’un peut casser les fins de ligne, committer le changement, passer la revue de code, et introduire un script cassé »
    Tout cela est vrai, mais c’est bien pour cela qu’on a des contrôles de lint en CI, non ?
    Si l’on scanne ce fichier pour détecter des fins de ligne qui ne sont pas en CRLF et que la CI échoue lorsqu’on en trouve, une modification qui casse les fins de ligne ne peut pas passer l’étape de la pull request

    • L’objectif de cet article ne semble pas seulement être de traiter le cas de jj chez Gradle, mais de montrer que ce problème concerne toutes les personnes et tous les projets qui voudraient utiliser jj dans l’écosystème JVM
      Répondre « il suffit d’ajouter de la CI » n’est pas suffisant
      Le projet concerné peut très bien être un mod Minecraft créé par des débutants, et n’importe lequel d’entre eux pourrait casser gradle.bat sans s’en rendre compte
      J’ai moi-même une part de responsabilité, puisque j’ai présenté jj comme une alternative bien meilleure que Git en ligne de commande pour les débutants
  • Passer de gradlew.bat à gradlew.ps1, autrement dit de cmd.exe/fichiers batch à PowerShell, serait-ce une option possible ?

    • Je n’ai pas participé à Gradle, mais j’ai essayé de faire cela avec Leiningen il y a quelques années, et à l’époque c’était un bazar complet, absolument pas pratique
      Aujourd’hui, on peut PEUT-ÊTRE supposer que 90 % du parc Windows installé dispose d’une version de PowerShell suffisamment récente
      Mais il reste des cas où il faut relever les droits d’exécution par défaut, et dans les environnements d’entreprise cela peut être verrouillé
      Je ne sais pas à quel point vous avez utilisé PowerShell, mais ce n’était pas du tout du genre « puisqu’il y a bash, il suffit d’exécuter le fichier .sh »
      Les ensembles de fonctionnalités variaient selon les versions de Windows, au point que cela donnait l’impression de comparer ksh et bash4