
et apporte un nouveau backend de stratégie de fusion
L’équipe du projet open source annonce la sortie de Git 2.33 avec des fonctionnalités et des corrections de bogues provenant de plus de 74 contributeurs. « La version 2.33 ne comporte pas beaucoup de changements et de nouvelles fonctionnalités pour l'utilisateur final », à part des corrections et des améliorations internes. Cependant, mais il y a un changement majeur, décrit comme un « nouveau backend de stratégie de fusion ». La stratégie en question est merge-ort, où "ort" signifie Ostensibly Recursive's Twin, selon son créateur Elijah Newren.
Rappelons qu’une stratégie de fusion est le mécanisme utilisé pour combiner le code de plusieurs versions d'une même base de code. La fusion est une caractéristique essentielle des systèmes de contrôle de version distribués, car elle évite de devoir verrouiller une version principale lorsqu'une copie extraite est en cours de modification. Les mécanismes de fusion fonctionnent en comparant le contenu d'un fichier avec le contenu de son ancêtre, afin d'identifier les sections modifiées, puis en comparant les sections modifiées d'un fichier avec celles d'un autre. Voici, ci-dessous, un aperçu des fonctionnalités et des changements les plus intéressants :
Repiquage géométrique
Dans un précédent billet de blog, l’équipe du projet open source Git a expliqué comment GitHub utilisait un nouveau mode de git repack pour mettre en œuvre les tâches de maintenance des dépôts. Dans Git 2.32, beaucoup de ces correctifs ont été publiés dans le projet open-source Git. Historiquement, git repack faisait l'une des deux choses suivantes : soit il repackait tous les objets libres dans un nouveau paquet (en supprimant éventuellement les copies libres de chacun de ces objets), soit il repackait tous les paquets ensemble dans un seul nouveau paquet (en supprimant éventuellement les paquets redondants).
D'une manière générale, Git est plus performant lorsqu'il y a moins de paquets, car de nombreuses opérations évoluent avec le nombre de paquets dans un dépôt. C'est donc souvent une bonne idée de tout regrouper en un seul pack. Mais historiquement parlant, les dépôts très fréquentés exigent souvent que tout leur contenu soit regroupé dans un seul et énorme pack. Cela s'explique par le fait que les bitmaps d'accessibilité, qui constituent une optimisation critique pour les performances de Git côté serveur, ne peuvent décrire que les objets d'un seul pack. Ainsi, si vous souhaitez utiliser des bitmaps pour couvrir efficacement de nombreux objets dans votre référentiel, ces objets doivent être stockés ensemble dans le même pack.
L’équipe du projet open source Git travaille à la suppression de cette limitation (vous pouvez en savoir plus sur la façon dont nous avons procédé), mais une étape importante est la mise en œuvre d'un nouveau schéma de repacking qui permet de concilier un nombre relativement faible de packs et le regroupement des objets récemment ajoutés (en d'autres termes, l'approximation des nouveaux objets ajoutés depuis le repack précédent). Pour ce faire, Git a appris une nouvelle stratégie de reconditionnement « géométrique ». L'idée est de déterminer un ensemble (assez petit) de paquets qui pourraient être combinés ensemble de sorte que les paquets restants forment une progression géométrique basée sur la taille des objets. En d'autres termes, si le plus petit paquet contient N objets, le plus grand paquet suivant doit contenir au moins 2N objets, et ainsi de suite, en doublant (ou en augmentant d'une constante arbitraire) à chaque étape. Voici, un exemple pour mieux comprendre comment cela fonctionne. Tout d'abord, Git ordonne tous les paquets (représentés ci-dessous par un carré vert ou rouge) par ordre croissant en fonction du nombre d'objets qu'ils contiennent (les chiffres à l'intérieur de chaque carré). Ensuite, les paquets adjacents sont comparés (en commençant par les plus grands paquets et en allant vers les plus petits) pour s'assurer qu'il existe une progression géométrique :
Ici, la progression est interrompue entre le deuxième et le troisième pack. Cela est dû au fait que ces deux packs contiennent chacun le même nombre d'objets (dans ce cas, un seul). Git décide alors qu'au moins les deux premiers packs seront contenus dans un nouveau pack destiné à rétablir la progression géométrique. Il doit ensuite déterminer combien de paquets plus grands doivent également être roulés afin de maintenir la progression :
En combinant les deux premiers paquets, on obtiendrait deux objets, qui seraient encore trop grands pour s'insérer dans la progression (puisque le plus grand paquet suivant ne contient qu'un seul objet). Mais il suffit de rouler les quatre premiers paquets, car le cinquième paquet contient plus de deux fois plus d'objets que les quatre premiers paquets réunis :
Vous pouvez essayer vous-même en comparant les tailles des paquets sur un référentiel sur votre ordinateur portable avant et après le repacking géométrique avec le script suivant :
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | $ packsizes() { find .git/objects/pack -type f -name '*.pack' | while read pack; do printf "%7d %s\n" \ "$(git show-index < ${pack%.pack}.idx | wc -l)" "$pack" done | sort -rn } $ packsizes # before $ git repack --geometric=2 -d $ packsizes # after |
merge-ort : une nouvelle stratégie de fusion
Lorsque Git effectue une fusion entre deux branches, il utilise l'une des nombreuses "stratégies" pour résoudre les changements. La stratégie originale est simplement appelée resolve et effectue une fusion standard à trois voies. Mais cette stratégie par défaut a été remplacée au début de l'histoire de Git par merge-recursive, qui présentait deux avantages importants :
- dans le cas de fusions "entrecroisées" (où il n'y a pas un seul point commun de divergence entre deux branches), la stratégie de fusion est la suivante point commun de divergence entre deux branches), la stratégie consiste à effectuer une série de fusions (de manière récursive, d'où son nom) pour chaque base possible. Cela permet de résoudre les cas pour lesquels la stratégie resolve produirait un conflit ;
- elle détecte les renommages de fichiers le long de chaque branche. Un fichier qui a été modifié d'un côté, mais renommé de l'autre, verra ses modifications seront appliquées à la destination renommée (plutôt que de produire un conflit très déroutant).
merge-recursive a bien servi comme valeur par défaut de Git pendant de nombreuses années, mais il avait quelques défauts. Il a été écrit à l'origine comme un script Python externe qui utilisait les commandes de plomberie de Git pour examiner les données. Il a ensuite été réécrit en C, ce qui a permis de gagner en rapidité. Mais l'organisation de son code et ses structures de données reflétaient toujours ses origines : il fonctionnait toujours principalement sur l'" index " de Git (la zone sur le disque où les modifications sont collectées pour les nouveaux commits) et l'arbre de travail. Cela a donné lieu à plusieurs bogues au fil des ans autour de cas de coin délicats (par exemple, celui-ci ou certains de ceux-ci).
Les origines de merge-recursive ont également rendu plus difficiles l'optimisation et l'extension du code. Le temps de fusion n'est pas un goulot d'étranglement dans la plupart des flux de travail, mais il y a certainement des cas importants (en particulier impliquant des renommages) où merge-recursive pourrait être très lent. De même, le backend de fusion est utilisé pour de nombreuses opérations qui combinent deux ensembles de modifications. Une opération de cherry-pick peut effectuer une série de fusions, et l'accélération de celles-ci a un effet notable.
La stratégie de fusion-ort est une réécriture from-scratch avec les mêmes concepts (récursion et détection des renommages), mais en résolvant de nombreux problèmes de correction et de performance de longue date. Le résultat est beaucoup plus rapide. Pour une fusion (mais une fusion importante et délicate contenant de nombreux renommages), merge-ort gagne plus de 500 fois en vitesse. Pour une série de fusions similaires dans une opération de cherry-pick, le gain de vitesse est de plus de 9000x (parce que merge-ort est capable de mettre en cache et de réutiliser certains calculs communs aux fusions). Ces cas ont été sélectionnés comme particulièrement mauvais pour l'algorithme merge-recursive, mais dans nos tests de cas typiques, nous trouvons que merge-ort est toujours un peu plus rapide que merge-recursive. Le véritable avantage est que merge-ort est...
La fin de cet article est réservée aux abonnés. Soutenez le Club Developpez.com en prenant un abonnement pour que nous puissions continuer à vous proposer des publications.