Comment gérer les dépôts volumineux avec Git

Nicola Paolucci
Nicola Paolucci
Retour à la liste

Git est une alternative fantastique pour suivre l'évolution de votre base de code et pour collaborer efficacement avec vos pairs. Mais que se passe-t-il si le dépôt dont vous voulez faire le suivi est vraiment gigantesque ?

Dans ce billet, je m'efforcerai de vous donner quelques idées et techniques pour traiter correctement différentes catégories de dépôts volumineux.

Deux catégories de dépôts volumineux

Quand on y pense, les dépôts grossissent considérablement pour deux grandes raisons :

  • Ceux-ci accumulent un historique très long (le projet grossit au fil du temps et les éléments s'accumulent).
  • Ils incluent des actifs binaires volumineux qu'il faut tracker et apparier avec le code.
  • Les deux raisons ci-dessus.

Un dépôt peut évoluer dans deux directions orthogonales : La taille du dépôt de travail (c.-à-d. le dernier commit) et la taille de tout l'historique accumulé.

Parfois, un autre problème se pose : d'anciens artefacts binaires obsolètes sont encore stockés dans le dépôt. Nous avons trouvé un moyen relativement simple, mais pénible, pour corriger ce bug. Voir ci-dessous.

Pour les deux scénarios ci-dessus, les techniques et les solutions de contournement diffèrent et se complètent parfois. Je les décrirai séparément.

Gérer les dépôts avec un historique très long

Même si les limites qui identifient un dépôt comme volumineux sont relativement élevées (par exemple, le dernier noyau Linux compte plus de 15 millions de lignes de code, mais les gens semblent heureux de le parcourir en entier), les projets très anciens qui doivent être conservés intégralement pour des raisons réglementaires/légales peuvent devenir difficiles à cloner. (Pour tout vous dire, le noyau Linux est divisé en un dépôt historique et un autre dépôt plus récent. Vous avez simplement besoin d'une configuration scindée pour avoir accès à tout l'historique unifié.)

Un clone superficiel est une solution simple

La première solution pour réaliser un clone rapide et pour faire gagner du temps et de l'espace disque aux développeurs et aux systèmes est d'exécuter une commande shallow clone avec Git. Un clone superficiel vous permet de cloner un dépôt en conservant uniquement les n commits les plus récents de l'historique.

Comment faire ? Utilisez simplement l'option - -depth, par exemple :

git clone --depth depth remote-url

Imaginez que vous ayez accumulé dix années d'historique (ou plus) dans votre dépôt (par exemple, pour Jira, nous avons migré une base de code de 11 ans vers Git), les gains de temps peuvent s'additionner et devenir substantiels.

Le clone complet de Jira fait 677 Mo (avec un répertoire de travail de plus de 320 Mo), ce qui représente plus de 47 000 commits. À partir d'une extraction rapide sur Jira, le traitement d'un clone superficiel a pris 29,5 secondes, contre 4 minutes 24 secondes pour un clone complet avec tout l'historique. Et, la disparité croît proportionnellement au nombre d'actifs binaires intégrés à votre projet au fil du temps. Dans tous les cas, les avantages de cette technique pour les systèmes sont vraiment intéressants.

Récemment, Git a amélioré la prise en charge des clones superficiels.

Les clones superficiels étaient souvent considérés comme des moutons noirs dans le monde de Git, car certaines opérations étaient à peine prises en charge. Toutefois, les versions récentes (1.9+) ont grandement amélioré la situation, et vous pouvez à présent réaliser des opérations de pull et de push efficaces sur les dépôts, même à partir d'un clone superficiel.

L'utilisation de la commande filter-branch est une solution partielle.

Pour les dépôts volumineux dans lesquels des fichiers binaires inutiles ont été commités par mégarde ou qui contiennent d'anciens actifs inutiles, l'utilisation de filter-branch est la solution idéale. La commande permet de parcourir l'historique entier du projet en filtrant, en manipulant, en modifiant et en ignorant des fichiers en fonction de modèles prédéfinis. C'est un outil vraiment efficace dans votre arsenal Git. Des scripts d'assistance sont déjà disponibles pour identifier les objets volumineux de votre dépôt Git. L'opération devrait donc être assez facile.

Exemple d'utilisation de filter-branch (crédit) :

git filter-branch --tree-filter 'rm -rf /path/to/spurious/asset/folder' HEAD

filter-branch présente un léger inconvénient : lorsque vous utilisez la commande filter-branch, vous réécrivez en fait tout l'historique de votre projet. Tous les ID des commits changent. Chaque développeur doit alors de nouveau cloner le dépôt mis à jour.

Par conséquent, si vous prévoyez d'effectuer un nettoyage avec filter-branch, avertissez votre équipe, planifiez une brève interruption pendant l'opération, puis prévenez tout le monde qu'il est nécessaire de cloner à nouveau le dépôt.

Alternatives au clone superficiel : cloner une seule branche

Depuis la version git 1.7.10 (avril 2012), vous pouvez également limiter la taille de l'historique cloné en ne clonant qu'une branche. Pour cela, procédez comme suit :

git clone URL --branch nom_branche --single-branch [dossier]

Ce hack spécifique pourrait s'avérer utile pour les branches longues et divergentes ou si vous avez de nombreuses branches. Si vous avez quelques branches comprenant un nombre limité de différences, vous ne constaterez probablement pas une grande différence lorsque vous utiliserez cette commande.

Référence Stack Overflow.

Gérer les dépôts comprenant des actifs binaires volumineux

La deuxième catégorie de dépôts volumineux se compose des bases de code qui doivent suivre des actifs binaires volumineux. Les équipes de conception de jeux jonglent avec des modèles 3D volumineux. Les équipes de développement Web doivent suivre des ressources d'image RAW. Les équipes de CAO doivent manipuler et suivre le statut de livrables binaires. Par conséquent, plusieurs catégories d'équipes de développement rencontrent ce problème avec Git.

Git n'est pas spécialement inadapté à la manipulation d'actifs binaires, mais ce n'est pas non plus sa spécialité. Par défaut, Git compressera et stockera toutes les versions ultérieures des actifs binaires, ce qui n'est évidemment pas optimal si vous en avez beaucoup.

Certains changements basiques améliorent la situation, par exemple, l'exécution du process de nettoyage garbage collection git gc ou la modification de l'utilisation des commits delta pour certains types binaires dans .gitattributes.

Mais il est important de réfléchir à la nature des fichiers binaires de votre projet, car la meilleure approche peut varier. Par exemple, voici trois points à vérifier (merci à Stefan Saasen pour ses remarques) :

  • Pour les fichiers binaires qui changent beaucoup (pas seulement quelques en-têtes de métadonnées), la compression delta sera probablement inutile. Il est donc recommandé d'exécuter la commande delta off pour tous les fichiers afin d'éviter l'exécution inutile de la compression delta lors du recompactage.
  • Dans le cas ci-dessus, il est probable que la compression zlib de ces fichiers ne soit pas très efficace. Vous pouvez donc désactiver la compression avec core.compression 0 ou core.loosecompression 0. Il s'agit d'un paramètre global qui se répercutera sur tous les fichiers non binaires pour lesquels la compression actuelle se déroule parfaitement. Cette recommandation n'est par conséquent pertinente que si vous placez les actifs binaires dans un dépôt distinct.
  • Il est important de garder à l'esprit que git gc convertit les objets bruts « dupliqués » en un fichier groupé unique. Mais, le fichier groupé ainsi obtenu ne sera pas tellement différent, sauf si les fichiers sont compressés d'une quelconque façon.
  • Découvrez la personnalisation de core.bigFileThreshold. La compression delta ne fonctionnera quoi qu'il en soit pas pour les fichiers de plus de 512 MiO (sans nécessité de définir .gitattributes), c'est donc une fonctionnalité qu'il serait intéressant de modifier.

Technique 1 : sparse checkout

La commande sparse checkout contribue à résoudre le problème des actifs binaires (disponible depuis Git 1.7.0]). Cette technique permet de nettoyer le répertoire de travail en détaillant précisément les dossiers que vous souhaitez renseigner. Malheureusement, cela n'affecte pas la taille du dépôt local général, mais peut être utile si vous disposez d'une importante arborescence de dossiers.

