Dans le cadre de nos déploiements continus, nous avions un serveur d’intégration continue qui archivait un répertoire de données et le poussait sur notre dépôt Mercurial. Ce fichier (sur les derniers commits) mesurait environ 70Mo. Nous sommes passés récemment de Mercurial vers Git pour nos projets. Cette transformation de dépôt a été effectuée grâce à l’outil fast-export. Afin que la conversion fonctionne, nous avons eu à modifier les packs de Git avec les paramètres suivants (dans le .gitconfig) :
[pack] windowMemory = "20m" packSizeLimit = "20m" threads = "4" window = "2" depth = "10"
Après cette conversion, nous avons rencontré un problème d’espace sur le répertoire du dépôt Git. En effet, le répertoire .git faisait 25Go.
Pourquoi? La réponse est simple, pour chaque commit effectué par le serveur d’intégration continue, le fichier était de nouveau stocké dans son ensemble, (ce qui est lié au fonctionnement interne de Git). Ainsi, chaque commit mesurait environ 70Mo. Il a donc fallu alléger le dépôt de ce fichier afin d’avoir un dépôt de taille acceptable et que le clonage de dépôt ne pose pas de problème. Nous avons donc procédé en quatre étapes.
1. Retrouver le fichier dans l’historique Git :
#lister dans un pack git les objets contenus en classant par taille. $ git verify-pack -v .git/objects/pack/pack-ffa83d3df62017b8f8f2000ab21d283ed110c4a8.pack | sort -k 3 -n | tail -3 > .git/objects/pack/pack-ffa83d3df62017b8f8f2000ab21d283ed110c4a8.pack: ok > non delta: 1 object > 691a3fb12ee41495d5090d5aaa3c41e179032006 blob 73731957 73749089 12 #connaître le fichier qui est si gros dans le dépôt $ git rev-list --objects --all | grep 691a3fb > 691a3fb12ee41495d5090d5aaa3c41e179032006 config/lfr-data/data.tgz #Compter le nombre de commit qui contiennent ce fichier: $ git log --oneline --branches -- config/lfr-data/data.tgz | wc -l > 437
Soit (70×437)/1024≈29.87 Go. On retrouve donc notre taille (à quelques Go près) de 25Go
2. Supprimer le fichier des commits (en soit, réécrire l’histoire) :
$ git filter-branch --index-filter 'git rm --ignore-unmatch --cached config/lfr-data/data.tgz' -- 7a0526d^.. > Rewrite 7a0526d8a8c486266fe59d4a04608f3a4c873ff5 (1/722)rm 'config/lfr-data/data.tgz' > Rewrite e55e5cd322149c87f0354967d8b11dce8c2e9287 (2/722)rm 'config/lfr-data/data.tgz' > ..... > > Ref 'refs/heads/master' was rewritten
La commande filter-branch permet de réécrire l’historique des branches en fonction de différents paramètres. Ici, nous supprimons les références à l’objet config/lfr-data/data.tgz à partir du commit 7a0526d.
3. Nettoyer le dépôt :
$ rm -Rf .git/refs/original $ rm -Rf .git/logs/ $ git gc > Counting objects: 10219, done. > Delta compression using up to 4 threads. > Compressing objects: 100% (5587/5587), done. > Writing objects: 100% (10219/10219), done. > Total 10219 (delta 3870), reused 6592 (delta 2326) > Removing duplicate objects: 100% (256/256), done. $ git count-objects -v > count: 2759 > size: 25641164 > in-pack: 10219 > packs: 7 > size-pack: 122754 > prune-packable: 0 > garbage: 0 > size-garbage: 0 $ git prune --expire now
La commande prune permet de nettoyer toutes les références encore existantes de l’objet supprimé dans l’arborescence de Git.
4. Pousser les modifications sur le dépôt distant :
$ git push --force
Attention : nous sommes obligés de forcer le push car la suppression d’un fichier inclus dans des commits implique la réécriture de l’historique du dépôt. Il faut donc forcer le push afin de réécrire l’historique sur le dépôt distant.
Grâce à ces différentes opérations, le répertoire .git est passé de 25Go à 96Mo. Nous avons désormais stocké le fichier archivé sur une machine distante sans le rajouter au dépôt.
Référence :
Dépôt de l’outil fast-export : https://github.com/frej/fast-export
Supprimer un objet de l’arborescence de Git : http://git-scm.com/book/en/v2/Git-Internals-Maintenance-and-Data-Recovery#Removing-Objects
3 commentaires sur “Nettoyage d’un dépôt Git”
une autre solution peut-être : https://github.com/github/git-lfs/
effectivement mettre des tgz (format binaire) dans un file storage externe est une bonne solution 😉
oui, et l’avantage de git-lfs (qui n’est pas encore sortie) c’est que ce sera quand même versionné. A étudier du coup…