Blog Open source Nouveautés de Git 2.49.0
Mise à jour : April 10, 2025
Lecture : 15 min

Nouveautés de Git 2.49.0

Découvrez la dernière version de Git, y compris les performances améliorées avec l'intégration de zlib-ng et la commande git-backfill(1).

git3-cover

Le projet Git a récemment publié sa version Git 2.49.0. Jetons un coup d'œil aux points forts de cette nouvelle version, comprenant des contributions de l'équipe Git de GitLab et de la communauté Git au sens large.

Commande git-backfill(1) associée à la nouvelle API path-walk

Vous avez désormais la possibilité, lorsque vous exécutez la commande git-clone(1) sur un dépôt Git, de transmettre l'option --filter afin de créer un clone partiel. Dans ce type de clonage, le serveur n'envoie qu'un sous-ensemble des objets accessibles, en fonction du filtre d'objets spécifié. Par exemple, la création d'un clone avec --filter=blob:none signifie que Git ne récupérera pas les blobs (ou contenus de fichiers) auprès du serveur et créera ainsi un clone blobless.

Les clones blobless sont des clones très légers, sans fichiers, qui contiennent l'ensemble des commits et des arbres accessibles, mais aucun blob. Lorsque vous effectuez une opération comme git-checkout(1), Git télécharge les blobs manquants pour exécuter la commande. Cependant, certaines opérations comme git-blame(1) peuvent entraîner un téléchargement séquentiel des objets, ce qui ralentit considérablement leur exécution. Cette dégradation des performances se produit parce que la commande git-blame(1) doit parcourir l'historique des commits pour identifier les blobs spécifiques requis, puis les demander un par un au serveur.

Pour remédier à cela, Git 2.49.0 introduit une nouvelle sous-commande : git-backfill(1). Elle permet de télécharger les blobs manquants dans un clone partiel blobless.

En arrière-plan, la commande git-backfill(1) tire parti de la nouvelle API path-walk, qui est différente de la méthode traditionnelle d'itération de Git sur les commits. Plutôt que d'itérer sur les commits en les parcourant un par un et de consulter de façon récursive les arbres et les blobs associés à chaque commit, l'API path-walk procède par chemin d'accès. Pour chaque chemin de fichier, elle accumule la liste des objets d'arbre associés à une pile, et cette dernière est ensuite traitée en profondeur. Ainsi, plutôt que de traiter chaque objet du commit 1 avant de passer au commit 2, elle traite toutes les versions du fichier A à travers tous les commits avant de passer au fichier B. Cette approche améliore considérablement les performances dans les scénarios où le regroupement par chemin est essentiel.

En guise d'exemple, nous allons créer un clone blobless du dépôt gitlab-org/git :

$ git clone --filter=blob:none --bare --no-tags [email protected]:gitlab-org/git.git
Cloning into bare repository 'git.git'...
remote: Enumerating objects: 245904, done.
remote: Counting objects: 100% (1736/1736), done.
remote: Compressing objects: 100% (276/276), done.
remote: Total 245904 (delta 1591), reused 1547 (delta 1459), pack-reused 244168 (from 1)
Receiving objects: 100% (245904/245904), 59.35 MiB | 15.96 MiB/s, done.
Resolving deltas: 100% (161482/161482), done.

Dans l'exemple ci-dessus, nous utilisons l'option --bare pour nous assurer que Git n'a pas besoin de télécharger de blobs pour effectuer le checkout d'une branche initiale. Nous pouvons ensuite vérifier que ce clone ne contient effectivement aucun blob :

$ git cat-file --batch-all-objects --batch-check='%(objecttype)' | sort | uniq -c
  83977 commit
 161927 tree

Si vous souhaitez afficher le contenu d'un fichier dans le dépôt, Git doit le télécharger :

$ git cat-file -p HEAD:README.md
remote: Enumerating objects: 1, done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 1 (from 1)
Receiving objects: 100% (1/1), 1.64 KiB | 1.64 MiB/s, done.

