git gc --aggressive vs git repack


88

Sto cercando modi per ridurre le dimensioni di un gitrepository. La ricerca mi porta alla git gc --aggressivemaggior parte delle volte. Ho anche letto che questo non è l'approccio preferito.

Perché? di cosa dovrei essere a conoscenza se corro gc --aggressive?

git repack -a -d --depth=250 --window=250è consigliato gc --aggressive. Perché? Come repackriduce la dimensione di un repository? Inoltre, non sono abbastanza chiaro riguardo alle bandiere --depthe --window.

Cosa dovrei scegliere tra gce repack? Quando dovrei usare gce repack?

Risposte:


76

Oggi non c'è differenza: git gc --aggressiveopera secondo il suggerimento di Linus nel 2007; vedi sotto. A partire dalla versione 2.11 (Q4 2016), git ha una profondità predefinita di 50. Una finestra di dimensione 250 è buona perché scansiona una sezione più ampia di ogni oggetto, ma la profondità a 250 è pessima perché fa riferimento a ogni catena molto profonda oggetti, che rallenta tutte le future operazioni git per un utilizzo del disco leggermente inferiore.


Sfondo storico

Linus ha suggerito (vedi sotto per il post completo della mailing list) di usare git gc --aggressivesolo quando hai, nelle sue parole, "un pacco davvero pessimo" o "delta davvero orribilmente pessimi", tuttavia "quasi sempre, in altri casi, è effettivamente un pessimo cose da fare." Il risultato potrebbe persino lasciare il tuo repository in condizioni peggiori rispetto a quando hai iniziato!

Il comando che suggerisce per farlo correttamente dopo aver importato “una storia lunga e impegnativa” è

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

Ma questo presuppone che tu abbia già rimosso gunk indesiderato dalla cronologia del tuo repository e che tu abbia seguito l'elenco di controllo per la riduzione di un repository trovato nella git filter-branchdocumentazione .

git-filter-branch può essere usato per sbarazzarsi di un sottoinsieme di file, di solito con qualche combinazione di --index-filtere --subdirectory-filter. Le persone si aspettano che il repository risultante sia più piccolo dell'originale, ma sono necessari alcuni passaggi in più per renderlo effettivamente più piccolo, perché Git si sforza di non perdere i tuoi oggetti finché non glielo dici. Innanzitutto assicurati che:

  • Hai davvero rimosso tutte le varianti di un nome file, se un BLOB è stato spostato nel corso della sua durata. git log --name-only --follow --all -- filenamepuò aiutarti a trovare le rinomine.

  • Hai davvero filtrato tutti i ref: usa --tag-name-filter cat -- --allquando chiami git filter-branch.

Quindi ci sono due modi per ottenere un repository più piccolo. Un modo più sicuro è clonare, che mantiene intatto il tuo originale.

  • Clona con git clone file:///path/to/repo. Il clone non avrà gli oggetti rimossi. Vedi git-clone. (Nota che la clonazione con un percorso semplice crea solo hardlink di tutto!)

Se davvero non vuoi clonarlo, per qualsiasi motivo, controlla invece i seguenti punti (in questo ordine). Questo è un approccio molto distruttivo, quindi fai un backup o torna alla clonazione. Sei stato avvertito.

  • Rimuovi i ref originali supportati da git-filter-branch: say

    git for-each-ref --format="%(refname)" refs/original/ |
      xargs -n 1 git update-ref -d
    
  • Fai scadere tutti i reflog con git reflog expire --expire=now --all.

  • Garbage collect tutti gli oggetti non referenziati con git gc --prune=now(o se il tuo git gcnon è abbastanza nuovo da supportare gli argomenti --prune, usa git repack -ad; git pruneinvece).


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: <4aca3dc20712052111o730f6fb6h7a329ee811a70f28@mail.gmail.com>
Message-ID: <alpine.LFD.0.9999.0712052132450.13796@woody.linux-foundation.org>
References: <4aca3dc20712051947t5fbbb383ua1727c652eb25d7e@mail.gmail.com>
            <20071205.202047.58135920.davem@davemloft.net>
            <4aca3dc20712052032n521c344cla07a5df1f2c26cb8@mail.gmail.com>
            <20071205.204848.227521641.davem@davemloft.net>
            <4aca3dc20712052111o730f6fb6h7a329ee811a70f28@mail.gmail.com>

