Risoluzione dei problemi di prestazioni causati da file di grandi dimensioni in Git
Diagnostica i repository Git lenti causati da file di grandi dimensioni, scegli Git LFS con attenzione e pulisci la cronologia senza sorprendere il tuo team.
Risoluzione dei problemi di prestazioni causati da file di grandi dimensioni in Git
I file di grandi dimensioni danneggiano Git in un modo che è facile trascurare finché il repository non è già doloroso da usare. Un singolo video, archivio, dump di database o file di progettazione potrebbe non sembrare pericoloso quando qualcuno lo aggiunge. Il problema inizia quando quel file cambia più volte. Git conserva la cronologia e ogni clone deve portarsi dietro quella cronologia.
Il sintomo è di solito vago all'inizio. git clone richiede più tempo del dovuto. git fetch sembra lento su una connessione Wi-Fi in hotel. I lavori CI passano troppo tempo a fare il checkout del repository prima ancora di compilare. Gli sviluppatori iniziano a usare vecchi cloni locali perché un clone fresco è fastidioso. Questo è il momento di ispezionare il repository invece di dire a tutti di essere pazienti.
Conferma che i file di grandi dimensioni sono il problema
Inizia con controlli semplici:
du -sh .git
git count-objects -vH
du -sh .git ti dice quanto è pesante il database del repository locale. git count-objects -vH mostra gli oggetti sciolti e la dimensione del pack. Se la dimensione del pack è grande rispetto all'albero sorgente effettivo, è probabile che la cronologia stia trasportando vecchi payload.
Per trovare file di grandi dimensioni nel checkout corrente:
find . -path ./.git -prune -o -type f -size +10M -print
Questo mostra solo ciò che esiste ora. Un repository può essere lento a causa di un file che è stato eliminato mesi fa. Per ispezionare la cronologia, Git LFS fornisce un report utile anche prima di migrare qualsiasi cosa:
git lfs migrate info --everything --above=10MB
Se Git LFS non è installato, puoi comunque investigare con i comandi di basso livello di Git, ma il comando sopra è spesso la vista più diretta per questo problema specifico.
Decidi cosa appartiene a Git
Non tutti i file di grandi dimensioni sono un errore. Un piccolo insieme di asset binari stabili può andare bene. Un repository per codice infrastrutturale non dovrebbe contenere immagini di macchine virtuali, backup di database, esportazioni di clienti o artefatti di build. Un repository di giochi può legittimamente contenere asset artistici e audio, ma quei file di solito necessitano di Git LFS o di un sistema di asset separato.
Una regola pratica è questa: Git è eccellente per testo sorgente e piccoli file di supporto. Git è scarso per blob binari che cambiano frequentemente. Se il file non può essere revisionato in modo significativo in un diff e cambia spesso, probabilmente non dovrebbe vivere come un normale oggetto Git.
Candidati comuni per Git LFS includono:
*.psd
*.ai
*.mp4
*.mov
*.wav
*.zip
*.uasset
*.fbx
*.blend
Fai attenzione con pattern di immagini ampi. Tracciare ogni *.png in LFS può essere utile per un repository ricco di design, ma può essere fastidioso per un'app web con molte piccole icone. I pattern dovrebbero corrispondere ai file che effettivamente causano problemi.
Usa Git LFS per file di grandi dimensioni futuri
Git LFS memorizza un piccolo file puntatore in Git e mantiene il contenuto grande nell'archivio LFS. La cronologia normale di Git rimane più leggera, mentre gli utenti ottengono comunque il file reale nell'albero di lavoro quando LFS lo scarica.
Installalo e inizializzalo:
git lfs install
Traccia i pattern di file di cui hai effettivamente bisogno:
git lfs track "*.psd"
git lfs track "*.mp4"
git add .gitattributes
git commit -m "Traccia file di grandi dimensioni di design e video con Git LFS"
Il file .gitattributes è importante. Committalo in modo che tutti usino le stesse regole LFS.
Dopodiché, aggiungi i file normalmente:
git add demo.mp4
git commit -m "Aggiungi video demo del prodotto"
git push origin main
Un collaboratore dovrebbe installare Git LFS prima di lavorare con il repository. Se clona senza supporto LFS, potrebbe vedere file puntatore invece di asset reali finché non installa LFS ed esegue:
git lfs pull
Controlla anche la politica di archiviazione e larghezza di banda sul tuo host Git. Git LFS risolve il problema del gonfiore degli oggetti Git, ma non rende gli asset grandi gratuiti da archiviare o trasferire.
Migrazione della cronologia esistente
Abilitare LFS oggi non risolve automaticamente i commit di ieri. Se un archivio da 700 MB è stato committato e poi eliminato, può ancora vivere nella cronologia. Pulirlo richiede la riscrittura della cronologia.
La riscrittura della cronologia cambia gli ID dei commit. Chiunque abbia un clone esistente deve risincronizzarsi con attenzione, e le richieste pull aperte potrebbero dover essere rebasate o ricreate. Fallo in una finestra di manutenzione e fai prima un backup mirror:
git clone --mirror [email protected]:ORG/REPO.git repo-backup.git
Poi lavora in un clone fresco. Assicurati che l'albero di lavoro sia pulito:
git status
Ispeziona cosa verrebbe migrato:
git lfs migrate info --everything --above=10MB
Migra per pattern quando possibile:
git lfs migrate import --everything --include="*.psd,*.mp4,*.zip"
Oppure migra file sopra una soglia se il repository ha molti file grandi sconosciuti:
git lfs migrate import --everything --above=10MB
Rivedi il risultato prima di fare push:
git log --oneline --decorate -5
git lfs ls-files
git status
git lfs migrate info --everything --above=10MB
Se la migrazione ha fatto ciò che ti aspettavi, fai push dei rami e dei tag riscritti deliberatamente:
git push --force-with-lease origin main
git push --force-with-lease origin --tags
Per un repository con molti rami attivi, decidi quali rami sono importanti. Potresti non aver bisogno di riscrivere ogni ramo abbandonato, ma qualsiasi ramo che contiene ancora gli oggetti grandi può mantenere pesante il repository remoto.
Dopo una riscrittura della cronologia
Dì ai compagni di squadra esattamente cosa è cambiato. L'istruzione più pulita è spesso di fare un nuovo clone. Se le persone hanno lavoro locale, dovrebbero salvarlo prima:
git status
git branch my-work-before-lfs-migration
git fetch origin
git rebase origin/main
Per cloni locali disordinati, fare un nuovo clone è meno rischioso che cercare di riparare chirurgicamente la vecchia cronologia.
L'archiviazione remota potrebbe non ridursi immediatamente. I provider di hosting mantengono gli oggetti irraggiungibili per un po', e alcuni richiedono supporto o manutenzione del repository prima che i numeri di archiviazione si aggiornino. Localmente, puoi potare gli oggetti vecchi dopo essere sicuro che la migrazione sia buona:
git reflog expire --expire=now --all
git gc --prune=now --aggressive
Non eseguire comandi di pulizia come sostituto della revisione. Rendono più difficile recuperare vecchi oggetti locali.
Prevenire lo stesso problema di nuovo
Aggiungi un controllo pre-commit o pre-receive se file grandi accidentali continuano ad apparire. Un hook pre-commit locale può avvisare gli sviluppatori prima che committino un artefatto grande. Una regola lato server è più forte perché protegge il repository condiviso anche quando qualcuno salta gli hook locali.
Un semplice controllo locale potrebbe rifiutare file sopra una dimensione scelta a meno che non siano già tracciati da LFS. La soglia esatta dipende dal progetto. Un sito di documentazione e un progetto di gioco non dovrebbero usare lo stesso limite.
Risolve anche la fonte dei file. Se CI crea dist/, target/, report di copertura, archivi o screenshot all'interno del repository, aggiungi le voci giuste a .gitignore:
dist/
target/
coverage/
*.log
*.zip
Non ignorare i file ciecamente. Assicurati che i percorsi ignorati siano output generati, non input sorgente.
Quando LFS non è la risposta
Git LFS non è un archivio universale di artefatti. Gli output di build di solito appartengono a un registro di pacchetti, archiviazione di oggetti, asset di rilascio o archivio di artefatti CI. I dump di database appartengono all'archiviazione di backup. I grandi set di dati potrebbero aver bisogno di uno strumento di versionamento dei dati o di un flusso di lavoro di archiviazione separato.
L'obiettivo non è nascondere ogni file grande da Git. L'obiettivo è mantenere il repository abbastanza veloce che le persone possano clonare, creare rami, fare fetch e revisionare senza combattere lo strumento.
Una buona pulizia lascia tre cose: regole chiare in .gitattributes per i file che appartengono a LFS, regole in .gitignore per i file che non dovrebbero mai essere committati, e una breve nota di squadra che spiega come i cloni esistenti dovrebbero risincronizzarsi. Questo è ciò che impedisce alla correzione di diventare una pulizia una tantum che ripeti il trimestre successivo.