Je cherche des moyens de réduire la taille d'un référentiel git
. La recherche me conduit à git gc --aggressive
La plupart du temps. J'ai aussi lu que ce n'est pas l'approche préférée.
Pourquoi? Que dois-je savoir si je lance gc --aggressive
?
git repack -a -d --depth=250 --window=250
Est recommandé par rapport à gc --aggressive
. Pourquoi? Comment repack
réduit-il la taille d'un référentiel? De plus, les indicateurs --depth
Et --window
Ne sont pas très clairs.
Que dois-je choisir entre gc
et repack
? Quand devrais-je utiliser gc
et repack
?
De nos jours, il n'y a pas de différence: git gc --aggressive
fonctionne selon la suggestion faite par Linus en 2007; voir ci-dessous. À partir de la version 2.11 (T4 2016), la valeur par défaut de git est 50. Une fenêtre de taille 250 est bonne car elle scanne une plus grande section de chaque objet, mais la profondeur à 250 est mauvaise car elle fait référence à une chaîne très ancienne. objets, ce qui ralentit toutes les futures opérations git pour une utilisation du disque légèrement inférieure.
Linus a suggéré (voir ci-dessous pour l'article complet de la liste de diffusion) d'utiliser git gc --aggressive
uniquement lorsque vous avez, selon ses mots, "un vraiment mauvais paquet" ou "vraiment d'horriblement mauvais deltas, mais presque toujours, dans d'autres cas, c'est en fait une très mauvaise chose à faire.
La commande qu'il suggère pour le faire correctement après avoir importé "une longue et complexe histoire" est
git repack -a -d -f --depth=250 --window=250
Mais cela suppose que vous ayez déjà gunk supprimé de votre historique de référentiel et que vous avez suivi la liste de contrôle pour la réduction d'un référentiel trouvé dans la git filter-branch
documentation .
git-filter-branch peut être utilisé pour supprimer un sous-ensemble de fichiers, généralement avec une combinaison de
--index-filter
et--subdirectory-filter
. Les gens s'attendent à ce que le référentiel résultant soit plus petit que l'original, mais vous avez besoin de quelques étapes supplémentaires pour le réduire, car Git s'efforce de ne pas perdre vos objets jusqu'à ce que vous le leur indiquiez. Assurez-vous d'abord que:
Vous avez vraiment supprimé toutes les variantes d'un nom de fichier, si un blob avait été déplacé au cours de sa vie.
git log --name-only --follow --all -- filename
peut vous aider à trouver des renommés.Vous avez vraiment filtré toutes les références: utilisez
--tag-name-filter cat -- --all
lorsque vous appelezgit filter-branch
.Ensuite, il existe deux manières d'obtenir un référentiel plus petit. Un moyen plus sûr est de cloner, pour que votre original reste intact.
- Clonez-le avec
git clone file:///path/to/repo
. Le clone n'aura pas les objets supprimés. Voir git-clone. (Notez que le clonage avec un chemin simple suffit à tout lier!)Si vous ne voulez vraiment pas le cloner, pour quelque raison que ce soit, vérifiez plutôt les points suivants (dans cet ordre). C'est une approche très destructive, alors faites une sauvegarde ou recommencez le clonage. Tu étais prévenu.
Supprimez les références d'origine sauvegardées par git-filter-branch: say
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
Expirer tous les reflogs avec
git reflog expire --expire=now --all
.Garbage récupère tous les objets non référencés avec
git gc --Prune=now
(ou si votregit gc
n'est pas assez nouveau pour prendre en charge les arguments de--Prune
, utilisez plutôtgit repack -ad; git Prune
.) .
Date: Wed, 5 Dec 2007 22:09:12 -0800 (PST) From: Linus Torvalds <torvalds at linux-foundation dot org> To: Daniel Berlin <dberlin at dberlin dot org> cc: David Miller <davem at davemloft dot net>, ismail at pardus dot org dot tr, gcc at gcc dot gnu dot org, git at vger dot kernel dot org Subject: Re: Git and GCC In-Reply-To: <[email protected]> Message-ID: <[email protected]> References: <[email protected]> <[email protected]> <[email protected]> <[email protected]> <[email protected]>
Le jeudi 6 décembre 2007, Daniel Berlin a écrit:
En fait, il s’avère que
git-gc --aggressive
fait cette chose idiote d’emballer des fichiers, que vous ayez ou non converti à partir d’un dépôt SVN.Absolument.
git --aggressive
est généralement stupide. C’est vraiment utile uniquement dans le cas de "Je sais que j’ai un vraiment mauvais paquet, et je veux jeter toutes les mauvaises décisions d’emballage que j’ai prises."Pour expliquer cela, cela vaut la peine d’expliquer (vous en êtes probablement conscient, mais permettez-moi de passer à l'essentiel) de la façon dont fonctionnent les chaînes delta git, et en quoi elles sont si différentes de la plupart des autres systèmes.
Dans d'autres SCM, une chaîne delta est généralement fixée. Il peut s’agir "d’avant" ou "d’arrière", et cela peut évoluer un peu à mesure que vous travaillez avec le référentiel, mais en général, il s’agit d’une chaîne de modifications apportées à un fichier unique représenté comme une sorte d’entité SCM unique. Dans CVS, il s’agit bien évidemment du fichier
*,v
, et de nombreux autres systèmes font des choses assez similaires.Git fait aussi des chaînes delta, mais il les fait beaucoup plus "vaguement". Il n'y a pas d'entité fixe. Les deltas sont générés par rapport à toute autre version aléatoire que git considère comme un bon candidat delta (avec diverses heuristiques assez réussies), et il n’ya absolument aucune règle de regroupement stricte.
C'est généralement une très bonne chose. C'est bon pour différentes raisons conceptuelles ( c'est-à-dire , git en interne n'a jamais vraiment besoin de se soucier de la totalité de la chaîne de révision - il ne pense pas vraiment en termes de deltas), mais c’est également formidable, car se débarrasser des règles delta inflexibles signifie que git n’a aucun problème à fusionner deux fichiers, par exemple - il n’existe tout simplement pas de
*,v
"fichiers de révision" arbitraires contenant sens caché.Cela signifie également que le choix des deltas est une question beaucoup plus ouverte. Si vous limitez la chaîne de delta à un seul fichier, vous n’avez vraiment pas beaucoup de choix sur ce qu’il faut faire pour les deltas, mais dans le cas de Git, le problème peut être totalement différent.
Et c’est là que le très mal nommé
--aggressive
entre en jeu. Alors que git essaie généralement de réutiliser les informations delta (parce que c’est une bonne idée et qu’il ne perd pas de temps en temps CPU à retrouver tous les bons deltas nous avons trouvé plus tôt), parfois vous voulez dire "recommençons à zéro, avec une ardoise vierge, et ignorons toutes les informations de delta précédentes, et essayons de générer un nouvel ensemble de deltas."Donc,
--aggressive
ne consiste pas vraiment à être agressif, mais à perdre du temps CPU à refaire une décision que nous avions déjà prise auparavant!Parfois c'est une bonne chose. Certains outils d'importation, en particulier, pourraient générer des deltas terriblement mauvais. Tout ce qui utilise
git fast-import
, par exemple, n’a probablement pas une excellente disposition delta, il serait donc utile de dire "Je veux partir de zéro".Mais presque toujours, dans d’autres cas, c’est vraiment une très mauvaise chose à faire. Cela va perdre du temps CPU, et surtout si vous avez déjà fait du bon travail en deltaing plus tôt, le résultat final ne va pas réutiliser tous ces bons bons deltas trouvé, donc vous vous retrouverez avec un résultat final bien pire!
J'enverrai un correctif à Junio pour supprimer simplement la documentation
git gc --aggressive
. Cela peut être utile, mais cela n’est généralement utile que lorsque vous comprenez vraiment très bien ce qu’il fait, et que la documentation ne vous aide pas à le faire.Généralement, la méthode incrémentielle
git gc
est la bonne approche et elle est préférable à la méthodegit gc --aggressive
. Il va réutiliser d’anciens deltas, et lorsque ces anciens deltas ne peuvent pas être trouvés (la raison pour laquelle on a commencé à faire du GC incrémental!), Il va en créer de nouveaux.D'autre part, il est tout à fait vrai qu'une "importation initiale d'une histoire longue et complexe" est un point où il peut être intéressant de passer beaucoup de temps à chercher les vraiment bons deltas. . Ensuite, chaque utilisateur pour toujours (tant qu’ils n’utiliseront pas
git gc --aggressive
pour l'annuler!) Bénéficiera de cet événement unique. Donc, en particulier pour les grands projets avec une longue histoire, il vaut probablement la peine de faire un travail supplémentaire, en disant au code de recherche delta de se déchaîner.Donc, l’équivalent de
git gc --aggressive
- mais fait correctement - consiste à faire (du jour au lendemain) quelque chose comme:git repack -a -d --depth=250 --window=250
où cette profondeur correspond à peu près à la profondeur que peuvent avoir les chaînes de delta (les rallonger pour l’ancienne histoire - cela vaut la peine de perdre de l’espace), et la fenêtre concerne la taille de la fenêtre d’un objet que nous voulons que chaque candidat delta analyse.
Et ici, vous voudrez peut-être ajouter le drapeau
-f
(qui consiste à "supprimer tous les anciens deltas", car vous essayez maintenant de vous assurer que celui-ci trouve les bons candidats.Et puis ça va prendre une éternité et un jour ( c’est-à-dire. , une chose à faire du jour au lendemain). Mais le résultat final est que tout le monde en aval de ce référentiel obtiendra de bien meilleurs packs, sans avoir à y consacrer aucun effort.
Linus
Quand devrais-je utiliser gc & remballer?
Comme je l’ai mentionné dans " la collection Git Garbage ne semble pas fonctionner complètement ", a git gc --aggressive
n'est ni suffisant ni même suffisant à lui seul.
Et, comme j'explique ci-dessous , souvent inutile.
La combinaison la plus efficace consisterait à ajouter git repack
, mais aussi git Prune
:
git gc
git repack -Ad # kills in-pack garbage
git Prune # kills loose garbage
Remarque: Git 2.11 (Q4 2016) définira la valeur par défaut gc aggressive
profondeur jusqu'à 50
Voir commit 07e7dbf (11 août 2016) par Jeff King (peff
) .
(Fusion par Junio C Hamano - gitster
- dans commit 0952ca8 , 21 septembre 2016)
gc
: profondeur agressive par défaut à 50"
git gc --aggressive
"utilisé pour limiter la longueur de la chaîne delta à 250, ce qui est beaucoup trop profond pour permettre des gains d’espace supplémentaires et nuit aux performances d’exécution.
La limite a été réduite à 50.Le résumé est le suivant: la valeur par défaut actuelle de 250 n’économise pas beaucoup d’espace et coûte de la CPU. Ce n'est pas un bon compromis.
Le "
--aggressive
"drapeau àgit-gc
fait trois choses:
- utilisation "
-f
"pour supprimer les deltas existants et recalculer à partir de zéro- utilisez "--window = 250" pour chercher plus fort pour les deltas
- utilisez "--depth = 250" pour créer des chaînes delta plus longues
Les éléments (1) et (2) conviennent bien pour un remballage "agressif".
Ils demandent au reconditionnement de faire plus de travail de calcul dans l’espoir d’obtenir un meilleur pack. Vous payez les coûts pendant le remballage et les autres opérations ne voient que l’avantage.L'élément (3) n'est pas si clair.
Permettre des chaînes plus longues signifie moins de restrictions sur les deltas, ce qui signifie potentiellement trouver de meilleures chaînes et gagner de la place.
Mais cela signifie également que les opérations qui accèdent aux deltas doivent suivre des chaînes plus longues, ce qui affecte leur performance.
C'est donc un compromis, et il n'est pas clair que le compromis soit même bon.
(Voir commettre pour l'étude )
Vous pouvez constater que les économies de processeur pour les opérations courantes s’améliorent à mesure que nous diminuons la profondeur.
Mais nous pouvons aussi voir que les économies d’espace ne sont pas aussi grandes que la profondeur augmente. Économiser entre 5 et 10% entre 10 et 50 équivaut probablement au compromis processeur. Économiser 1% pour passer de 50 à 100, ou encore 0,5% pour passer de 100 à 250 ne l’est probablement pas.
En parlant d’économie de processeur, "git repack
"a appris à accepter le --threads=<n>
_ et passez-le à pack-objects.
Voir commit 40bcf31 (26 avril 2017) par Junio C Hamano (gitster
) .
(Fusionnée par Junio C Hamano - gitster
- dans commit 31fb6f4 , 29 mai 2017)
remballer: accepter
--threads=<n>
et le transmettre àpack-objects
Nous le faisons déjà pour --window=<n>
et --depth=<n>
; cela aidera lorsque l'utilisateur veut forcer --threads=1
pour des tests reproductibles sans être affecté par la course de plusieurs threads.
Le problème avec git gc --aggressive
est que le nom de l’option et la documentation sont trompeurs.
Comme Linus lui-même explique dans cet e-mail , qu'est-ce que git gc --aggressive
fondamentalement, est-ce:
Bien que git essaye généralement de réutiliser les informations de delta (parce que c'est une bonne idée et qu'il ne perd pas de temps CPU à retrouver tous les bons deltas que nous avons trouvés plus tôt), vous voulez parfois dire "recommençons, avec un slate vierge et ignorer toutes les informations delta précédentes et essayer de générer un nouvel ensemble de deltas ".
Généralement, il n’est pas nécessaire de recalculer les deltas dans git, car ceux-ci sont très flexibles. Cela n'a de sens que si vous savez que vous avez de très, très mauvais deltas. Comme l'explique Linus, principalement les outils qui utilisent git fast-import
tombent dans cette catégorie.
La plupart du temps, git fait un très bon travail pour déterminer les deltas utiles et utiliser git gc --aggressive
vous laissera des deltas potentiellement encore pires tout en vous faisant perdre beaucoup de temps processeur.
Linus termine son courrier avec la conclusion que git repack
avec un grand --depth
et --window
est le meilleur choix dans la plupart des cas; surtout après avoir importé un grand projet et que vous voulez vous assurer que git trouve de bons deltas.
Donc, l'équivalent de
git gc --aggressive
- mais fait correctement - consiste à faire (du jour au lendemain) quelque chose comme:
git repack -a -d --depth=250 --window=250
où cette profondeur correspond à peu près à la profondeur que peuvent avoir les chaînes de delta (les allonger pour l'ancienne histoire - cela vaut la peine de perdre de l'espace), et la fenêtre concerne la taille de la fenêtre d'objet que nous voulons que chaque candidat delta analyse.
Et ici, vous voudrez peut-être ajouter le
-f
drapeau (qui consiste à "supprimer tous les anciens deltas", car vous essayez maintenant de vous assurer que celui-ci trouve les bons candidats.
Mise en garde. Ne cours pas git gc --agressive
avec un référentiel qui n'est pas synchronisé avec remote si vous n'avez pas de sauvegardes.
Cette opération recrée les deltas à partir de zéro et peut entraîner une perte de données en cas d'interruption harmonieuse.
Pour mon ordinateur de 8 Go, gc agressif a manqué de mémoire sur un référentiel de 1 Go avec 10 000 petits commits. Lorsque le tueur OOM a mis fin au processus git - il me laissait un référentiel presque vide, il ne restait que des arbres en fonctionnement et quelques deltas.
Bien sûr, ce n’était pas la seule copie du référentiel, je l’ai donc simplement recréée et extraite à distance (l’extraction n’a pas fonctionné avec un référentiel brisé et bloquée lors de la résolution des deltas, j’ai essayé de le faire), mais si votre référentiel est repo local pour développeur unique sans télécommandes - sauvegardez-le en premier.
Remarque: méfiez-vous de l'utilisation de git gc --aggressive
, Comme le précise Git 2.22 (T2 2019).
Voir commit 0044f77 , commit daecbf2 , commit 7384504 , commit 22d4e3b , commit 080a448 , commit 54d56f5 , commit d257e0f , commit b6a8d09 (07 avril 2019), et commit fc559fb , commit cf9cd77 , commit b11e856 (22 mars 2019) par var Arnfjörð Bjarmason (avar
) .
(Fusionné par Junio C Hamano - gitster
- dans commit ac70c5 , 25 avril 2019)
gc
docs: minimiser l'utilité de--aggressive
Les documents existants "
gc --aggressive
" N'arrivent pas à recommander aux utilisateurs de l'exécuter régulièrement.
J'ai personnellement parlé à de nombreux utilisateurs qui ont pris ces documents comme un conseil pour utiliser cette option. Ils ont généralement une perte de temps .Alors clarifions ce qu’il fait réellement et laissons l’utilisateur tirer ses propres conclusions.
Clarifions également les "Les effets [...] sont persistants" pour paraphraser une brève version de explication de Jeff King .
Cela signifie que la documentation de git-gc inclut maintenant :
AGRESSIF
Quand l'option
--aggressive
Est fournie,git-repack
sera invoqué avec le drapeau-f
, Qui passera à son tour--no-reuse-delta
À git-pack-objects .
Cela jettera tous les deltas existants et les recalculera, au détriment de passer beaucoup plus de temps à remballer.Les effets sont principalement persistants, par exemple Lorsque des paquets et des objets en vrac sont fusionnés, les deltas existants dans ce paquet peuvent être réutilisés, mais il existe également différents cas dans lesquels nous pourrions choisir un delta sous-optimal à partir d'un nouveau paquet.
De plus, fournir
--aggressive
Modifiera les options--depth
Et--window
Passées àgit-repack
.
Voir les paramètresgc.aggressiveDepth
Etgc.aggressiveWindow
Ci-dessous.
En utilisant une fenêtre plus grande, nous avons plus de chances de trouver des deltas plus optimaux.Cela ne vaut probablement pas la peine d'utiliser cette option sur un référentiel donné sans exécuter de tests de performances sur mesure .
Cela prend beaucoup plus de temps et l'optimisation résultante de l'espace/delta peut en valoir la peine. Ne pas l'utiliser du tout est le bon compromis pour la plupart des utilisateurs et leurs référentiels.
Et ( commit 080a448 ):
gc
docs: notez l'impact de--aggressive
sur--window
&--depth
Depuis 7e7dbf (
gc
: profondeur agressive par défaut à 50, 2016-08-11, Git v2.10.1), nous utilisons de manière quelque peu confuse la même profondeur sous--aggressive
faire par défaut.Comme indiqué dans ce commit qui a du sens, il était erroné de définir par défaut le paramètre "agressif" par défaut, ce qui permet d'économiser de l'espace disque au détriment des performances d'exécution, ce qui est généralement le contraire de celui qui aime "agressif". veut.