git gc --agressif vs git repack

88

Je cherche des moyens de réduire la taille d'un gitréférentiel. La recherche m'amène la git gc --aggressiveplupart du temps. J'ai également lu que ce n'est pas l'approche préférée.

Pourquoi? que dois-je savoir si je cours gc --aggressive?

git repack -a -d --depth=250 --window=250est recommandé sur gc --aggressive. Pourquoi? Comment repackréduit la taille d'un référentiel? De plus, je ne suis pas tout à fait clair sur les drapeaux --depthet --window.

Que dois-je choisir entre gcet repack? Quand dois-je utiliser gcet repack?

Ajith R Nayak
la source

Réponses:

76

Aujourd'hui, il n'y a aucune différence: git gc --aggressivefonctionne selon la suggestion de Linus faite en 2007; voir ci-dessous. À partir de la version 2.11 (Q4 2016), git prend par défaut une profondeur de 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 à chaque 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.


Contexte historique

Linus a suggéré (voir ci-dessous pour le post complet de la liste de diffusion) de n'utiliser git gc --aggressiveque lorsque vous avez, selon ses mots, "un très mauvais pack" ou "vraiment horriblement mauvais deltas", mais "presque toujours, dans d'autres cas, c'est en fait un très mauvais chose à faire." Le résultat peut même laisser votre référentiel dans un état pire que lorsque vous avez commencé!

La commande qu'il suggère pour faire cela correctement après avoir importé «une histoire longue et complexe» est

git repack -a -d -f --depth=250 --window=250

Mais cela suppose que vous avez déjà supprimé les résidus indésirables de l'historique de votre référentiel et que vous avez suivi la liste de contrôle pour réduire un référentiel figurant dans la git filter-branchdocumentation .

git-filter-branch peut être utilisé pour se débarrasser d'un sous-ensemble de fichiers, généralement avec une combinaison de --index-filteret --subdirectory-filter. Les gens s'attendent à ce que le dépôt 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 lui disiez. Assurez-vous d'abord que:

  • Vous avez vraiment supprimé toutes les variantes d'un nom de fichier, si un objet blob a été déplacé au cours de sa durée de vie. git log --name-only --follow --all -- filenamepeut vous aider à trouver des noms.

  • Vous avez vraiment filtré toutes les références: à utiliser --tag-name-filter cat -- --alllors de l'appel git filter-branch.

Ensuite, il existe deux façons d'obtenir un référentiel plus petit. Un moyen plus sûr est de cloner, qui garde votre original 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 relie simplement tout!)

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 destructrice, alors faites une sauvegarde ou recommencez à la cloner. Tu étais prévenu.

  • Supprimez les références d'origine sauvegardées par git-filter-branch: dites

    git for-each-ref --format="%(refname)" refs/original/ |
      xargs -n 1 git update-ref -d
    
  • Expirez tous les reflogs avec git reflog expire --expire=now --all.

  • Garbage collect tous les objets non référencés avec git gc --prune=now(ou si votre git gcn'est pas assez nouveau pour prendre en charge les arguments --prune, utilisez à la git repack -ad; git pruneplace).


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 --aggressivecela fait parfois cette chose stupide de compresser des fichiers, que vous ayez ou non converti à partir d'un dépôt SVN.

Absolument. git --aggressiveest la plupart du temps stupide. Ce n'est vraiment utile que dans le cas de "Je sais que j'ai un très mauvais pack, 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 laissez-moi tout de même passer en revue les bases) comment 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 fixe. Cela peut être «vers l'avant» ou «vers l'arrière», et cela peut évoluer un peu à mesure que vous travaillez avec le référentiel, mais il s'agit généralement d'une chaîne de changements vers un seul fichier représenté comme une sorte d'entité SCM unique. Dans CVS, c'est évidemment le *,vfichier, et beaucoup d'autres systèmes font des choses assez similaires.

Git fait aussi des delta-chains, mais il les fait beaucoup plus «vaguement». Il n'y a pas d'entité fixe. Les deltas sont générés contre toute autre version aléatoire que git considère comme un bon candidat delta (avec diverses heuristiques assez réussies), et il n'y a absolument aucune règle de regroupement dure.

C'est généralement une très bonne chose. C'est bon pour diverses raisons conceptuelles ( c'est-à - dire que git en interne n'a jamais vraiment besoin de se soucier de toute la chaîne de révision - il ne pense pas vraiment en termes de deltas), mais c'est aussi génial parce que se débarrasser des règles de delta inflexibles signifie que git n'a aucun problème avec la fusion de deux fichiers ensemble, par exemple - il n'y a tout simplement pas de *,v«fichiers de révision» arbitraires qui ont une signification cachée.

