- Le format Unified Diff existant présente des limites et ne reflète pas suffisamment les besoins des environnements de développement
- DiffX est parfaitement compatible avec le format existant, tout en offrant une structure pensée pour l’avenir et une extensibilité des métadonnées
- Il permet de stocker de manière structurée plusieurs informations de commit, des fichiers binaires, l’encodage des caractères et des métadonnées
- L’introduction de règles de parsing standardisées facilite l’intégration avec divers outils (patch, revue de code, etc.)
- Il peut être utilisé sans problème avec les outils et workflows existants ; seules les nouvelles fonctionnalités nécessitent une prise en charge spécifique des outils
Les développeurs et les fichiers diff
- Les développeurs logiciels consultent généralement les modifications de code sous forme de fichiers diff dans Git, Subversion, CVS, etc.
- Un fichier diff est structuré autour des insertions (
+) et suppressions (-) de texte, ainsi que des noms de fichiers, chemins, horodatages et de certaines métadonnées
- La plupart des outils et des utilisateurs emploient le format Unified Diff, qui permet de visualiser les différences de façon relativement simple
Les limites de Unified Diff
- Unified Diff ne standardise que l’identification des fichiers, l’étendue des modifications et les lignes ajoutées ou supprimées ; il ne standardise pas l’encodage, les révisions ni les métadonnées étendues
- Il est donc difficile de prendre en charge divers systèmes de gestion de sources, d’assurer un parsing fiable et d’extraire des informations riches
- Les problèmes suivants continuent de se poser :
- impossibilité de représenter plusieurs commits en une seule fois
- absence d’un format standard dédié pour les fichiers binaires
- impossibilité de connaître l’encodage des caractères, ce qui entraîne pertes d’information et confusions
- manque de standardisation pour les métadonnées arbitraires, avec des formes différentes selon les outils
Axe d’amélioration
- Le format Unified Diff existant manque de structure et de standardisation, mais il est déjà largement répandu grâce à sa souplesse dans des environnements variés
- Git Diff joue de fait le rôle de standard, mais il manque toujours une spécification officielle du format et une extensibilité véritablement générale
- Le besoin d’un nouveau format s’accentue : il s’agirait de conserver les avantages de Unified Diff tout en y ajoutant extensibilité et structure standardisée
Qu’est-ce que DiffX ?
- DiffX est un format diff extensible, parfaitement compatible avec les outils existants, qui conserve la lisibilité humaine tout en permettant de structurer métadonnées et contenu
- Exemples de syntaxe :
- il stocke de manière structurée et extensible les métadonnées et le contenu concernant les fichiers, les commits et l’ensemble du diff
- les exemples de sortie incluent une syntaxe comme
#diffx:, ainsi que des sections, des métadonnées (JSON), des chemins de fichiers et des informations de commit
Principales caractéristiques de DiffX
- Fournit des règles de parsing standardisées, permettant aux outils de lire et d’écrire les informations de manière fiable
- Formalise le stockage et la gestion des métadonnées : elles peuvent être utilisées au niveau du diff global, du commit ou du fichier
- Reste compatible avec tous les outils existants, qu’il s’agisse de parseurs, d’outils de patch ou de revue de code (les nouvelles fonctionnalités nécessitent un support, mais la compatibilité des fonctions existantes est garantie)
- Permet d’exprimer efficacement dans un seul fichier plusieurs commits, des diff binaires, des informations d’encodage texte et d’autres contenus
- Prend en charge la mutabilité : un outil peut ouvrir un diff, enregistrer et modifier les informations nécessaires, puis le sauvegarder à nouveau
Objectifs et non-objectifs de DiffX
- N’impose pas la prise en charge du format à tous les outils et ne crée pas de problèmes de compatibilité
- N’entraîne pas de dépendance à un fournisseur et ne casse pas les workflows existants
- Vise à résoudre les problèmes des fichiers diff actuels et à offrir une expérience cohérente et fiable dans les outils de développement, de revue et d’analyse
1 commentaires
Avis Hacker News
Je n’aime pas les formats hiérarchiquement complexes comme
..metaet...meta. Pour la clarté de l’expression, je pense qu’il serait plus lisible de distinguer seulement trois niveaux — le diff entier, le fichier et le chunk — en leur donnant à chacun un nom différent. Même sans méta-bloc, on pourrait distinguer la cible d’un coup d’œil, ce qui réduirait aussi les erreurs. Le fait que les métadonnées du diff entier et celles au niveau fichier utilisent les mêmes champs me semble également peu rationnel. Et je ne vois pas pourquoi il faut deux formats, JSON etkey=value; s’il y a peu d’éléments à gérer, n’utiliser qu’un seul format serait bien plus avantageux pour l’implémentation ou l’intégration avec les outils existants (grep,sedoujq, un seul suffirait). En plus, ce serait bien d’autoriser les trailing commas dans les listes, et je me demande quel impact ce format a sur le fait qu’un diff est à l’origine applicable par morceaux (par exemple, pour n’appliquer qu’une partie d’un diff, il faut recopier le préambule puis copier séparément les blocs, ce qui me paraît fastidieux). Je me demande aussi sirevisionest un attribut du fichier ou bien un checksum de commit.#<section_level><section_type>pour la simplicité du point de vue du parsing. Pour chaque méta-bloc, il suffit de vérifier verticalement le niveau, et en comptant le nombre de points on peut naturellement savoir à quel niveau de métadonnées on a affaire. Le format d’en-tête clé/valeur a été pensé pour ne contenir que des propriétés simples connues à l’avance par le parseur, tandis que les métadonnées libres vont dans un bloc meta séparé. En plus du JSON existant, cela permet d’indiquer dans l’en-tête un format afin de conserver l’extensibilité si un autre mode de sérialisation devient nécessaire avec le temps. C’est le résultat d’un effort pour trouver un équilibre entre simplicité et flexibilité. Personnellement, j’aimerais bien autoriser les trailing commas, mais à cause des problèmes de compatibilité avec le JSON de base, il est difficile d’exiger un parseur JSON5. Le diff reste fractionnable, et comme nous avons placé des informations dans des zones ignorées par Unified Diff, des outils comme GNU patch les ignorent aussi sans problème. En revanche, si l’on scinde en deux fichiers DiffX, il faut réajouter l’en-tête, ce qui peut compliquer un peu les choses. Dans certains SCM, un diff scindé peut perdre certaines métadonnées (par exemple des informations sur le commit parent), ou perdre des informations selon la cible d’application.revisiondépend du SCM ; cela peut être un ID de commit, un ID par fichier, une combinaison des deux, ou des informations supplémentaires, car les besoins sont très variés. La structure a été pensée pour répondre aux exigences diverses des différents SCM.À mes yeux, parmi les quatre critiques ci-dessous, la seule réellement raisonnable dans l’optique d’une généralisation du fichier diff est celle de la notation standard des patchs binaires. Le reste relève de données ou de problèmes de protocole internes à des systèmes de gestion de versions (SCM), utilisables seulement dans leurs clients, serveurs ou systèmes de sauvegarde respectifs. Tout le reste me semble superflu.
Impossible d’énumérer plusieurs commits dans un seul diff
Pas de standard pour les patchs binaires
Impossible d’identifier l’encodage du texte (c’est un vrai problème, mine de rien)
Absence de format standard pour les métadonnées arbitraires
Nous développons depuis 20 ans un produit de revue de code qui s’intègre à plus de 12 SCM, et nous avons rencontré d’innombrables problèmes de formats de diff et de spécificités propres à chaque SCM, bien au-delà de ce qu’on pourrait imaginer. Ce ne sont pas forcément des problèmes dont l’utilisateur final se soucie directement, mais du point de vue du développement d’outils, ce sont des pain points qu’il faut absolument résoudre. Certains SCM n’ont même pas leur propre format de diff, ou bien il leur manque beaucoup d’informations (par exemple l’impossibilité d’indiquer un fichier supprimé), ce qui empêche d’autres outils d’identifier correctement les fichiers ; c’est pourquoi nous avons jugé cette amélioration nécessaire.
C’est moins fréquent aujourd’hui, mais moi aussi j’utilise encore parfois des outils semblables à patch(1). Quand des développeurs de différentes plateformes collaborent, des problèmes liés à la casse des noms de fichiers, à l’encodage des caractères, etc., continuent bel et bien à survenir.
Si l’on intègre du JSON sous forme auto-délimitée avec une information de longueur, alors le simple fait de changer un espace dans le contenu JSON laisse le JSON valide, mais risque de casser DiffX dans son ensemble. L’ensemble donne une impression de structure lourde et brouillonne (mélange entre en-têtes maison et payload JSON, impossibilité de distinguer les différents blocs meta sans compter les points, nécessité de deux parseurs, etc.). L’idée de standardiser un diff extensible pour les métadonnées est bonne, mais cette implémentation ressemble à un tâtonnement.
Je pense que le format patch résout déjà tous ces problèmes. Lien vers l’explication de git format-patch
J’apprends aujourd’hui même l’existence de ce format et j’en prends note (je suis juste un utilisateur ordinaire d’internet, pas l’auteur).
C’est possible avec git, mais pour un produit comme Review Board, il faut s’intégrer à plusieurs VCS comme SVN, CVS, Perforce, etc., ce qui semble expliquer l’apparition de ce format. J’ai moi aussi déjà utilisé Review Board avec SVN, et quand plusieurs développeurs mélangeaient git-svn et svn, il y avait souvent des problèmes lors de l’upload de diffs pour la revue. S’il avait existé un format de diff standard pris en charge par les deux côtés, cela aurait beaucoup facilité l’usage des outils.
Personnellement, je n’ai pas vraiment l’impression que les problèmes présentés existent réellement (hors fichiers binaires).
Même si l’encodage diffère, l’algorithme de patch reste le même, donc il n’y a pas lieu de s’en soucier (les caractères n’ont même pas besoin d’être un UTF-8 valide).
Je ne vois pas de raison de vouloir mettre plusieurs commits dans un seul diff ; les séparer en plusieurs diffs est plus intuitif.
Les métadonnées ne sont-elles pas valables uniquement à l’intérieur du système ?
Pour l’encodage aussi, les données de patch doivent de toute façon être traitées comme des données binaires basées sur ASCII ; comme on peut modifier des fichiers à encodage mixte, figer l’encodage n’a pas beaucoup de sens.
Je ne vois vraiment pas cela comme un problème ; il vaudrait mieux écouter l’expérience réelle des utilisateurs qui manipulent beaucoup les diffs, et éviter d’overengineerer un format qui fonctionne déjà bien.
Le problème des données binaires, en revanche, existe clairement.
En général, on ne rencontre réellement ces problèmes que lorsqu’on développe soi-même des outils ou qu’on doit interfacer avec un SCM donné.
Le document entier me paraît difficile à lire. Pour moi, un « diff » désigne la différence entre deux éléments (fichiers, répertoires, etc.), mais le diff évoqué dans l’article est ce que j’appelle un « patch ». Le sujet discuté ici n’est pas le diff, mais la gestion des métadonnées d’un patch. Si l’on standardisait les métadonnées dans un format obligatoire comme JSON, cela irait, mais ici la structure auto-descriptive et length-delimited donne l’impression de masquer le problème. La standardisation en elle-même est une bonne chose, mais j’ai l’impression qu’il faut une solution plus claire et mieux structurée. Je trouve d’ailleurs intéressant que, de mon point de vue, le style git diff soit plus proche du standard de fait.
Je me demande quel problème ce format cherche à résoudre. On dit que les formats patch/diff actuels ne sont pas assez bons, mais pour qui veut-on les améliorer ? Est-ce parce que les plaintes augmentent dans la communauté GNU Patch ? Il faudrait être plus précis sur les raisons. Je reste avec la question suivante : pourquoi a-t-on réellement besoin d’un meilleur format de patch ?
J’ai rédigé à part un texte trop long pour être mis ici, mais en résumé il s’agit de réflexions destinées aux développeurs qui créent directement des SCM ou des outils qui s’y intègrent. Pour l’utilisateur ordinaire, il n’y a pas besoin de s’en préoccuper. Les formats de diff diffèrent entièrement d’un SCM à l’autre : certains sont très bien conçus, d’autres sont gravement insuffisants, et parfois il n’existe même pas de format. Pour un produit comme Review Board, qui doit couvrir plusieurs SCM, un tel standard d’intégration est réellement nécessaire en pratique. C’est une tentative d’amélioration qui intègre les retours de collaboration avec des fournisseurs de SCM.
Cela ressemble à un format surtout utilisé autour de Review Board ; comme ce produit prend en charge divers VCS et que le diff y est central dans la revue de code, cela semble expliquer son adoption.
La représentation de diff la plus générale et la plus claire consiste à inclure tout simplement les deux fichiers. Aujourd’hui, la taille des données n’est plus vraiment un problème, donc au lieu de
diff a b | patch c, on pourrait avoir quelque chose commeapply a b c, peu importe la représentation interne. Les diffs sont difficiles à lire pour les humains, et une vue colorée côte à côte est bien meilleure ; il est donc plus intuitif de récupérer directement les deux fichiers et de les traiter ainsi. Je ne pense pas qu’il soit nécessaire de transmettre un diff non standardisé.Un diff produit à partir de deux fichiers n’est pas unique, et plusieurs variantes peuvent exister selon l’objectif recherché. Avec un format de diff, on peut séparer la logique de génération et la logique d’application du diff, ce qui réduit un problème en n*m à un problème en n+m.
Pour des mises à jour logicielles, devoir retélécharger les 130 Go complets à chaque fois serait pénible ; comme des fichiers presque identiques se compressent facilement, transmettre seulement la différence entre deux versions peut aussi avoir un intérêt concret. On peut imaginer des méthodes encore plus efficaces, comme n’envoyer que le hash du fichier original et transmettre le corps compressé séparément.
Transmettre et gérer deux paires de fichiers sans conteneur dédié est difficile, et si l’on veut échanger plusieurs changements (modification de 10 fichiers, suppression, ajout, etc.) par email par exemple, on régresse plutôt vers la complexité de l’époque pré-VCS, avec des structures comme tar/zip.
Même s’il est plus intuitif d’envoyer le fichier complet plutôt qu’un diff, dans les usages et environnements réels le diff garde une grande importance. Aujourd’hui, quand on génère des résultats comme des modifications de code avec des LLM, demander un diff permet d’économiser énormément de tokens et de réduire la latence des réponses d’un facteur 5 à 10, avec un gain d’efficacité considérable. Envoyer les deux fichiers gaspille des tokens et coûte plus cher. Pour appliquer rapidement sur un sandbox de code ou une machine distante, le diff offre un vrai avantage d’optimisation. Si l’on a le fichier A, le fichier A2 et le diff AxA2, il est aussi facile de reconstruire A2 et d’optimiser le stockage du dépôt. Et en cas de conflit à la fusion, on peut n’intervenir manuellement qu’à ce moment-là. En résumé, le diff est excellent.
Je trouve toujours frustrant que les outils de diff dépendent autant du découpage par retours à la ligne. Quand une ligne est très longue (par exemple du JSON, de longs tableaux, etc.), la revue devient difficile.
Je suis d’accord. Il y a probablement matière à explorer de meilleures façons de représenter les diffs pour des données structurées (par exemple des AST diff). Ce format (DiffX) met l’accent sur le fait d’être une extension du format Unified Diff existant ; si des formats plus spécifiques, comme les AST, deviennent largement utilisés, il a aussi été conçu pour pouvoir les embarquer et les prendre en charge facilement.
La forme la plus courante est un compromis entre lisibilité humaine et parsing par les outils, ce qui lui donne une structure un peu bâtarde. Cette fois, on a visiblement essayé de résoudre une partie du problème en étendant les métadonnées, mais une très bonne solution consisterait sans doute à définir un nouveau format, pas en texte brut, à la fois lisible et parseable. Concevoir un meilleur algorithme de diff pour les longues lignes ou les données structurées est un défi difficile, mais certainement solvable.
git prend aussi en charge un word diff plus fin que le line diff, et le séparateur par défaut est l’espace.
Je m’interroge sur le fait que JSON soit le seul format de métadonnées pris en charge. Pour un standard de métadonnées conçu pour un usage général, JSON est au contraire assez complexe.