Un repository che cresce senza governo degrada in modo silenzioso: clone lenti, branch switch pesanti, pipeline che passano più tempo a scaricare che a compilare. E quando in quella history finisce una credenziale, il costo non è tecnico ma di sicurezza. Su AZ-400 la domanda non è “come si installa Git LFS”, ma “dato questo repository e questo incidente, quale intervento risolve davvero e quale sposta solo il problema”. Diamo per acquisiti i meccanismi base e concentriamoci sulle decisioni di design e sui trade-off.
Git LFS: quando conviene e cosa rompe
Git comprime e calcola differenze in modo efficiente sul testo. Su un binario che cambia interamente fra una versione e l’altra questa capacità svanisce: Git conserva ogni versione per intero. Il criterio di design non è la dimensione assoluta del file ma il prodotto dimensione per frequenza di aggiornamento: cento modifiche a un binario da 100 KB pesano quanto dieci modifiche a un file da 1 MB, e penalizzano il branching molto più spesso.
Ne discende una scala di decisione precisa. Le dipendenze non vanno nel repository ma in un feed di package management come Azure Artifacts. Gli output di build, log e diagnostica non sono sorgenti. Gli archivi compressi vanno scompattati e committati come sorgenti diffabili, lasciando a Git il compito di comprimere. I binari piccoli e raramente aggiornati restano in Git per non spezzare il workflow. Solo ciò che resta — asset grandi, non diffabili, aggiornati spesso — è candidato reale per Git LFS.
LFS committa nel repository un piccolo file di puntatore con version, oid e size, mentre il contenuto binario vive in uno storage remoto separato che il client scarica al clone e al cambio di branch. Il workflow Git resta invariato, ma i costi vanno messi sul tavolo, tra cui: ogni client deve avere LFS installato e configurato, altrimenti al clone vede il puntatore invece del binario, e un commit fatto senza client spinge il binario vero nel repository; Git non sa fare merge di due binari, per cui serve il file locking più la disciplina di aggiornare l’asset prima di lavorarci; un drag and drop dall’interfaccia web committa il binario, non il puntatore. Su Azure Repos va inoltre considerato che SSH non è supportato sui repository con file tracciati da LFS.
I numeri documentati motivano la scelta: Azure Repos indica 250 GB come tetto per repository, 10 GB come soglia consigliata, 100 MB come dimensione massima raccomandata per file e 5 GB come limite di push. Il dettaglio decisivo: gli oggetti LFS non concorrono al limite di 5 GB, che riguarda solo i file nel repository vero e proprio.
Scalare il clone: filtri, sparse-checkout e Scalar
LFS risolve il peso dei blob binari, non quello di una history profonda o di un monorepo con migliaia di directory. Qui le leve si scelgono in base a cosa vuoi evitare di scaricare.
Il partial clone agisce sugli oggetti: con un filtro il client omette i blob e li recupera on demand. Lo sparse-checkout agisce sul working directory: gli oggetti restano, ma sul disco materializzi solo le directory che ti interessano. Sono ortogonali e si combinano. Lo shallow clone, che tronca la history, è la scelta giusta per un agent di build effimero che compila un singolo commit, ma sbagliata per una workstation: perde la history necessaria a blame, bisect e merge.
Scalar è la confezione opinionata di queste leve: partial clone di soli commit e tree, sparse-checkout abilitato per default e soprattutto manutenzione in background via git maintenance, con prefetch, commit-graph e incremental repack. È l’ultimo punto a fare la differenza architetturale, perché il degrado non dipende solo dal primo clone ma dall’accumulo di loose object e packfile frammentati. Sul lato pipeline la stessa logica si applica al task di checkout, che supporta filtri di partial clone e sparse-checkout per directory o per pattern.
Regola di design: prima decidi cosa non deve entrare, poi ottimizza il modo in cui esce.
Recuperare dati: reflog, cherry-pick, revert
Le tre operazioni coprono scenari distinti, e le domande vivono sulla distinzione.
Il reflog è il registro locale di dove hanno puntato HEAD e i branch. È la rete di sicurezza dopo un reset distruttivo o un rebase andato male: il commit non è perso, è solo irraggiungibile, e dal reflog ne recuperi lo SHA. Due vincoli: è locale alla singola copia — non si clona e non si pusha — ed è soggetto a scadenza e garbage collection.
Il cherry-pick riapplica un commit specifico su un altro branch generando un nuovo SHA. È lo strumento per portare un hotfix su un branch di release senza trascinare il resto del lavoro. Il costo è la duplicazione logica: lo stesso cambiamento esiste in due punti della history, e un merge successivo può presentare conflitti. Da qui la regola: eccezioni mirate, non strategia di integrazione ordinaria.
Il revert crea un nuovo commit che annulla l’effetto di uno precedente senza riscrivere la history. È la scelta obbligata quando il commit incriminato è già su un branch condiviso, perché non invalida i cloni altrui né rompe branch policy e riferimenti. Il contrapposto è reset, che riscrive: accettabile solo su lavoro locale non pubblicato.
Rimuovere segreti: prima si ruota, poi si riscrive
L’errore concettuale più costoso è credere che riscrivere la history sia la remediation. Nel momento in cui una credenziale viene committata è compromessa: chi ha clonato prima della pulizia ne ha una copia, e nessuna riscrittura raggiunge quelle copie. La prima azione è sempre revocare e ruotare il segreto, aggiornare i servizi che lo usavano e spostarlo in un secret store come Azure Key Vault. GitHub è esplicito: la rotazione spesso rende superflua una riscrittura estesa, e il supporto interviene solo quando il rischio non è mitigabile ruotando la credenziale.
Quando la riscrittura serve comunque — dati personali, materiale non revocabile, requisiti di compliance — lo strumento raccomandato da GitHub è git-filter-repo, che nelle versioni recenti espone un flag dedicato alla rimozione di dati sensibili. Da qui nascono gli effetti collaterali che le domande amano: tutti gli SHA cambiano, le firme dei commit si perdono, branch policy e riferimenti esterni si rompono, ogni collaboratore deve riallineare il clone. Restano poi superfici fuori dal tuo controllo: viste in cache e pull request vanno rimosse aprendo un caso al supporto, e i fork non sono raggiungibili dal proprietario originale, che deve coordinarsi con chi li possiede.
Il controllo che chiude il ciclo è preventivo. GitHub Advanced Security for Azure DevOps offre secret scanning, che analizza il repository inclusa la history e genera un alert per ogni credenziale unica, e push protection, che intercetta i segreti ad alta confidenza prima che entrino, rifiutando il push da riga di comando e da web. È la leva strategica: sposta il costo da una riscrittura di history a una correzione di commit. Nota che il bypass è possibile via stringa dedicata nel messaggio di commit ma genera comunque un alert, e che alcuni token legacy possono non essere bloccati.
Trappole tipiche d’esame
- “Un team committa texture e video da centinaia di MB, aggiornati a ogni sprint; i clone sono inutilizzabili” → Git LFS con
.gitattributese file locking; non basta uno shallow clone, che non riduce il peso storico degli oggetti. - “Il push supera il limite di 5 GB anche se i file grandi sono tracciati con LFS” → verifica che
.gitattributesincluda le estensioni corrette e sia committato prima dello staging dei binari; gli oggetti LFS non contano verso quel limite. - “Monorepo enorme, ogni sviluppatore lavora su una sola area e vuole ridurre clone e disco senza perdere la history” → partial clone più sparse-checkout, confezionati da Scalar con manutenzione in background; non shallow clone, che sacrifica blame e bisect.
- “Un rebase ha distrutto commit non ancora pushati” →
git reflogper recuperare gli SHA irraggiungibili; ricorda che è locale e scade, quindi non è backup. - “Un bug è arrivato in main e va annullato senza impattare i cloni del team” →
git revert; nonreset, che riscrive una history già pubblica. - “Serve portare un solo hotfix dal branch di sviluppo a quello di release” →
git cherry-pickdel commit mirato, consapevoli della duplicazione logica. - “Una chiave di accesso committata è stata scoperta oggi” → prima ruota e revoca la credenziale e spostala in Key Vault, poi valuta git-filter-repo; la sola pulizia della history non rende sicura una credenziale esposta.
- “L’organizzazione vuole impedire strutturalmente che nuovi segreti entrino nel codice” → push protection di GitHub Advanced Security for Azure DevOps, affiancata al secret scanning sulla history esistente.