Cela signifie également que le choix des deltas est une question beaucoup plus ouverte. Si vous limitez la chaîne delta à un seul fichier, vous n'avez vraiment pas beaucoup de choix sur ce qu'il faut faire avec les deltas, mais dans git, cela peut vraiment être un problème totalement différent.

Et c'est là que le très mal nommé --aggressiveentre 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 CPU à retrouver tous les bons deltas que nous avons trouvés plus tôt), parfois vous Je veux dire "recommençons, avec une ardoise vierge, ignorons toutes les informations delta précédentes et essayons de générer un nouvel ensemble de deltas."

Il --aggressivene s'agit donc pas vraiment d'être agressif, mais de perdre du temps CPU à refaire une décision que nous avons déjà prise plus tôt!

Parfois, c'est une bonne chose. Certains outils d'importation en particulier pourraient générer des deltas vraiment horribles. Tout ce qui utilise git fast-import, par exemple, n'a probablement pas une grande disposition en delta, il peut donc être utile de dire "Je veux partir d'une table rase."

Mais presque toujours, dans d'autres cas, c'est en fait une très mauvaise chose à faire. Cela va gaspiller du temps CPU, et surtout si vous avez fait du bon travail de deltaing plus tôt, le résultat final ne va pas réutiliser tous ces bons deltas que vous avez déjà trouvés, vous vous retrouverez donc avec beaucoup pire résultat final aussi!

Je vais envoyer un correctif à Junio ​​pour simplement supprimer la git gc --aggressive documentation. Cela peut être utile, mais il n'est généralement utile que lorsque vous comprenez vraiment à un niveau très profond ce qu'il fait, et cette documentation ne vous aide pas à le faire.

En règle générale, faire progressivement git gcest la bonne approche, et mieux que de faire git gc --aggressive. Il va réutiliser les anciens deltas, et quand ces anciens deltas ne peuvent pas être trouvés (la raison pour laquelle le GC incrémental est en premier lieu!), Il va en créer de nouveaux.

D'un autre côté, il est tout à fait vrai qu'une «importation initiale d'une histoire longue et complexe» est un moment où il peut valoir la peine de passer beaucoup de temps à trouver les très bons deltas. Ensuite, chaque utilisateur après (tant qu'il n'utilise pas git gc --aggressivepour l'annuler!) Bénéficiera de cet événement unique. Donc, en particulier pour les grands projets avec une longue histoire, cela vaut probablement la peine de faire un travail supplémentaire, en disant au code de recherche de delta de se déchaîner.

Donc, l'équivalent de git gc --aggressive- mais fait correctement - est de faire (du jour au lendemain) quelque chose comme

git repack -a -d --depth=250 --window=250