Quelles sont les commandes impliquées ? Voici un exemple (crédit) :

  • Cloner une fois le dépôt complet : git clone <adresse-répertoire>
  • Activer la fonctionnalité : git config core.sparsecheckout true
  • Ajouter les dossiers qui sont explicitement requis, en ignorant les dossiers assets :
echo src/ › .git/info/sparse-checkout
  • Lire l'arborescence comme spécifié : git read-tree -m -u HEAD

Vous pouvez ensuite revenir à vos commandes Git habituelles, mais votre répertoire de travail ne contiendra que les dossiers spécifiés auparavant.

Technique nº 2 : Utiliser des submodules

Vous disposez d'une autre méthode pour gérer les dossiers de fichiers binaires volumineux : les diviser en un dépôt séparé et placer les fichiers dans votre projet principal à l'aide de submodules. Vous pouvez ainsi contrôler le moment où les fichiers sont mis à jour. Pour en savoir plus sur les submodules, consultez ces billets : Concept de base et conseils et Alternatives.

Si vous adoptez la méthode submodules, vous pouvez vous reporter au billet traitant des complexités de la gestion des dépendances de projet, étant donné que l'approche que j'y évoque pourrait compléter certaines des approches possibles pour les problèmes d'actifs binaires.

Technique nº 3 : Utiliser git annex ou git-bigfiles

