Linus a suggéré (voir ci-dessous pour le post complet de la liste de diffusion) de n'utiliser git gc --aggressive
que 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
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
cela fait parfois cette chose stupide de compresser des fichiers, que vous ayez ou non converti à partir d'un dépôt SVN.
Absolument. git --aggressive
est 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 *,v
fichier, 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é --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 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 --aggressive
ne 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 gc
est 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 --aggressive
pour 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 -f
drapeau (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
Comme je l'ai mentionné dans " Git Garbage collection ne semble pas fonctionner complètement ", a
git gc --aggressive
n'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 aussigit prune
:Remarque: Git 2.11 (Q4 2016) définira la
gc aggressive
profondeur par défaut à 50Voir commit 07e7dbf (11 août 2016) par Jeff King (
peff
) .(Fusionné par Junio C Hamano -
gitster
- dans commit 0952ca8 , 21 septembre 2016)(Voir commit pour étude )
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)Nous le faisons déjà pour
--window=<n>
et--depth=<n>
; cela aidera lorsque l'utilisateur veut forcer--threads=1
des tests reproductibles sans être affecté par la course de plusieurs threads.la source
git gc --aggressive
a é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).man git-repack
dit pour-d
: `Exécutez également git prune-pack pour supprimer les fichiers objets redondants en vrac.` Ou fait-ilgit prune
également cela?man git-prune
ditIn most cases, users should run git gc, which calls git prune.
, alors quelle est l'utilité aprèsgit gc
? Ne serait-il pas préférable ou suffisant d'utiliser uniquementgit repack -Ad && git gc
?Le problème avec
git gc --aggressive
est que le nom de l'option et la documentation sont trompeurs.Comme l' explique Linus lui-même dans ce mail ,
git gc --aggressive
voici ce que fait fondamentalement: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-import
entrent 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 --aggressive
vous laissera avec des deltas qui sont potentiellement encore pires tout en gaspillant beaucoup de temps CPU.Linus termine son courrier avec la conclusion
git repack
qu'avec un grand--depth
et--window
est 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.la source
Mise en garde. N'exécutez pas
git gc --agressive
avec 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
la source
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)Cela signifie que la documentation de git-gc comprend désormais :
Et ( commit 080a448 ):
la source