Git utilise une compression delta pour stocker des objets similaires les uns aux autres.
Cet algorithme est-il normalisé et utilisé également dans d'autres outils? Existe-t-il une documentation décrivant le format? Est-il compatible avec xdelta/VCDIFF/RFC 3284?
Je pense que l'algorithme diff utilisé pour fichiers de pack était lié à l'un des encodage delta là-bas: initialement (2005) xdelta , puis - libXDiff .
Mais ensuite, comme détaillé ci-dessous, il est passé à une implémentation personnalisée.
Quoi qu'il en soit, comme mentionné ici :
Git fait la deltification seulement dans les fichiers pack.
Mais lorsque vous poussez via SSH, git générerait un fichier de pack avec des validations que l'autre côté n'a pas, et ces packs sont des packs légers, donc ils ont aussi des deltas ... mais le côté distant ajoute ensuite des bases à ces paquets minces les rendant autonomes.
(note: créer de nombreux fichiers pack, ou récupérer des informations dans un fichier pack énorme est coûteux , et expliquez pourquoi git ne gère pas bien les fichiers volumineux ou le repo énorme.
Voir plus sur " git avec de gros fichiers ")
Ce fil nous rappelle également:
En fait, les fichiers de paquets et la deltification ( LibXDiff, pas xdelta ) étaient, d'après ce que je me souviens et comprends, à l'origine à cause du réseau la bande passante (qui est beaucoup plus coûteuse que l'espace disque) et les performances d'E/S en utilisant à la place un fichier mmappé unique d'un très grand nombre d'objets en vrac.
LibXDiff est mentionné dans ce thread 2008 .
Cependant, depuis lors, l'algo a évolué, probablement dans un personnalisé, comme cela le fil de 2011 illustre , et comme en-tête de diff-delta.c
souligne:
Donc, à proprement parler, le code actuel dans Git ne ressemble en rien au code libxdiff.
Cependant, l'algorithme de base derrière les deux implémentations est le même .
L'étude de la version de libxdiff est probablement plus facile afin de comprendre comment cela fonctionne.
/*
* diff-delta.c: generate a delta between two buffers
*
* This code was greatly inspired by parts of LibXDiff from Davide Libenzi
* http://www.xmailserver.org/xdiff-lib.html
*
* Rewritten for GIT by Nicolas Pitre <[email protected]>, (C) 2005-2007
*/
En savoir plus sur les packfiles the Git Book :
Git 2.18 ajoute à la description delta dans cette nouvelle section documentation , qui indique maintenant (Q2 2018):
Types d'objets
Les types d'objets valides sont:
OBJ_COMMIT
(1)OBJ_TREE
(2)OBJ_BLOB
(3)OBJ_TAG
(4)OBJ_OFS_DELTA
(6)OBJ_REF_DELTA
(7)Le type 5 est réservé pour une expansion future. Le type 0 n'est pas valide.
Représentation délicate
Conceptuellement, il n'y a que quatre types d'objets: commit, tree, tag et blob.
Cependant, pour économiser de l'espace, un objet peut être stocké en tant que "delta" d'un autre objet "de base".
Ces représentations reçoivent de nouveaux types de s-delta et ref-delta, qui ne sont valides que dans un fichier pack.Tous les deux
ofs-delta
etref-delta
stocke le "delta" à appliquer à un autre objet (appelé "objet de base") pour reconstruire l'objet.
La différence entre eux est,
- ref-delta code directement le nom de l'objet de base sur 20 octets.
- Si l'objet de base se trouve dans le même pack, ofs-delta code à la place le décalage de l'objet de base dans le pack.
L'objet de base peut également être supprimé s'il se trouve dans le même pack.
Ref-delta peut également faire référence à un objet en dehors du pack (c'est-à-dire le soi-disant "pack mince") . Cependant, lorsqu'il est stocké sur disque, le pack doit être autonome pour éviter une dépendance cyclique.Les données delta sont une séquence d'instructions pour reconstruire un objet à partir de l'objet de base.
Si l'objet de base est supprimé, il doit d'abord être converti en forme canonique. Chaque instruction ajoute de plus en plus de données à l'objet cible jusqu'à ce qu'elle soit terminée.
Jusqu'à présent, deux instructions sont prises en charge:
- un pour copier une plage d'octets de l'objet source et
- une pour insérer de nouvelles données intégrées dans l'instruction elle-même.
Chaque instruction a une longueur variable. Le type d'instruction est déterminé par le septième bit du premier octet. Les diagrammes suivants suivent la convention de la RFC 1951 (Déflatez le format de données compressé).
L'encodage Git delta est basé sur la copie/l'insertion.
Cela signifie que le fichier dérivé est codé comme une séquence d'opcodes qui peut représenter des instructions de copie (par exemple: copier du fichier de base y octets à partir de l'offset x dans le tampon cible) ou insérer des instructions (par exemple: insérer les x octets suivants dans le tampon cible).
Comme exemple très simple (tiré de l'article "File System Support for Delta Compression"), considérons que nous voulons créer un tampon delta pour transformer le texte "proxy cache" en "cache proxy". Les instructions résultantes doivent être:
Ce qui s'est traduit par l'encodage de git devient:
(les octets 1-3 représentent la première instruction)
(les octets 4-6 représentent la deuxième instruction)
(les octets 7 à 8 représentent la dernière instruction)
Notez que dans la dernière instruction de copie ne spécifie pas un décalage qui signifie décalage 0. D'autres bits dans l'opcode copie peuvent également être définis lorsque des décalages/longueurs plus importants sont nécessaires.
Le résultat du tampon delta a dans cet exemple 8 octets, ce qui n'est pas vraiment une compression puisque le tampon cible a 12 octets, mais lorsque ce codage appliqué à de gros fichiers texte peut faire une énorme différence.
J'ai récemment poussé une bibliothèque node.js vers github qui implémente les deux fonctions diff/patch en utilisant l'encodage git delta. Le code devrait être plus lisible et commenté que celui de la source git, qui est fortement optimisé.
J'ai également écrit quelques tests qui expliquent les opcodes de sortie utilisés dans chaque exemple avec un format similaire au précédent.
Cet algorithme est-il normalisé et utilisé également dans d'autres outils?
Le format du pack fait partie d'une API publique: les protocoles de transfert utilisés pour les opérations Push et fetch l'utilisent pour envoyer moins de données sur le réseau.
Ils sont implémentés dans au moins deux autres implémentations majeures de Git en plus de la référence: JGit et libgit2 .
Par conséquent, il est très peu probable qu'il y ait des changements incompatibles en arrière au format, et peut être considéré comme "normalisé" dans ce sens.
Ce fichier étonnant de la documentation décrit l'heuristique utilisée dans l'algorithme du pack comme un commentaire amusant sur un e-mail de Linus: https://github.com/git/git/blob/v2.9.1/Documentation/technical/ pack-heuristics.txt