O comando que ele sugere para fazer isso corretamente após ter importado "uma longa e complicada história" é
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]>
Na quinta-feira, 6 de dezembro de 2007, Daniel Berlin escreveu:
Na verdade, acontece que git-gc --aggressive
isso faz essa coisa idiota para empacotar arquivos às vezes, independentemente de você ter convertido de um repositório SVN ou não.
Absolutamente. git --aggressive
é principalmente burro. É realmente útil apenas para o caso de "Eu sei que tenho uma embalagem muito ruim e quero jogar fora todas as decisões ruins de embalagem que tomei."
Para explicar isso, vale a pena explicar (você provavelmente está ciente disso, mas deixe-me passar pelo básico de qualquer maneira) como as cadeias delta git funcionam e como elas são tão diferentes da maioria dos outros sistemas.
Em outros SCMs, uma cadeia delta é geralmente fixa. Pode ser “para frente” ou “para trás” e pode evoluir um pouco conforme você trabalha com o repositório, mas geralmente é uma cadeia de alterações em um único arquivo representado como algum tipo de entidade SCM única. No CVS, é obviamente o *,v
arquivo, e muitos outros sistemas fazem coisas bastante semelhantes.
Git também faz cadeias delta, mas as faz muito mais "vagamente". Não há entidade fixa. Deltas são gerados contra qualquer outra versão aleatória que git considere ser um bom candidato delta (com várias heurísticas razoavelmente bem sucedidas), e não há absolutamente nenhuma regra rígida de agrupamento.
Em geral, isso é muito bom. É bom por várias razões conceituais ( ou seja , git internamente nunca realmente precisa se preocupar com toda a cadeia de revisão - ele realmente não pensa em termos de deltas), mas também é ótimo porque se livrar das regras inflexíveis de delta significa que o git não tem nenhum problema com a fusão de dois arquivos, por exemplo - simplesmente não existem *,v
“arquivos de revisão” arbitrários que tenham algum significado oculto.
Isso também significa que a escolha de deltas é uma questão muito mais aberta. Se você limitar a cadeia delta a apenas um arquivo, não terá muitas opções de escolha sobre o que fazer com os deltas, mas no git pode ser um problema totalmente diferente.
E é aí que --aggressive
entra o nome realmente ruim . Enquanto o git geralmente tenta reutilizar as informações delta (porque é uma boa ideia e não desperdiça tempo de CPU reencontrando todos os deltas bons que encontramos anteriormente), às vezes você quero dizer “vamos começar tudo de novo, com uma tela em branco, e ignorar todas as informações delta anteriores e tentar gerar um novo conjunto de deltas”.
Portanto, --aggressive
não se trata realmente de ser agressivo, mas de desperdiçar tempo de CPU refazendo uma decisão que já tomamos antes!
Às vezes, isso é uma coisa boa. Algumas ferramentas de importação em particular podem gerar deltas terrivelmente ruins. Qualquer coisa que use git fast-import
, por exemplo, provavelmente não tem um ótimo layout delta, então pode valer a pena dizer “Eu quero começar do zero”.
Mas quase sempre, em outros casos, é realmente uma coisa muito ruim de se fazer. Isso vai desperdiçar tempo de CPU e, especialmente se você realmente fez um bom trabalho no delta antes, o resultado final não vai reutilizar todos os bons deltas que você já encontrou, então você realmente vai acabar com muito pior resultado final também!
Vou enviar um patch para o Junio apenas para remover a git gc --aggressive
documentação. Pode ser útil, mas geralmente só é útil quando você realmente entende em um nível muito profundo o que está fazendo, e essa documentação não ajuda você a fazer isso.
Geralmente, fazer incrementais git gc
é a abordagem certa e melhor do que fazer git gc --aggressive
. Ele vai reutilizar deltas antigos e, quando esses deltas antigos não puderem ser encontrados (a razão para fazer GC incremental em primeiro lugar!), Ele criará novos.
Por outro lado, é definitivamente verdade que uma “importação inicial de uma história longa e envolvente” é um ponto em que vale a pena gastar muito tempo procurando deltas realmente bons . Então, todo usuário desde então (contanto que não use git gc --aggressive
para desfazê-lo!) Terá a vantagem desse evento único. Então, especialmente para grandes projetos com uma longa história, provavelmente vale a pena fazer algum trabalho extra, dizendo ao delta que localiza o código para enlouquecer.
Portanto, o equivalente a git gc --aggressive
- mas feito corretamente - é fazer (durante a noite) algo como
git repack -a -d --depth=250 --window=250
onde aquela coisa de profundidade é apenas sobre quão profundas as cadeias delta podem ser (torná-las mais longas para a história antiga - vale a pena o espaço acima), e a coisa de janela é sobre o tamanho da janela de um objeto que queremos que cada candidato delta examine.
E aqui, você pode querer adicionar o -f
sinalizador (que é “largar todos os deltas antigos”, já que agora você está realmente tentando ter certeza de que este realmente encontre bons candidatos.
E então vai levar uma eternidade e um dia ( ou seja , algo do tipo “faça durante a noite”). Mas o resultado final é que todos os downstream desse repositório receberão pacotes muito melhores, sem ter que gastar nenhum esforço com eles.
Linus
Como mencionei em " A coleta de lixo do Git não parece funcionar totalmente ", a
git gc --aggressive
não é suficiente ou mesmo suficiente por si só.E, como explico abaixo , muitas vezes não é necessário.
A combinação mais eficaz seria adicionar
git repack
, mas tambémgit prune
:Nota: Git 2.11 (Q4 2016) irá definir a
gc aggressive
profundidade padrão para 50Veja o commit 07e7dbf (11 de agosto de 2016) de Jeff King (
peff
) .(Fundido por Junio C Hamano -
gitster
- no commit 0952ca8 , 21 de setembro de 2016)(Veja commit para estudo )
Falando em economia de CPU, "
git repack
" aprendi a aceitar a--threads=<n>
opção e passá-la para objetos de pacote.Consulte o commit 40bcf31 (26 de abril de 2017) de Junio C Hamano (
gitster
) .(Incorporado por Junio C Hamano -
gitster
- no commit 31fb6f4 , 29 de maio de 2017)Já o fazemos para
--window=<n>
e--depth=<n>
; isso ajudará quando o usuário quiser forçar--threads=1
um teste reproduzível sem ser afetado pela corrida de vários threads.fonte
git gc --aggressive
foi consertado duas vezes: primeiro, para fazer o que Linus sugeriu em 2007 como um "método de embalagem melhor". E então no Git 2.11 para evitar a profundidade excessiva do objeto que Linus havia sugerido, mas que acabou sendo prejudicial (retarda todas as operações futuras do Git e não economiza nenhum espaço que valha a pena falar).man git-repack
diz para-d
: `Também execute git prune -pack para remover arquivos redundantes de objetos soltos`. Ougit prune
também faz isso?man git-prune
dizIn most cases, users should run git gc, which calls git prune.
, então qual é a utilidade depoisgit gc
? Não seria melhor ou suficiente usar apenasgit repack -Ad && git gc
?O problema
git gc --aggressive
é que o nome da opção e a documentação são enganosos.Como o próprio Linus explica neste e-mail , o que
git gc --aggressive
basicamente faz é isso:Normalmente não há necessidade de recalcular deltas no git, já que o git determina esses deltas muito flexíveis. Só faz sentido se você souber que tem deltas muito, muito ruins. Como Linus explica, principalmente as ferramentas que fazem uso se
git fast-import
enquadram nessa categoria.Na maioria das vezes, o git faz um bom trabalho na determinação de deltas úteis e o uso
git gc --aggressive
deixará você com deltas que são potencialmente ainda piores, ao mesmo tempo que desperdiça muito tempo de CPU.Linus termina sua correspondência com a conclusão de que
git repack
com uma grande--depth
e--window
é a melhor escolha na maioria das vezes; especialmente depois que você importou um grande projeto e deseja ter certeza de que o git encontra bons deltas.fonte
Cuidado. Não execute
git gc --agressive
com o repositório que não está sincronizado com o remoto se você não tiver backups.Esta operação recria deltas do zero e pode levar à perda de dados se for interrompida normalmente.
Para o meu computador de 8 GB agressivo, o gc ficou sem memória no repositório de 1 GB com 10k pequenos commits. Quando o OOM killer encerrou o processo git - ele me deixou com o repositório quase vazio, apenas a árvore de trabalho e alguns deltas sobreviveram.
Claro, não era a única cópia do repositório, então eu apenas o recriei e extraí do remoto (fetch não funcionou em repo quebrado e travou na etapa de 'resolução de deltas' algumas vezes eu tentei fazer isso), mas se o seu repo é repositório local de desenvolvedor único sem nenhum controle remoto - faça backup primeiro.
fonte
Observação: cuidado ao usar
git gc --aggressive
, conforme esclarece o Git 2.22 (segundo trimestre de 2019).Consulte o commit 0044f77 , o commit daecbf2 , o commit 7384504 , o commit 22d4e3b , o commit 080a448 , o commit 54d56f5 , o commit d257e0f , o commit b6a8d09 (07 abr 2019) e o commit fc559fb , o commit cf9cd77 , o commit b11e856 (22 Marvar Arnmason
avar
) de .(Fundido por Junio C Hamano -
gitster
- no commit ac70c53 , 25 de abril de 2019)Isso significa que a documentação do git-gc agora inclui :
E ( commit 080a448 ):
fonte