Une troisième possibilité pour manipuler les actifs binaires avec Git consiste à utiliser une extension tierce efficace.

La première commande mentionnée est git-annex. Elle permet de gérer les fichiers binaires avec Git sans vérifier le contenu du fichier dans le dépôt. git-annex enregistre les fichiers dans un magasin clés/valeurs spécial. Seuls les liens symboliques sont ensuite vérifiés dans Git et contrôlés comme des fichiers normaux. La méthode est directe et ses exemples parlent d'eux-mêmes.

La deuxième commande est git-bigfiles, un fork Git qui vise à simplifier la vie des utilisateurs de Git pour le traitement des projets contenant des fichiers très volumineux.

[MISE À JOUR] …ou vous pouvez oublier tout cela et utiliser Git LFS

Si vous utilisez régulièrement des fichiers volumineux, la meilleure solution est peut-être d'exploiter l'extension Large File Storage (LFS) codéveloppée par Atlassian et GitHub en 2015. 

Git LFS est une extension qui stocke des pointeurs (évidemment !) vers des fichiers volumineux dans votre dépôt, au lieu de stocker les fichiers eux-mêmes. Les fichiers réels sont stockés sur un serveur distant. Comme vous pouvez l'imaginer, cela réduit considérablement le temps nécessaire pour cloner votre dépôt.

Comme GitHub, Bitbucket prend en charge Git LFS. Il est donc probable que vous ayez déjà accès à cette technologie. Elle est particulièrement utile pour les équipes comprenant des concepteurs, des vidéastes, des musiciens ou des utilisateurs de CAO.

Conclusions

Ne laissez pas le volume de votre historique de dépôt et de vos actifs vous priver des incroyables fonctionnalités de Git. Il est possible de résoudre chacun de ces problèmes.

Suivez-moi (@durdn) pour plus d'infos passionnantes sur les systèmes de contrôle de version décentralisés.

Prêt à découvrir Git ?

Essayez ce tutoriel interactif.

Démarrez maintenant