Giovedì 6 dicembre 2007, Daniel Berlin ha scritto:

In realtà, si scopre che git-gc --aggressivefa questa cosa stupida per impacchettare i file a volte indipendentemente dal fatto che tu abbia convertito da un repository SVN o meno.

Assolutamente. git --aggressiveè per lo più stupido. E 'davvero utile solo per il caso di “So di avere un davvero pacco pessimo e voglio buttare via tutte le decisioni sbagliate che ho preso".

Per spiegare questo, vale la pena spiegare (probabilmente ne sei consapevole, ma lasciami comunque passare attraverso le basi) come funzionano le catene delta di git e come sono così diverse dalla maggior parte degli altri sistemi.

In altri SCM, una catena delta è generalmente fissa. Potrebbe essere "in avanti" o "indietro" e potrebbe evolversi un po 'mentre lavori con il repository, ma generalmente è una catena di modifiche a un singolo file rappresentato come una sorta di singola entità SCM. In CVS, è ovviamente il *,vfile e molti altri sistemi fanno cose piuttosto simili.

Git esegue anche catene delta, ma le fa molto più "liberamente". Non esiste un'entità fissa. I Delta vengono generati rispetto a qualsiasi altra versione casuale che git ritenga essere un buon candidato delta (con varie euristiche abbastanza efficaci) e non ci sono assolutamente regole di raggruppamento rigide.