[![Build status](https://github.com/git/git/workflows/CI/badge.svg)](https://github.com/git/git/actions?query=branch%3Amaster+event%3Apush)

Git - fast, scalable, distributed revision control system
=========================================================

Git is a fast, scalable, distributed revision control system with an
unusually rich command set that provides both high-level operations
and full access to internals.

[snip]

Comme vous pouvez le voir ci-dessus, Git interroge d'abord le dépôt distant pour télécharger le blob avant de pouvoir l'afficher.

Mais si vous souhaitez effectuer une opération git-blame(1) sur ce fichier, Git devra télécharger de nombreux autres fichiers :

$ git blame HEAD README.md
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (1/1), 1.64 KiB | 1.64 MiB/s, done.
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (1/1), 1.64 KiB | 1.64 MiB/s, done.
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (1/1), 1.64 KiB | 1.64 MiB/s, done.
remote: Enumerating objects: 1, done.

[snip]

df7375d772 README.md (Ævar Arnfjörð Bjarmason 2021-11-23 17:29:09 +0100  1) [![Build status](https://github.com/git/git/workflows/CI/badge.svg)](https://github.com/git/git/actions?query=branch%3Amaster+event%3Apush)
5f7864663b README.md (Johannes Schindelin 	2019-01-29 06:19:32 -0800  2)
28513c4f56 README.md (Matthieu Moy        	2016-02-25 09:37:29 +0100  3) Git - fast, scalable, distributed revision control system
28513c4f56 README.md (Matthieu Moy        	2016-02-25 09:37:29 +0100  4) =========================================================
556b6600b2 README	(Nicolas Pitre       	2007-01-17 13:04:39 -0500  5)
556b6600b2 README	(Nicolas Pitre       	2007-01-17 13:04:39 -0500  6) Git is a fast, scalable, distributed revision control system with an
556b6600b2 README	(Nicolas Pitre       	2007-01-17 13:04:39 -0500  7) unusually rich command set that provides both high-level operations
556b6600b2 README	(Nicolas Pitre       	2007-01-17 13:04:39 -0500  8) and full access to internals.
556b6600b2 README	(Nicolas Pitre       	2007-01-17 13:04:39 -0500  9)

[snip]

Nous avons tronqué la sortie, mais comme vous pouvez le constater, Git interroge le serveur séparément pour chaque révision de ce fichier. Ce processus est loin d'être optimal. Avec la commande git-backfill(1), nous pouvons demander à Git de télécharger tous les blobs en une seule fois :

$ git backfill
remote: Enumerating objects: 50711, done.
remote: Counting objects: 100% (15438/15438), done.
remote: Compressing objects: 100% (708/708), done.
remote: Total 50711 (delta 15154), reused 14730 (delta 14730), pack-reused 35273 (from 1)
Receiving objects: 100% (50711/50711), 11.62 MiB | 12.28 MiB/s, done.
Resolving deltas: 100% (49154/49154), done.
remote: Enumerating objects: 50017, done.
remote: Counting objects: 100% (10826/10826), done.
remote: Compressing objects: 100% (634/634), done.
remote: Total 50017 (delta 10580), reused 10192 (delta 10192), pack-reused 39191 (from 1)
Receiving objects: 100% (50017/50017), 12.17 MiB | 12.33 MiB/s, done.
Resolving deltas: 100% (48301/48301), done.
remote: Enumerating objects: 47303, done.
remote: Counting objects: 100% (7311/7311), done.
remote: Compressing objects: 100% (618/618), done.
remote: Total 47303 (delta 7021), reused 6693 (delta 6693), pack-reused 39992 (from 1)
Receiving objects: 100% (47303/47303), 40.84 MiB | 15.26 MiB/s, done.
Resolving deltas: 100% (43788/43788), done.

Cette commande permet de télécharger l'ensemble des blobs, transformant ainsi le clone blobless en un clone complet :

$ git cat-file --batch-all-objects --batch-check='%(objecttype)' | sort | uniq -c
 148031 blob
  83977 commit
 161927 tree

Ce projet a été mené par Derrick Stolee et a été fusionné via le commit e565f37553.

Intégration de zlib-ng

Tous les objets contenus dans le dossier .git/ sont compressés par Git à l'aide de zlib, la bibliothèque de référence conforme à la spécification RFC 1950 : ZLIB Compressed Data Format. Créée en 1995, zlib bénéficie d'une longue histoire et d'une portabilité incroyable. Elle prend même en charge de nombreux systèmes antérieurs à Internet. En raison de sa compatibilité étendue avec une grande diversité d'architectures et de compilateurs, elle s'accompagne de certaines limites techniques.

Pour pallier ces contraintes, une bifurcation nommée zlib-ng a été créée. zlib-ng est une version optimisée pour les systèmes modernes. Cette bifurcation abandonne la prise en charge des systèmes hérités au profit d'optimisations Intel, de certaines optimisations Cloudflare et de quelques autres correctifs plus ciblés.

La bibliothèque zlib-ng elle-même inclut une couche de compatibilité avec zlib, permettant d'utiliser zlib-ng en remplacement immédiat de zlib. Toutefois, cette couche de compatibilité n'est pas encore disponible sur toutes les distributions Linux. Dans Git 2.49.0 :

  • Une couche de compatibilité a été intégrée directement au projet Git.
  • Des options de compilation ont été ajoutées à la fois au fichier Makefile et au fichier de compilation Meson.

Grâce à ces ajouts, vous tirez plus facilement parti des gains de performances procurés par zlib-ng.

Lors de benchmarks en local, nous avons constaté une accélération d'environ 25 % en utilisant zlib-ng au lieu de zlib. Nous sommes d'ailleurs en train de déployer progressivement ces améliorations sur GitLab.com.

Si vous souhaitez bénéficier des améliorations apportées par zlib-ng, vérifiez d'abord si Git sur votre machine l'utilise déjà en exécutant la commande git version --build-options :

$ git version --build-options
git version 2.47.1
cpu: x86_64
no commit associated with this build
sizeof-long: 8
sizeof-size_t: 8
shell-path: /bin/sh
libcurl: 8.6.0
OpenSSL: OpenSSL 3.2.2 4 Jun 2024
zlib: 1.3.1.zlib-ng

Si la dernière ligne contient zlib-ng, votre instance Git est déjà créée à l'aide de la variante optimisée de zlib. Sinon, vous pouvez :

  • Soit demander au chargé de maintenance du paquet Git que vous utilisez d'inclure la prise en charge de zlib-ng.
  • Soit compiler Git vous-même à partir de la source.

Ces améliorations ont été introduites par Patrick Steinhardt.

Poursuite de l'optimisation de Meson

Dans notre article sur la version 2.48.0 de Git, nous avons évoqué l'introduction du système de compilation Meson. Meson est un outil d'automatisation de compilation utilisé par le projet Git qui, à terme, pourrait remplacer Autoconf, CMake et peut-être même Make.

Au cours de ce cycle de sortie de nouvelles versions, nous avons poursuivi notre optimisation de l'utilisation de Meson, en ajoutant diverses fonctionnalités manquantes et des correctifs de stabilisation :

L'ensemble de ces contributions a été mené par Patrick Steinhardt.

Retrait définitif des sous-répertoires .git/branches/ et .git/remotes/

Vous connaissez probablement l'existence du répertoire .git et de son contenu. Mais avez-vous déjà entendu parler des sous-répertoires .git/branches/ et .git/remotes/ ? Comme vous le savez peut-être, les références aux branches sont stockées dans .git/refs/heads/, ce n'est donc pas à cela que sert .git/branches/, et qu'en est-il de .git/remotes/ ?

En 2005, le sous-répertoire .git/branches/ a été introduit afin de stocker le nom abrégé de dépôts distants, et quelques mois plus tard, ces informations ont été déplacées vers .git/remotes/. Puis, en 2006, à l'aide de la commande git-config(1), il a été possible de gérer les dépôts distants de manière centralisée, ce qui est devenu la méthode standard de configurer des dépôts distants. En 2011, les sous-répertoires .git/branches/ et .git/remotes/ ont été documentés comme étant « hérités » et obsolètes. Ils ne sont plus utilisés dans les dépôts modernes.

Enfin en 2024, la documentation BreakingChanges a été créée pour répertorier les changements cassants de la prochaine version majeure de Git (v3.0). Bien que cette nouvelle version ne soit pas encore planifiée dans un avenir proche, cette documentation permet de suivre les modifications significatives à venir.

Dans le commit 8ccc75c245, l'utilisation des sous-répertoires .git/branches/ et .git/remotes/ a été ajoutée à cette liste, en les marquant officiellement comme obsolètes et voués à être supprimés dans Git 3.0.

Merci à Patrick Steinhardt d'avoir formalisé ce retrait définitif.

Ajout de liaisons en Rust pour libgit

Lors de la compilation de Git, une bibliothèque interne nommée libgit.a est créée. Elle contient certaines des fonctionnalités de base de Git.

Bien que cette bibliothèque (comme la majeure partie de Git) soit écrite en C, la version Git 2.49.0 introduit des liaisons pour que certaines de ces fonctions deviennent disponibles en langage Rust. Pour ce faire, deux nouveaux paquets Cargo ont été créés : libgit-sys et libgit-rs. Ils se trouvent dans le sous-répertoire contrib/ de l'arbre source de Git.

C'est une pratique assez courante de diviser une bibliothèque en deux packages lorsqu'une interface de fonction étrangère est utilisée. Le paquet libgit-sys fournit l'interface directe et minimaliste vers les fonctions en C et effectue le lien avec la bibliothèque native libgit.a. Le paquet libgit-rs fournit une interface de haut niveau vers les fonctions de libgit-sys, assez similaire à Rust.

Jusqu'à présent, les fonctionnalités de ces packages Rust sont très limitées : elles fournissent uniquement une interface d'interaction avec la commande git-config(1).

Cette initiative a été menée par Josh Steadmon et a été fusionnée via le commit a4af0b6288.

Nouvel algorithme de hachage de noms

La base de données d'objets Git dans .git/ stocke la plupart de ses données sous forme de fichiers d'empaquetage. Ces derniers sont également utilisés pour transférer des objets entre le serveur et le client Git par le biais du réseau.

Pour en savoir plus sur le format de ces fichiers, consultez la documentation gitformat-pack(5). D'autre part, les fichiers d'empaquetage utilisent une technique de compression qui a son importance, appelée la compression delta. Avec ce type de compression, tous les objets ne sont pas stockés dans leur intégralité : certains sont enregistrés en tant que delta d'une autre base. Ainsi, au lieu d'enregistrer le contenu complet des objets, ce sont les modifications par rapport à un autre objet de référence qui sont stockées.

Sans détailler la façon dont ces deltas sont calculés ou stockés, vous vous doutez bien qu'il est essentiel de regrouper les fichiers très similaires pour optimiser la compression. Jusqu'à la version v2.48.0, Git examinait les 16 derniers caractères du nom du chemin d'accès au fichier pour déterminer si les blobs semblaient similaires, à l'aide d'un algorithme nommé « version 1 ».

Dans Git 2.49.0, l'algorithme « version 2 » a été introduit. Il s'agit d'une itération de l'algorithme version 1, mais modifié, de sorte que l'impact du répertoire parent dans le calcul est réduit. Vous pouvez spécifier la version de l'algorithme de hachage de noms à utiliser à l'aide de l'option --name-hash-version de la commande git-repack(1).

Derrick Stolee, qui a mené ce projet, a effectué une comparaison de la taille des fichiers d'empaquetage après exécution de git repack -adf --name-hash-version=<n> :

Dépôt Taille avec la version 1 Taille avec la version 2
fluentui 440 Mo 161 Mo
Dépôt B 6 248 Mo 856 Mo
Dépôt C 37 278 Mo 6 921 Mo
Dépôt D 131 204 Mo 7 463 Mo

Pour en savoir plus, consultez l'ensemble de correctifs, qui a été fusionné dans le commit aae91a86fb.

Capacité de promisor remote

Il est de notoriété publique que Git ne sait pas très bien gérer les fichiers volumineux. Des solutions comme Git LFS existent pour résoudre ce problème, mais celles-ci comportent malgré tout certaines lacunes. En voici quelques exemples :

  • Avec Git LFS, l'utilisateur doit configurer les fichiers à placer dans LFS. Le serveur n'a aucun contrôle sur ce choix et doit donc servir tous les fichiers.
  • Chaque fois qu'un fichier est validé dans le dépôt, il est impossible de le récupérer sans réécrire l'historique. C'est un vrai problème, surtout pour les fichiers volumineux qui sont bloqués pour toujours.
  • Les utilisateurs ne peuvent pas changer d'avis une fois le fichier sélectionné et placé dans Git LFS.
  • Configurer, apprendre et utiliser de manière optimale un outil comme Git LFS est fastidieux.

Depuis un certain temps, Git a adopté le concept de « promisor remotes ». Cette fonctionnalité pouvant être utilisée pour gérer des fichiers volumineux a été améliorée dans Git 2.49.0.

L'idée de la capacité « promisor remote » est relativement simple : au lieu d'envoyer lui-même tous les objets, un serveur Git peut indiquer au client Git de télécharger ces objets à partir de XYZ. XYZ représente alors un « promisor remote ».

Git 2.49.0 permet désormais au serveur de transmettre les informations du « promisor remote » au client. Cette amélioration est une extension du protocole gitprotocol-v2. Pendant l'échange de données, le serveur peut ainsi envoyer au client les noms et les URL des « promisor remotes » dont il a connaissance.

Jusqu'à présent, le client n'utilise pas les informations du « promisor remote » qu'il reçoit du serveur lors d'un clonage, de sorte que tous les objets sont toujours transmis depuis le dépôt distant à partir duquel le clonage a été initié. Nous envisageons de poursuivre l'optimisation de cette fonctionnalité pour faire en sorte de permettre au client d'utiliser les informations du « promisor remote » du serveur, ainsi que pour simplifier son utilisation.

Cet ensemble de correctifs a été soumis par Christian Couder et fusionné via le commit 2c6fd30198.

Clone léger utilisant --revision

Une nouvelle option --revision a été ajoutée à la commande git-clone(1). Elle vous permet de créer un clone léger d'un dépôt ne contenant que l'historique associé à une révision donnée. Elle fonctionne de manière similaire à --branch, mais accepte un nom de référence (comme refs/heads/main, refs/tags/v1.0 et refs/merge-requests/123) ou un ID d'objet de commit hexadécimal. La différence avec --branch est qu'elle ne crée pas de branche de suivi et le pointeur HEAD est détaché. Cette option n'est donc pas adaptée si vous souhaitez contribuer à cette branche.

Vous pouvez combiner --revision avec --depth pour créer un clone minimal, par exemple dans le cadre de tests automatisés. Lorsque vous disposez d'un système CI qui doit effectuer le checkout d'une branche (ou toute autre référence) pour exécuter des tests autonomes sur le code source, un clone minimal suffit largement.

Ce changement a été mené par Toon Claes.

En savoir plus

Votre avis nous intéresse

Cet article de blog vous a plu ou vous avez des questions ou des commentaires ? Partagez vos réflexions en créant un nouveau sujet dans le forum de la communauté GitLab. Partager votre expérience

Lancez-vous dès maintenant

Découvrez comment la plateforme DevSecOps unifiée de GitLab peut aider votre équipe.

Commencer un essai gratuit

Découvrez le forfait qui convient le mieux à votre équipe

En savoir plus sur la tarification

Découvrez ce que GitLab peut offrir à votre équipe

Échanger avec un expert