- 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
Avis sur Lobste.rs
Je pense que la configuration
autocrlfde Git, puis la configuration.gitattributes, ont été une erreur dès le départ et le resterontUn 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
core.{eol, safecrlf, autocrlf}sur la voie de la dépréciationLa 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
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
.batailleurs que sous WindowsJe 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
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.batsans s’en rendre compteJ’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 decmd.exe/fichiers batch à PowerShell, serait-ce une option possible ?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
kshetbash4