Questa è generalmente una cosa molto buona. È buono per varie ragioni concettuali (ad esempio , git internamente non ha mai bisogno di preoccuparsi dell'intera catena di revisione - non pensa affatto in termini di delta), ma è anche fantastico perché sbarazzarsi delle regole delta inflessibili significa quel git non ha alcun problema con l'unione di due file insieme, per esempio - semplicemente non ci sono *,v"file di revisione" arbitrari che hanno qualche significato nascosto.

Significa anche che la scelta dei delta è una domanda molto più aperta. Se limiti la catena delta a un solo file, non hai davvero molte scelte su cosa fare con i delta, ma in git può davvero essere un problema completamente diverso.

Ed è qui che --aggressiveentra in gioco il nome davvero male . Mentre git generalmente cerca di riutilizzare le informazioni delta (perché è una buona idea, e non spreca tempo della CPU per ritrovare tutti i delta buoni che abbiamo trovato in precedenza), a volte tu voglio dire "ricominciamo da capo, con una tabula rasa, ignoriamo tutte le informazioni delta precedenti e proviamo a generare una nuova serie di delta"

Quindi --aggressivenon si tratta davvero di essere aggressivi, ma di sprecare tempo della CPU per ripetere una decisione che abbiamo già fatto in precedenza!

A volte è una buona cosa. Alcuni strumenti di importazione in particolare potrebbero generare delta davvero orribilmente pessimi. Tutto ciò che usagit fast-import , ad esempio, probabilmente non ha un ottimo layout delta, quindi potrebbe valere la pena dire "Voglio iniziare da zero".

Ma quasi sempre, in altri casi, è davvero una cosa davvero brutta da fare. Sprecherà tempo per la CPU e, soprattutto se in precedenza hai fatto un buon lavoro nel delta, il risultato finale non riutilizzerà tutti quei delta buoni che hai già trovato, quindi ti ritroverai con molto anche il risultato finale peggiore!

Invierò una patch a Junio ​​per rimuovere solo la git gc --aggressive documentazione. Può essere utile, ma generalmente è utile solo quando capisci veramente a un livello molto profondo cosa sta facendo e quella documentazione non ti aiuta a farlo.

In generale, fare incrementale git gcè l'approccio giusto e meglio che farlo git gc --aggressive. Riutilizzerà i vecchi delta, e quando quei vecchi delta non possono essere trovati (il motivo per fare GC incrementale in primo luogo!) Ne creerà di nuovi.

D'altra parte, è sicuramente vero che un '"importazione iniziale di una storia lunga e impegnativa" è un punto in cui può valere la pena spendere molto tempo per trovare i delta davvero buoni . Quindi, ogni utente per sempre (a patto che non l'abbia git gc --aggressiveannullato!) Otterrà il vantaggio di quell'evento una tantum. Quindi, specialmente per i grandi progetti con una lunga storia, probabilmente vale la pena fare un po 'di lavoro extra, dicendo al delta che trova il codice di scatenarsi.

Quindi l'equivalente di git gc --aggressive- ma fatto correttamente - è fare (durante la notte) qualcosa di simile

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

dove quella cosa della profondità è solo quanto possono essere profonde le catene delta (renderle più lunghe per la vecchia storia - vale lo spazio in testa), e la cosa della finestra riguarda la dimensione della finestra dell'oggetto che vogliamo che ogni candidato delta scansiona.

E qui, potresti voler aggiungere il -fflag (che è "elimina tutti i vecchi delta", poiché ora stai effettivamente cercando di assicurarti che questo trovi effettivamente dei buoni candidati.

E poi ci vorrà un'eternità e un giorno ( cioè , una cosa da fare durante la notte). Ma il risultato finale è che tutti a valle di quel repository riceveranno pacchetti molto migliori, senza dover spendere alcuno sforzo per realizzarli.

          Linus

2
Il tuo commento sulla profondità è un po 'confuso. All'inizio volevo lamentarmi che ti sbagli di grosso, che aggressivo può velocizzare notevolmente un repository git. Dopo aver eseguito una raccolta di rifiuti aggressiva, un repository ENORME che ha impiegato cinque minuti per eseguire uno stato git ridotto a pochi secondi. Ma poi ho capito che non intendevi che il gc aggressivo avesse rallentato il repo, ma solo una dimensione di profondità estremamente grande.
user6856

57

Quando dovrei usare gc & repack?

Come ho detto in " Git Garbage collection non sembra funzionare completamente ", a git gc --aggressivenon è né sufficiente né sufficiente da solo.
E, come spiego di seguito , spesso non è necessario.

La combinazione più efficace sarebbe l'aggiunta git repack, ma anche git prune:

git gc
git repack -Ad      # kills in-pack garbage
git prune           # kills loose garbage

Nota: Git 2.11 (Q4 2016) imposterà la gc aggressiveprofondità predefinita a 50

Vedere commit 07e7dbf (11 agosto 2016) di Jeff King ( peff) .
(Fuso da Junio ​​C Hamano - gitster- in commit 0952ca8 , 21 settembre 2016)

gc: profondità aggressiva predefinita a 50

" git gc --aggressive" utilizzato per limitare la lunghezza della catena delta a 250, che è troppo profonda per ottenere un ulteriore risparmio di spazio ed è dannoso per le prestazioni di runtime.
Il limite è stato ridotto a 50.

Il riepilogo è: l'attuale impostazione predefinita di 250 non consente di risparmiare molto spazio e costa CPU. Non è un buon compromesso.

Il --aggressiveflag " " a git-gcfa tre cose:

  1. usa " -f" per eliminare i delta esistenti e ricalcolarli da zero
  2. usa "--window = 250" per cercare i delta con maggiore attenzione
  3. usa "--depth = 250" per creare catene delta più lunghe

Gli articoli (1) e (2) sono ottimi abbinamenti per un repack "aggressivo".
Chiedono al repack di fare più lavoro di calcolo nella speranza di ottenere un pack migliore. Paghi i costi durante il reimballaggio e altre operazioni vedono solo il vantaggio.

L'articolo (3) non è così chiaro.
Consentire catene più lunghe significa meno restrizioni sui delta, il che significa potenzialmente trovarne di migliori e risparmiare spazio.
Ma significa anche che le operazioni che accedono ai delta devono seguire catene più lunghe, il che influisce sulle loro prestazioni.
Quindi è un compromesso e non è chiaro se il compromesso sia nemmeno buono.

(Vedi impegno per studio )

Puoi vedere che il risparmio della CPU per le operazioni regolari migliora man mano che diminuiamo la profondità.
Ma possiamo anche vedere che il risparmio di spazio non è così grande man mano che la profondità aumenta. Risparmiare il 5-10% tra 10 e 50 probabilmente vale il compromesso della CPU. Risparmiare l'1% per passare da 50 a 100 o un altro 0,5% per passare da 100 a 250 probabilmente non lo è.


Parlando di risparmio della CPU, " git repack" ha imparato ad accettare l' --threads=<n>opzione e passarla a pack-object.

Vedi commit 40bcf31 (26 aprile 2017) di Junio ​​C Hamano ( gitster) .
(Fuso da Junio ​​C Hamano - gitster- in commit 31fb6f4 , 29 maggio 2017)

reimballare: accettare --threads=<n>e trasmetterlo apack-objects

Lo facciamo già per --window=<n>e --depth=<n>; questo aiuterà quando l'utente vuole forzare il --threads=1test riproducibile senza essere influenzato dalla corsa di più thread.


3
Ho menzionato il thread Linus nel link "Git Garbage collection non sembra funzionare completamente"
VonC

1
Grazie per questo aggiornamento moderno! Ogni altra risposta qui è vecchia. Ora possiamo vedere che git gc --aggressiveè stato risolto due volte: primo, fare ciò che Linus suggeriva nel 2007 come "metodo di imballaggio migliore". E poi in Git 2.11 per evitare l'eccessiva profondità dell'oggetto che Linus aveva suggerito ma che si è rivelata dannosa (rallenta tutte le operazioni future di Git e non risparmia spazio di cui valga la pena parlare).
gw0

git gc, seguito da git repack -Ad e git prune aumenta la dimensione del mio repository ... perché?
devops

@devops Non sono sicuro: quale versione di Git stai usando? Puoi fare una nuova domanda per questo (con maggiori dettagli come il sistema operativo, le dimensioni generali del tuo repo, ...)
VonC

man git-repackdice per -d: `Esegui anche git prune-pack per rimuovere file oggetto sciolti ridondanti. O fa git pruneanche questo? man git-prunedice In most cases, users should run git gc, which calls git prune., quindi a che serve dopo git gc? Non sarebbe meglio o sufficiente da usare solo git repack -Ad && git gc?
Jakob

14

Il problema git gc --aggressiveè che il nome dell'opzione e la documentazione sono fuorvianti.

Come spiega lo stesso Linus in questa mail , ciò che git gc --aggressivefondamentalmente fa è questo:

Mentre git generalmente cerca di riutilizzare le informazioni delta (perché è una buona idea e non spreca tempo per la CPU a ritrovare tutte le buone delta che abbiamo trovato in precedenza), a volte potresti dire "ricominciamo da capo, con un tabula rasa e ignora tutte le informazioni delta precedenti e prova a generare un nuovo insieme di delta ".

Di solito non è necessario ricalcolare i delta in git, poiché git determina che questi delta sono molto flessibili. Ha senso solo se sai di avere delta davvero, davvero pessimi. Come spiega Linus, git fast-importrientrano in questa categoria principalmente gli strumenti che fanno uso .

La maggior parte delle volte git fa un buon lavoro nel determinare i delta utili e l'utilizzo git gc --aggressiveti lascerà con delta che sono potenzialmente anche peggiori mentre sprechi molto tempo della CPU.


Linus conclude la sua mail con la conclusione che git repackcon una grande --depthed --windowè la scelta migliore nella maggior parte del tempo; soprattutto dopo che hai importato un progetto di grandi dimensioni e vuoi assicurarti che git trovi buoni delta.

Quindi l'equivalente di git gc --aggressive- ma fatto correttamente - è fare (durante la notte) qualcosa di simile

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

dove quella cosa della profondità è solo quanto possono essere profonde le catene delta (renderle più lunghe per la vecchia storia - vale lo spazio in testa), e la cosa della finestra riguarda la dimensione della finestra dell'oggetto che vogliamo che ogni candidato delta scansiona.

E qui, potresti voler aggiungere il -fflag (che è "elimina tutti i vecchi delta", poiché ora stai effettivamente cercando di assicurarti che questo trovi effettivamente dei buoni candidati.


8

Attenzione. Non eseguire git gc --agressivecon un repository che non è sincronizzato con il telecomando se non si dispone di backup.

Questa operazione ricrea i delta da zero e potrebbe portare alla perdita di dati se interrotta regolarmente.

Per il mio computer da 8 GB, gc aggressivo ha esaurito la memoria sul repository da 1 GB con 10k piccoli commit. Quando OOM killer ha terminato il processo git, mi ha lasciato con un repository quasi vuoto, solo l'albero funzionante e pochi delta sono sopravvissuti.

Ovviamente, non era l'unica copia del repository, quindi l'ho semplicemente ricreato e estratto da remoto (il recupero non ha funzionato su repository rotto e deadlock sul passaggio di 'risoluzione delta' alcune volte ho provato a farlo), ma se il tuo repository è repository locale per singolo sviluppatore senza telecomandi: eseguire prima il backup.


5

Nota: attenzione all'uso git gc --aggressive , come chiarisce Git 2.22 (Q2 2019).

Vedere commettere 0044f77 , commettere daecbf2 , commettere 7.384.504 , commettere 22d4e3b , commettere 080a448 , commettere 54d56f5 , commettere d257e0f , commettere b6a8d09 (7 Apr 2019), e impegnarsi fc559fb , commettere cf9cd77 , commettere b11e856 (22 Mar 2019) da Ævar Arnfjörð Bjarmason ( avar) .
(Fuso da Junio ​​C Hamano - gitster- in commit ac70c53 , 25 aprile 2019)

gc docs: minimizza l'utilità di --aggressive

I gc --aggressivedocumenti " " esistenti sono appena al di sotto di consigliare agli utenti di eseguirli regolarmente.
Ho parlato personalmente con molti utenti che hanno preso questi documenti come un consiglio per usare questa opzione, e di solito è (principalmente) una perdita di tempo .

Quindi chiariamo cosa fa veramente e lasciamo che l'utente tragga le proprie conclusioni.

Chiariamo anche "Gli effetti [...] sono persistenti" per parafrasare una breve versione della spiegazione di Jeff King .

Ciò significa che la documentazione di git-gc ora include :

AGGRESSIVO

Quando l' --aggressiveopzione viene fornita, git-repackverrà invocata con il -fflag, che a sua volta passerà --no-reuse-deltaagli oggetti git-pack .
Ciò eliminerà eventuali delta esistenti e li ricalcolerà, a scapito di dedicare molto più tempo al reimballaggio.

Gli effetti di questo sono per lo più persistenti, ad es. Quando pacchi e oggetti sciolti sono uniti l'uno nell'altro pacco, i delta esistenti in quel pacco potrebbero essere riutilizzati, ma ci sono anche vari casi in cui potremmo scegliere un delta subottimale da uno più recente confezionare invece.

Inoltre, la fornitura --aggressivemodificherà le opzioni --depthe --windowpassate a git-repack.
Vedere le impostazioni gc.aggressiveDepthe di gc.aggressiveWindowseguito.
Utilizzando una dimensione della finestra più grande, è più probabile trovare delta più ottimali.

Probabilmente non vale la pena utilizzare questa opzione su un dato repository senza eseguire benchmark delle prestazioni su misura su di esso .
Richiede molto più tempo e l'ottimizzazione dello spazio / delta risultante potrebbe valerne la pena o meno. Non utilizzarlo affatto è il giusto compromesso per la maggior parte degli utenti e dei loro repository.

E ( commit 080a448 ):

gcdocs: nota come --aggressiveinfluisce --windowe--depth

Da 07e7dbf ( gc: profondità aggressiva predefinita a 50, 2016-08-11, Git v2.10.1) usiamo in modo un po 'confuso la stessa profondità sotto --aggressivecome facciamo di default.

Come notato in quel commit che ha senso, era sbagliato rendere più profondo il valore predefinito per "aggressivo", e quindi risparmiare spazio su disco a scapito delle prestazioni di runtime, che di solito è l'opposto di qualcuno che vorrebbe "gc aggressivo" vuole.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.