où cette question de profondeur est à peu près la profondeur des chaînes delta (allongez-les pour l'histoire ancienne - cela en vaut la peine), et la fenêtre concerne la taille d'une fenêtre d'objet que nous voulons que chaque candidat delta scanne.

Et ici, vous voudrez peut-être ajouter le -fdrapeau (qui est le «supprimer tous les anciens deltas», puisque vous essayez maintenant de vous assurer que celui-ci trouve réellement de 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
Greg Bacon
la source
2
Votre commentaire sur la profondeur est un peu déroutant. Au début, j'allais me plaindre que vous avez complètement tort, que l'agressivité peut considérablement accélérer un dépôt git. Après avoir effectué un garbage collection agressif, un dépôt ÉNORME qui a pris cinq minutes pour faire un état git réduit à secondes. Mais ensuite, j'ai réalisé que vous ne vouliez pas dire que le gc agressif ralentissait le repo, mais juste une taille de profondeur extrêmement grande.
user6856
57

Quand devrais-je utiliser GC & Repack?

Comme je l'ai mentionné dans " Git Garbage collection ne semble pas fonctionner complètement ", a git gc --aggressiven'est ni suffisant ni même suffisant à lui seul.
Et, comme je l'explique ci - dessous , souvent pas nécessaire.

La combinaison la plus efficace serait d'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 gc aggressiveprofondeur par défaut à 50

Voir commit 07e7dbf (11 août 2016) par Jeff King ( peff) .
(Fusionné 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 bien trop profond pour gagner de l'espace supplémentaire 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 du processeur. Ce n'est pas un bon compromis.

Le --aggressivedrapeau " " git-gcfait trois choses:

  1. utilisez " -f" pour supprimer les deltas existants et recalculer à partir de zéro
  2. utilisez "--window = 250" pour rechercher des deltas plus difficiles
  3. utilisez "--depth = 250" pour créer des chaînes delta plus longues

Les éléments (1) et (2) correspondent bien à un reconditionnement «agressif».
Ils demandent au reconditionnement de faire plus de calcul dans l'espoir d'obtenir un meilleur pack. Vous payez les frais lors du reconditionnement, et les autres opérations ne voient que l'avantage.

Le point (3) n'est pas aussi clair.
Autoriser des chaînes plus longues signifie moins de restrictions sur les deltas, ce qui signifie potentiellement en trouver de meilleures et économiser de l'espace.
Mais cela signifie également que les opérations qui accèdent aux deltas doivent suivre des chaînes plus longues, ce qui affecte leurs performances.
C'est donc un compromis, et il n'est pas clair que le compromis soit même bon.

(Voir commit pour étude )

Vous pouvez voir que les économies de CPU pour les opérations régulières s'améliorent à mesure que nous diminuons la profondeur.
Mais on peut aussi voir que les gains d'espace ne sont pas si importants que la profondeur augmente. Économiser 5 à 10% entre 10 et 50 vaut probablement le compromis du processeur. Économiser 1% pour passer de 50 à 100, ou encore 0,5% pour passer de 100 à 250, ce n'est probablement pas.


En parlant d'économie de CPU, " git repack" a appris à accepter l' --threads=<n>option et à la transmettre aux objets pack.

Voir commit 40bcf31 (26 avril 2017) par Junio ​​C Hamano ( gitster) .
(Fusionné par Junio ​​C Hamano - gitster- dans commit 31fb6f4 , 29 mai 2017)

remballer: accepter --threads=<n>et transmettre àpack-objects

Nous le faisons déjà pour --window=<n>et --depth=<n>; cela aidera lorsque l'utilisateur veut forcer --threads=1des tests reproductibles sans être affecté par la course de plusieurs threads.

VonC
la source
3
J'ai mentionné le fil de discussion Linus dans le lien "Git Garbage collection ne semble pas fonctionner complètement"
VonC
1
Merci pour cette mise à jour moderne! Toute autre réponse ici est ancienne. Maintenant, nous pouvons voir que cela git gc --aggressivea été corrigé deux fois: Premièrement, faire ce que Linus a suggéré en 2007 comme une «meilleure méthode d'emballage». Et puis dans Git 2.11 pour éviter la profondeur d'objet excessive que Linus avait suggérée mais qui s'est avérée être nuisible (ralentit toutes les futures opérations Git et n'a pas économisé de place qui vaut la peine de parler).
gw0
git gc, suivi de git repack -Ad et git prune augmente la taille de mon dépôt ... pourquoi?
devops
@devops Vous n'êtes pas sûr: quelle version de Git utilisez-vous? Vous pouvez poser une nouvelle question pour cela (avec plus de détails comme le système d'exploitation, la taille générale de votre dépôt, ...)
VonC
man git-repackdit pour -d: `Exécutez également git prune-pack pour supprimer les fichiers objets redondants en vrac.` Ou fait-il git pruneégalement cela? man git-prunedit In most cases, users should run git gc, which calls git prune., alors quelle est l'utilité après git gc? Ne serait-il pas préférable ou suffisant d'utiliser uniquement git repack -Ad && git gc?
Jakob
14

Le problème avec git gc --aggressiveest que le nom de l'option et la documentation sont trompeurs.

Comme l' explique Linus lui-même dans ce mail , git gc --aggressivevoici ce que fait fondamentalement:

Alors que git essaie généralement de réutiliser les informations delta (parce que c'est une bonne idée, et que cela ne gaspille pas de temps CPU à retrouver tous les bons deltas que nous avons trouvés plus tôt), parfois vous voulez dire "recommençons tout, avec un videz l'ardoise et ignorez toutes les informations delta précédentes et essayez de générer un nouvel ensemble de deltas ".

Habituellement, il n'est pas nécessaire de recalculer les deltas dans git, car git détermine ces deltas de manière très flexible. Cela n'a de sens que si vous savez que vous avez vraiment de très mauvais deltas. Comme l'explique Linus, la plupart des outils qui l'utilisent git fast-importentrent dans cette catégorie.

La plupart du temps, git fait un très bon travail pour déterminer les deltas utiles et l'utilisation git gc --aggressivevous laissera avec des deltas qui sont potentiellement encore pires tout en gaspillant beaucoup de temps CPU.


Linus termine son courrier avec la conclusion git repackqu'avec un grand --depthet --windowest le meilleur choix dans la plupart du temps; surtout après avoir importé un gros projet et que vous voulez vous assurer que git trouve de bons deltas.

Donc, l'équivalent de git gc --aggressive- mais fait correctement - est de faire (du jour au lendemain) quelque chose comme

git repack -a -d --depth=250 --window=250

où cette question de profondeur est à peu près la profondeur des chaînes delta (allongez-les pour l'histoire ancienne - cela en vaut la peine), et la fenêtre concerne la taille d'une fenêtre d'objet que nous voulons que chaque candidat delta scanne.

Et ici, vous voudrez peut-être ajouter le -fdrapeau (qui est le "drop all old deltas", puisque vous essayez maintenant de vous assurer que celui-ci trouve réellement de bons candidats.

Sascha Wolf
la source
8

Mise en garde. N'exécutez pas git gc --agressiveavec un référentiel qui n'est pas synchronisé avec distant si vous n'avez aucune sauvegarde.

Cette opération recrée les deltas à partir de zéro et peut entraîner une perte de données si elle est interrompue correctement.

Pour mon ordinateur de 8 Go, gc agressif a manqué de mémoire sur un référentiel de 1 Go avec 10k petits commits. Quand OOM killer a mis fin au processus git - il m'a laissé un référentiel presque vide, seul l'arbre de travail et quelques deltas ont survécu.

Bien sûr, ce n'était pas la seule copie du référentiel, donc je l'ai simplement recréé et extrait de la télécommande (la récupération ne fonctionnait pas sur le repo cassé et a été bloquée à l'étape de `` résolution des deltas '' à plusieurs reprises, j'ai essayé de le faire), mais si votre dépôt est dépôt local à développeur unique sans télécommande du tout - sauvegardez-le d'abord

Pointeur de sauge
la source
5

Remarque: méfiez-vous de l'utilisation git gc --aggressive, comme le précise Git 2.22 (Q2 2019).

Voir commettre 0044f77 , engager daecbf2 , engager 7.384.504 , engager 22d4e3b , engager 080a448 , engager 54d56f5 , commettre d257e0f , commettre b6a8d09 (7 avril 2019), et engager fc559fb , engager cf9cd77 , engager b11e856 (22 mars 2019) par Ævar Arnfjord Bjarmason ( avar) .
(Fusionné par Junio ​​C Hamano - gitster- in commit ac70c53 , 25 avril 2019)

gc docs: minimiser l'utilité de --aggressive

La documentation existante " gc --aggressive" est loin de 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 et qui, généralement, c'est (principalement) une perte de temps .

Clarifions donc ce qu'il fait vraiment et laissons l'utilisateur tirer ses propres conclusions.

Clarifions également le "Les effets [...] sont persistants" pour paraphraser une brève version de l'explication de Jeff King .

Cela signifie que la documentation de git-gc comprend désormais :

AGRESSIF

Lorsque l' --aggressiveoption est fournie, git-repacksera invoquée avec l' -findicateur, qui à son tour passera --no-reuse-deltaà git-pack-objects .
Cela éliminera tous les deltas existants et les recalculera, au détriment de passer beaucoup plus de temps sur le reconditionnement.

Les effets de ceci sont pour la plupart persistants, par exemple lorsque des packs et des objets en vrac sont fusionnés dans un autre pack, les deltas existants dans ce pack peuvent être réutilisés, mais il existe également divers cas où nous pourrions choisir un delta sous-optimal d'un plus récent. emballer à la place.

De plus, la fourniture --aggressivemodifiera les options --depthet --windowtransmises à git-repack.
Voir les paramètres gc.aggressiveDepthet gc.aggressiveWindowci-dessous.
En utilisant une taille de fenêtre plus grande, nous sommes plus susceptibles 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 des tests de performances personnalisés dessus .
Cela prend beaucoup plus de temps et l'optimisation espace / delta qui en résulte peut en valoir la peine ou non. Ne pas l'utiliser du tout est le bon compromis pour la plupart des utilisateurs et leurs référentiels.

Et ( commit 080a448 ):

gcdocs: notez comment les --aggressiveimpacts --windowet--depth

Depuis 07e7dbf ( gc: profondeur agressive par défaut à 50, 11/08/2016, Git v2.10.1), nous utilisons quelque peu la même profondeur --aggressiveque nous le faisons par défaut.

Comme indiqué dans ce commit qui a du sens, il était erroné de faire de plus de profondeur la valeur par défaut pour «agressif», et donc d'économiser de l'espace disque au détriment des performances d'exécution, ce qui est généralement le contraire de quelqu'un qui aimerait «gc agressif» veut.

VonC
la source