gestor-tasques ha recorregut un camí llarg. Té missatges de confirmació que expliquen el perquè, un historial net amb una política d'integració acordada, els fitxers correctes a dins i ben tractats, i els secrets a fora. Queda un últim problema del mòdul, molt menys dramàtic que l'anterior però cada cop més molest:
"Cada
git statustriga tres segons. El clon del repositori pesa 900 MB i el codi en són quatre. I ahir, clonar al portàtil nou de la Carla va trigar vuit minuts."
Un repositori lent no trenca res, però degrada silenciosament tota la resta: si git status triga tres segons, deixes d'executar-lo; si el clon triga vuit minuts, el CI es torna car; si git log s'arrossega, deixes de consultar l'historial. La lentitud erosiona els hàbits que hem construït durant tot el mòdul.
Aquesta lliçó ensenya a mesurar abans que a optimitzar, i després a actuar sobre allò que la mesura assenyali. Se centra en el repositori de mida normal que s'ha tornat lent per descuit. Els repositoris veritablement enormes i els monorepos tenen tècniques pròpies —clons parcials, clons superficials, índex dispers— i són el contingut de la lliçó 10-04.
Contingut
- Mesurar abans d'optimitzar
git count-objects -vH: la foto del repositori- Trobar els objectes més grans de l'historial
- Empaquetatge: objectes solts i packfiles
git gc: què fa realmentgit maintenance: el substitut modern- Accelerar
git status:fsmonitoriuntrackedCache feature.manyFilesi altres ajustos- El cost dels fitxers binaris grans
- Higiene de referències:
pruneipacked-refs - Altres ordres lentes i les seves causes
- Bons costums que eviten el problema
- Mesurar abans d'optimitzar
La regla és la mateixa que en qualsevol optimització: la intuïció sobre què és lent és gairebé sempre equivocada. Abans de tocar res, mesura.
Git porta un mecanisme de traces que diu exactament on se'n va el temps:
# Traça bàsica: temps per etapa
GIT_TRACE=1 git status
# Traça de rendiment, molt més detallada
GIT_TRACE_PERFORMANCE=1 git status12:04:31.882 read-cache.c:2402 performance: 0.412 s: read cache .git/index 12:04:32.741 name-hash.c:610 performance: 0.856 s: init name hash 12:04:34.102 dir.c:2419 performance: 1.361 s: directory traversal 12:04:34.180 trace.c:487 performance: 2.298 s: git command: git status
Aquí hi ha el diagnòstic complet: 1,36 segons recorrent directoris i 0,41 llegint l'índex. El problema no és l'historial: és la quantitat de fitxers a la còpia de treball. Això apunta a l'apartat 7, no al 5.
Una manera més estructurada, amb la traça moderna en format llegible:
I per comparar abans i després d'un canvi, mesura diverses vegades:
Què mesurar en cada símptoma
| Símptoma | Causa probable | Apartat |
|---|---|---|
git status lent |
Molts fitxers a la còpia de treball | 7 |
git clone lent o clon enorme |
Objectes grans a l'historial | 3, 9 |
git log lent |
Historial molt llarg, o mal empaquetat | 4, 5 |
git fetch/push lent |
Moltes referències remotes obsoletes | 10 |
| Tot una mica lent després de molta activitat | Molts objectes solts sense empaquetar | 4, 5, 6 |
git checkout/switch lent |
Molts fitxers, o fitxers grans | 7, 9 |
git count-objects -vH: la foto del repositori
git count-objects -vH: la foto del repositoriÉs la primera ordre que cal executar. Dóna l'estat de la base d'objectes en una pantalla:
count: 8432 size: 156.42 MiB in-pack: 214893 packs: 7 size-pack: 743.18 MiB prune-packable: 0 garbage: 0 size-garbage: 0 bytes
Com s'ha de llegir:
| Camp | Què significa | Quan preocupa |
|---|---|---|
count |
Objectes solts (un fitxer per objecte a .git/objects/XX/) |
Més d'uns quants milers: falta empaquetar |
size |
Espai que ocupen els solts | Si és una fracció important del total |
in-pack |
Objectes dins de packfiles | Informatiu |
packs |
Nombre de packfiles | Més de 5 o 10: convé consolidar |
size-pack |
Espai dels packfiles | La xifra real del repositori |
prune-packable |
Solts que ja són també dins d'un pack (duplicats) | Qualsevol valor alt: el gc els neteja |
garbage |
Fitxers que Git no reconeix dins de .git/objects |
Diferent de 0: ha passat alguna cosa estranya |
A l'exemple: 743 MB de packfiles per a un projecte el codi del qual ocupa 4 MB. Aquí hi ha el problema, i l'apartat 3 diu d'on ve.
Comparació amb la mida real:
# El que ocupa el .git sencer
du -sh .git
# El que ocupa la còpia de treball (sense .git)
du -sh --exclude=.git .Una relació de 200 a 1 entre l'historial i el contingut actual és un senyal inequívoc que a l'historial hi ha alguna cosa que no hi hauria de ser.
- Trobar els objectes més grans de l'historial
Aquest és el diagnòstic clau, i val la pena entendre'l peça a peça. La recepta combina dues ordres de baix nivell que ja coneixes de la lliçó 01-04.
#!/usr/bin/env bash
# objectes-grans.sh — els N blobs més grans de l'historial, amb la seva ruta
#
# Com funciona:
# 1. rev-list --objects --all
# Llista TOTS els objectes assolibles des de qualsevol referència,
# amb el format "<sha> <ruta>". La ruta només apareix en blobs i arbres.
# 2. cat-file --batch-check
# Rep SHA per l'entrada estàndard i, per a cadascun, imprimeix el
# format demanat sense abocar el contingut (que seria caríssim).
# %(rest) retorna el que venia després del SHA: la ruta.
# 3. awk / sort / head
# Es queda amb els blobs, ordena per mida i mostra els més grans.
N=${1:-20}
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize:disk) %(objectsize) %(rest)' \
| awk '$1 == "blob" { print $3, $4, $2, $5 }' \
| sort -rn \
| head -n "$N" \
| while read -r al_disc real sha ruta; do
printf '%8s al disc %8s real %s %s\n' \
"$(numfmt --to=iec --suffix=B "$al_disc")" \
"$(numfmt --to=iec --suffix=B "$real")" \
"${sha:0:10}" "$ruta"
done214MiB al disc 240MiB real a1b2c3d4e5 demo/video-presentacio.mp4 96MiB al disc 112MiB real b2c3d4e5f6 disseny/mockups.psd 84MiB al disc 84MiB real c3d4e5f6a7 dades/bolcat-proves.sql 71MiB al disc 88MiB real d4e5f6a7b8 demo/video-presentacio.mp4 52MiB al disc 52MiB real e5f6a7b8c9 disseny/mockups.psd 31MiB al disc 36MiB real f6a7b8c9d0 demo/captures.zip 2.1MiB al disc 8.4MiB real a7b8c9d0e1 node_modules/.package-lock.json 1.8MiB al disc 1.9MiB real b8c9d0e1f2 package-lock.json
El diagnòstic salta a la vista:
video-presentacio.mp4apareix dues vegades, amb 214 i 71 MB. Són dues versions del mateix fitxer: algú el va actualitzar i Git en desa les dues senceres.mockups.psdtambé apareix dues vegades. El mateix.- Un bolcat de base de dades de 84 MB que mai no s'hauria d'haver versionat.
- I un
node_modules/.package-lock.json, resta de la contaminació que vam netejar a la lliçó 08-03.
Les dues mides que imprimeix l'script són importants:
objectsize(real): la mida del contingut descomprimit.objectsize:disk(al disc): el que ocupa realment dins del packfile, després de comprimir i d'aplicar deltes.
Que s'assemblin en els vídeos i les imatges ho diu tot: els formats ja comprimits no es comprimeixen més ni admeten deltes útils. És l'apartat 9.
Variants útils
# Només allò assolible des de HEAD (el que un clon nou s'enduria de la branca actual)
git rev-list --objects HEAD \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '$1=="blob" {print $3, $4}' | sort -rn | head
# Sumar per extensió: quin tipus de fitxer pesa més?
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '$1=="blob" && $4 != "" {
n = split($4, p, "."); ext = (n > 1) ? p[n] : "(sense ext)";
suma[ext] += $3
}
END { for (e in suma) printf "%12d %s\n", suma[e], e }' \
| sort -rn | head -15Aquesta vista per extensió és la més útil per prendre decisions: diu quina categoria de fitxer està inflant el repositori.
Què cal fer amb el que hi trobis
Localitzar el problema és la meitat de la feina; l'altra meitat és l'apartat 9, i anticipa la conclusió: esborrar el fitxer de la punta no allibera res. Treure'l de l'historial exigeix la mateixa reescriptura que vam veure a la lliçó 08-05, amb les mateixes conseqüències per a tot l'equip.
- Empaquetatge: objectes solts i packfiles
Per entendre el gc cal entendre com emmagatzema Git els objectes. Hi ha dues maneres:
Objectes solts
Cada objecte és un fitxer independent dins de .git/objects/, comprimit amb zlib, en un directori anomenat pels dos primers caràcters del seu SHA:
És el format que Git fa servir en crear objectes nous: ràpid d'escriure, molt ineficient d'emmagatzemar. Si modifiques una línia d'un fitxer d'1 MB i confirmes, es crea un blob nou i complet d'1 MB.
Packfiles
Un packfile és un únic fitxer que conté molts objectes, amb dues optimitzacions:
- Compressió conjunta, més eficaç que comprimir cada objecte per separat.
- Deltes: en lloc de desar cada versió sencera, Git en desa una de completa i les altres com a diferències respecte a aquella.
flowchart LR
subgraph S["Objectes solts"]
A1["blob v1<br/>1 MB"]
A2["blob v2<br/>1 MB"]
A3["blob v3<br/>1 MB"]
end
subgraph P["Packfile"]
B1["blob v3 complet<br/>1 MB"]
B2["delta v2 ← v3<br/>2 KB"]
B3["delta v1 ← v2<br/>3 KB"]
end
S -->|"git gc"| P
De 3 MB a poc més d'1 MB. Amb un historial de centenars de versions d'un fitxer de text, l'estalvi és d'un o dos ordres de magnitud.
Detalls que convé conèixer:
- Git fa la delta contra l'objecte més semblant, no necessàriament contra la versió anterior. Pot encadenar deltes (amb un límit de profunditat configurable).
- Les deltes es calculen per contingut, no per història. Dos fitxers semblants de commits llunyans poden servir de base l'un de l'altre.
- Els formats ja comprimits (JPG, PNG, MP4, ZIP, PSD) no admeten deltes útils. Un canvi mínim a la imatge canvia tot el flux de bytes comprimit. Per això a l'apartat 3 la mida al disc i la real coincidien.
# Veure els packfiles i el seu contingut
ls -lh .git/objects/pack/
# Estadístiques d'un packfile: profunditat de les cadenes de delta
git verify-pack -v .git/objects/pack/pack-*.idx | tail -5Quan es crea un packfile: en fer git gc (manual o automàtic), en fer git clone o git fetch (el que viatja per la xarxa sempre és un pack), i en arribar als llindars de gc.auto.
git gc: què fa realment
git gc: què fa realmentgit gc (garbage collection) és l'ordre de manteniment clàssica. Fa cinc coses:
- Empaqueta els objectes solts en packfiles.
- Consolida diversos packfiles en menys.
- Elimina objectes inassolibles que hagin superat el període de gràcia.
- Empaqueta les referències a
.git/packed-refs(apartat 10). - Fa caducar les entrades antigues del reflog (90 dies per defecte per a allò assolible, 30 per a la resta).
# Abans # Després count: 8432 count: 0 size: 156.42 MiB size: 0 bytes in-pack: 214893 in-pack: 223325 packs: 7 packs: 1 size-pack: 743.18 MiB size-pack: 698.44 MiB
Els 8.432 objectes solts han desaparegut, els 7 packfiles s'han consolidat en 1, i el total ha baixat. Fixa't que continua havent-hi 698 MB: el gc reorganitza i comprimeix, però no pot eliminar objectes que continuen sent assolibles des d'alguna referència. Els vídeos de l'apartat 3 continuen allà perquè són en commits que continuen a l'historial.
gc automàtic
Git ja executa gc --auto pel seu compte després de certes ordres (commit, merge, rebase, receive-pack). Només actua si se superen uns llindars:
# Llindars, amb els seus valors per defecte
git config --get gc.auto # 6700 objectes solts
git config --get gc.autoPackLimit # 50 packfiles
# Ajustar-los
git config --global gc.auto 256
# Desactivar el gc automàtic (si prefereixes git maintenance, apartat 6)
git config --global gc.auto 0--aggressive: per què gairebé mai no cal
--aggressive descarta les deltes existents i ho recalcula tot des de zero, amb una finestra de cerca molt més gran. És extremadament car i el benefici sol ser marginal, perquè les deltes que ja hi havia eren raonablement bones.
| Situació | --aggressive? |
|---|---|
| Manteniment periòdic | No. git gc normal, o millor git maintenance |
Després d'un filter-repo que ho va reescriure tot |
Sí, una vegada |
| Després d'importar des d'un altre sistema de control de versions | Sí, una vegada |
| "Per si de cas", cada setmana | No. Hores de CPU per a res |
| El repositori va lent i no sé per què | No. Mesura primer (apartat 1) |
Si de debò vols un reempaquetatge a fons, això és més controlable que --aggressive:
-a: tot en un sol pack.-d: esborra els packs vells.-f: recalcula les deltes.--window: quants objectes considerar com a base de delta (més = millor i més lent).--depth: longitud màxima de la cadena de deltes (més = més petit i més lent de llegir).
L'advertiment important
El gc pot eliminar objectes inassolibles, i amb ells la possibilitat de recuperar commits perduts:
Després d'aquestes dues ordres, un commit que haguessis perdut amb un reset desafortunat ja no es pot recuperar. La recuperació de commits perduts i el paper del reflog són el contingut de la lliçó 09-04; fins que no l'hagis vista, no executis --prune=now llevat que sàpigues exactament què estàs fent (per exemple, després del filter-repo de la lliçó 08-05, on és precisament el que es busca).
git maintenance: el substitut modern
git maintenance: el substitut modernDes de Git 2.29 existeix git maintenance, pensat per substituir el gc manual i automàtic. Els seus avantatges:
- Es programa en segon pla, així que no bloqueja les teves ordres.
- Tasques separades amb freqüències diferents, en lloc d'un
gcmonolític. - Incremental: reempaqueta a poc a poc en lloc de tot de cop.
- No fa caducar el reflog per sorpresa.
Això registra el repositori i crea les tasques programades del sistema (cron, systemd, launchd o el programador de tasques de Windows, segons la plataforma). A partir d'aquell moment:
| Tasca | Freqüència | Què fa |
|---|---|---|
prefetch |
Cada hora | Descarrega objectes del remot en segon pla; els teus fetch posteriors són gairebé instantanis |
commit-graph |
Cada hora | Actualitza el graf de commits, que accelera moltíssim log, merge-base i els càlculs d'assolibilitat |
loose-objects |
Cada dia | Empaqueta objectes solts de manera incremental |
incremental-repack |
Cada dia | Consolida packfiles a poc a poc |
gc |
Desactivada | La substitueixen les tasques anteriors |
# Veure què hi ha registrat
git config --get-all maintenance.repo --global
# Executar una tasca manualment
git maintenance run --task=commit-graph
git maintenance run --task=incremental-repack
# Executar-ho tot ara
git maintenance run
# Desactivar-ho
git maintenance stop
# Treure el repositori del registre
git maintenance unregisterEl commit-graph, la joia amagada
És probablement la millora més rendible d'aquesta lliçó. El fitxer commit-graph és un índex amb la informació estructural dels commits —pares, dates, números de generació— que evita haver de llegir i descomprimir cada objecte de commit per recórrer l'historial.
# Generar-lo a mà
git commit-graph write --reachable
# I que es mantingui sol
git config --global fetch.writeCommitGraph trueL'efecte en un repositori amb historial llarg:
# Sense commit-graph
time git log --oneline --graph --all > /dev/null # 4,8 s
# Amb commit-graph
time git log --oneline --graph --all > /dev/null # 0,3 sAccelera git log --graph, git merge-base, git branch --contains, git tag --contains, git bisect (lliçó 06-02) i tot el que necessiti calcular assolibilitat. És gratis i no té contrapartides.
La configuració recomanada
# Manteniment modern en lloc de gc manual
git maintenance start
git config --global gc.auto 0 # que el gc automàtic no interfereixi
git config --global fetch.writeCommitGraph true
- Accelerar
git status: fsmonitor i untrackedCache
git status: fsmonitor i untrackedCacheAquest és el problema de la Carla, i val la pena entendre'l perquè la causa no és la que la gent suposa.
Per què git status és lent
git status ha de respondre a tres preguntes:
- Quins fitxers seguits han canviat? → comparar l'índex amb el disc.
- Què hi ha preparat? → comparar l'índex amb
HEAD. - Quins fitxers sense seguiment hi ha? → recórrer tots els directoris de la còpia de treball.
La tercera és la cara. Recórrer 40.000 fitxers —node_modules inclòs, encara que estigui ignorat, perquè Git ha de mirar per saber que l'ignora— costa milers de crides al sistema. A Linux és ràpid; a Windows i macOS, notablement més lent, i aquesta és la raó que la Carla ho pateixi més que l'Ana.
core.fsmonitor (Git 2.37+)
En lloc de recórrer l'arbre, Git pregunta al sistema de vigilància de fitxers del sistema operatiu què ha canviat des de l'última vegada. Des de Git 2.37 hi ha un monitor integrat, sense eines externes:
La primera execució arrenca un dimoni en segon pla:
L'efecte és dràstic:
core.untrackedCache
Desa a la memòria cau el resultat del recorregut de directoris, fent servir la marca de temps de modificació de cada directori per saber quins cal tornar a mirar.
# Comprovar si el teu sistema de fitxers ho admet
git update-index --test-untracked-cache
# Activar-ho
git config core.untrackedCache trueRequereix que el sistema de fitxers actualitzi de manera fiable el mtime dels directoris. L'ordre de prova ho verifica; si falla, no ho activis.
Quan ajuda cadascun
| Ajust | Ajuda quan | No ajuda quan | Cost |
|---|---|---|---|
core.fsmonitor |
Molts fitxers a la còpia de treball (>10.000); Windows o macOS | Repositoris petits; sistemes de fitxers de xarxa | Un dimoni en segon pla per repositori |
core.untrackedCache |
Molts directoris amb fitxers sense seguiment | El sistema de fitxers no dóna un mtime fiable de directoris |
Una mica més de mida d'índex |
index.version 4 |
Índexs molt grans (comprimeix els noms de ruta) | Índexs petits | Incompatible amb versions de Git molt antigues |
core.preloadIndex |
Sistemes amb diversos nuclis (actiu per defecte) | — | Cap |
| Reduir fitxers | Sempre | — | Requereix canviar el projecte |
L'última fila és la que més rendeix i la que menys s'aplica: si node_modules té 40.000 fitxers, cap optimització de Git no serà tan bona com no tenir-los. Els sistemes de dependències moderns amb magatzem central i enllaços redueixen molt aquest nombre.
Un truc senzill per mesurar quant costa el recorregut de fitxers sense seguiment:
Si la diferència és gran, el teu problema és el recorregut de directoris i fsmonitor és la solució.
feature.manyFiles i altres ajustos
feature.manyFiles i altres ajustosGit agrupa configuracions recomanades en "macros" de funcionalitat:
Equival a activar de cop:
| Ajust | Efecte |
|---|---|
index.version 4 |
Format d'índex comprimit: índex més petit i ràpid de llegir |
core.untrackedCache true |
La memòria cau de l'apartat 7 |
index.skipHash true |
Omet el càlcul del hash de l'índex en escriure'l (Git 2.40+) |
Està pensat per a còpies de treball amb molts fitxers. No toca l'historial: no ajuda amb un repositori gran per la seva història, només pel seu nombre de fitxers actuals.
Altres ajustos amb bona relació benefici/cost:
# Escriure el commit-graph en fer fetch (apartat 6)
git config --global fetch.writeCommitGraph true
# Escriure l'índex de bitmaps en reempaquetar: accelera molt clone i fetch
git config --global repack.writeBitmaps true
# Paral·lelitzar la compressió amb tots els nuclis
git config --global pack.threads 0
# Limitar la memòria que fa servir l'empaquetatge (útil en màquines amb poca RAM)
git config --global pack.windowMemory 256m
git config --global pack.packSizeLimit 2gI un ajust que no hauries de tocar a la lleugera:
Fa que el repositori ocupi força més a canvi d'una millora marginal. Només té sentit en casos molt concrets, i gairebé mai en el teu.
- El cost dels fitxers binaris grans
Tornem al diagnòstic de l'apartat 3: 357 MB de vídeos i 154 MB de fitxers PSD. Aquest és el problema estructural de gestor-tasques, i cal entendre bé per què és tan greu.
Per què fan tant de mal
1. No es comprimeixen. Un MP4 o un PSD ja estan comprimits. Git hi aplica zlib al damunt i el resultat és pràcticament la mateixa mida.
2. No admeten deltes útils. Canviar un fotograma d'un vídeo altera tot el flux de bytes comprimit posterior. Git no hi troba cap similitud aprofitable i desa cada versió completa.
3. Cada versió es desa sencera. Un vídeo de 200 MB actualitzat cinc vegades són 1 GB a l'historial, per sempre.
4. Tothom se'l descarrega. git clone porta l'historial complet. La Carla es descarrega els cinc vídeos encara que només necessiti l'últim. El CI també, a cada execució que no faci servir memòria cau.
5. No es pot fusionar. Un conflicte en un binari només es resol triant una versió sencera (lliçó 08-04, atribut binary).
Comparació amb un fitxer de text:
app.js (200 KB, 500 versions) |
video.mp4 (200 MB, 5 versions) |
|
|---|---|---|
| Contingut lògic | 100 MB | 1.000 MB |
| Espai real al pack | ~3 MB (deltes) | ~1.000 MB (sense deltes) |
| Cost del clon | Menyspreable | Minuts |
Per què no s'arregla esborrant-los
git rm demo/video-presentacio.mp4
git commit -m "chore: elimina el vídeo de la demo"
git count-objects -vH # la mida NO baixaÉs exactament el mateix mecanisme que vam veure amb els secrets a la lliçó 08-05: el commit nou no inclou el fitxer, però els commits anteriors continuen referenciant els blobs, que continuen sent assolibles i per tant el gc no els toca mai.
Per recuperar l'espai de debò cal reescriure l'historial:
# Sobre un clon --mirror, amb còpia de seguretat prèvia (lliçó 08-05)
git filter-repo --path demo/ --path disseny/ --path dades/bolcat-proves.sql --invert-paths
# O per mida: elimina qualsevol blob de més de 10 MB
git filter-repo --strip-blobs-bigger-than 10MAmb exactament les mateixes conseqüències que a la lliçó anterior: tots els SHA canvien, tots els clons queden obsolets, cal coordinar-se amb l'equip, i els forks (el del Diego) no es netegen sols. Reescriure l'historial per estalviar espai és una decisió seriosa, no una tasca de manteniment. Gairebé sempre convé més aprendre la lliçó i no tornar-ho a fer.
La solució correcta: Git LFS
Per a binaris que sí que cal versionar —dissenys, recursos gràfics d'un joc, documents mestres—, la resposta és Git LFS (Large File Storage). Recorda de l'apartat 13 de la lliçó 08-04 que s'activa amb un filter al .gitattributes:
El repositori desa un punter de text de 130 bytes en lloc del fitxer, i el contingut viu en un magatzem a part que només es descarrega quan cal.
El mecanisme complet, el servidor d'emmagatzematge, els costos, git lfs migrate per convertir un historial existent i les limitacions que cal conèixer abans d'adoptar-lo són el contingut de la lliçó 10-03: Git LFS per a Fitxers Grans.
- Higiene de referències:
prune i packed-refs
prune i packed-refsLes referències —branques, etiquetes, branques de seguiment— són fitxers de 41 bytes (lliçó 03-01). Individualment no pesen res; acumulades per milers, sí que importen.
Branques remotes obsoletes
Quan algú esborra una branca al servidor, la teva còpia local d'aquella referència no desapareix sola:
1.247 branques remotes, de les quals potser 8 continuen existint. Cada fetch les processa i cada git branch -a les llista.
# Veure què s'eliminaria, sense fer-ho
git remote prune origin --dry-run
# Netejar
git fetch --prune
# I que sigui el comportament per defecte, per sempre
git config --global fetch.prune true
git config --global fetch.pruneTags false # amb les etiquetes, millor ser conservadorReprenent el que vam veure a la lliçó 03-06: fetch.prune true hauria de ser a la configuració global de tothom. És una línia que evita una acumulació silenciosa que ningú no mira mai.
I les branques locals ja fusionades:
# Veure quines estan completament integrades a main
git branch --merged main | grep -vE '^\*|main|develop'
# Esborrar-les
git branch --merged main | grep -vE '^\*|main|develop' | xargs -r git branch -dCompte amb
--mergedsi la teva política d'integració és el condensat (lliçó 08-02): una branca condensada no apareix com a fusionada, perquè els seus commits no són amain. Comprova-ho abans d'esborrar, o esborra per la branca remota que la plataforma elimina en fusionar.
packed-refs
Amb milers de referències soltes, cadascuna és un fitxer diminut i llegir-les totes costa milers d'operacions de disc:
git pack-refs les consolida en un únic fitxer .git/packed-refs:
# pack-refs with: peeled fully-peeled sorted a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0 refs/heads/main b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1 refs/remotes/origin/main c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2 refs/tags/v1.5.0 ^d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3
Ho fa git gc automàticament, així que rarament cal executar-ho a mà. Les referències que canvien es tornen a escriure com a fitxers solts i es reempaqueten al següent gc.
Altres referències que s'acumulen
# Etiquetes: si l'equip etiqueta cada compilació, se n'acumulen milers
git tag | wc -l
# Referències de notes, stash i worktrees
ls .git/refs/Un cas concret que sorprèn: els worktrees de la lliçó 06-06 deixen referències i metadades. Si crees i esborres worktrees sovint:
- Altres ordres lentes i les seves causes
| Ordre lenta | Causa habitual | Solució |
|---|---|---|
git log --graph --all |
Recórrer tot l'historial calculant assolibilitat | git commit-graph write --reachable (apartat 6) |
git blame en un fitxer gran |
Recorre l'historial línia a línia | Acotar amb -L 100,200 o amb un rang de commits |
git clone |
Historial complet amb binaris grans | Reduir l'historial (apartat 9); o clon parcial, lliçó 10-04 |
git checkout / switch |
Escriure molts fitxers al disc | core.fsmonitor; menys fitxers |
git fetch |
Moltes referències, o manca de bitmaps | fetch.prune true, repack.writeBitmaps true |
git push |
Calcular què falta al servidor | repack.writeBitmaps true |
git grep |
Cercar a tota la còpia de treball | git grep --cached (cerca a l'índex, molt més ràpid) |
git bisect |
Molts passos, cadascun amb un checkout complet | commit-graph; bisect --first-parent (lliçó 08-02) |
git diff en fitxers grans |
Càlcul del diff | -diff al .gitattributes si és binari (lliçó 08-04) |
Un cas concret que mereix menció, perquè enllaça amb la lliçó 06-05: els submòduls alenteixen git status de manera notable, perquè Git ha d'entrar a cadascun i comprovar-ne l'estat.
# No comprovar l'estat intern dels submòduls
git config --global status.submoduleSummary false
git config diff.ignoreSubmodules dirtyAmb components-ui com a submòdul, aquests dos ajustos es noten a cada git status.
- Bons costums que eviten el problema
Tot l'anterior és tractament. Això és prevenció, que és infinitament més barata:
1. Un .gitignore correcte des del primer commit (lliçó 08-03). Gairebé tots els repositoris enormes ho són per alguna cosa que mai no hi hauria d'haver entrat.
2. No versionar mai binaris grans sense pensar-hi. Abans d'afegir un fitxer de més d'uns quants MB, pregunta't: canviarà? Quantes vegades? Si la resposta és "sí, moltes", necessites Git LFS (lliçó 10-03) o un magatzem extern.
3. Posar un límit i comprovar-lo al hook pre-commit (lliçó 06-01):
#!/usr/bin/env bash
# .githooks/pre-commit — bloqueja fitxers massa grans
LIMIT=$((5 * 1024 * 1024)) # 5 MB
errors=0
while IFS= read -r fitxer; do
[ -f "$fitxer" ] || continue
mida=$(wc -c < "$fitxer")
if [ "$mida" -gt "$LIMIT" ]; then
echo "BLOQUEJAT: '$fitxer' ocupa $((mida / 1024 / 1024)) MB (límit: 5 MB)." >&2
echo " Si de debò cal versionar-lo, fes servir Git LFS." >&2
errors=1
fi
done < <(git diff --cached --name-only --diff-filter=ACM)
exit $errorsI la mateixa comprovació al CI (lliçó 07-06), perquè --no-verify existeix.
4. Commits atòmics i petits (lliçó 08-02). Un historial de commits petits es comprimeix millor i produeix deltes més eficients que un de commits gegantins.
5. fetch.prune true i gc.auto raonable a la configuració global de tot l'equip.
6. git maintenance start a cada repositori amb què treballis cada dia.
7. Mesurar de tant en tant. Un git count-objects -vH trimestral detecta el problema quan encara és fàcil d'arreglar.
8. No versionar sortides d'eines. Fitxers minificats, documentació generada, captures de proves fallides. Es regeneren.
9. Compte amb els bolcats de base de dades. A més de grans, solen contenir dades personals (lliçó 08-05).
Un guió de revisió trimestral
#!/usr/bin/env bash
# revisio-repositori.sh — informe de salut del repositori
echo "=== Mida ==="
du -sh .git
git count-objects -vH
echo ""
echo "=== Els 10 objectes més grans de l'historial ==="
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '$1=="blob" {printf "%10.1f MB %s\n", $3/1048576, $4}' \
| sort -rn | head -10
echo ""
echo "=== Referències ==="
echo "Branques locals: $(git branch | wc -l)"
echo "Branques remotes: $(git branch -r | wc -l)"
echo "Etiquetes: $(git tag | wc -l)"
echo "Refs soltes: $(find .git/refs -type f | wc -l)"
echo ""
echo "=== Branques remotes que ja no existeixen al servidor ==="
git remote prune origin --dry-run
echo ""
echo "=== Velocitat de git status ==="
/usr/bin/time -f " amb untracked: %e s" git status > /dev/null
/usr/bin/time -f " sense untracked: %e s" git status --untracked-files=no > /dev/null
echo ""
echo "=== Manteniment ==="
echo "commit-graph: $([ -f .git/objects/info/commit-graph ] && echo 'sí' || echo 'NO — executa git commit-graph write --reachable')"
echo "fsmonitor: $(git config --get core.fsmonitor || echo 'no configurat')"
echo "fetch.prune: $(git config --get fetch.prune || echo 'no configurat')"Errors Habituals i Consells
Error 1: optimitzar sense mesurar. Executar gc --aggressive perquè "el repositori va lent" quan el problema és el recorregut de fitxers sense seguiment gasta hores de CPU per a res. Mesura primer amb GIT_TRACE_PERFORMANCE=1.
Error 2: gc --aggressive com a rutina. És car i el seu benefici és marginal llevat que sigui després d'una reescriptura completa de l'historial o d'una importació.
Error 3: creure que git gc reduirà un repositori inflat per binaris. El gc reorganitza i comprimeix, però no pot eliminar objectes assolibles. Els binaris continuen allà perquè continuen a l'historial.
Error 4: esborrar un fitxer gran i esperar que el repositori aprimi. Mateix mecanisme que amb els secrets: git rm no toca l'historial. Cal filter-repo, amb totes les seves conseqüències.
Error 5: git reflog expire --expire=now --all && git gc --prune=now sense saber què es fa. Destrueix la xarxa de seguretat que permet recuperar commits perduts (lliçó 09-04).
Error 6: no activar fetch.prune. Milers de branques remotes fantasma que alenteixen cada fetch i embruten cada llistat.
Error 7: versionar binaris grans "perquè són pocs". Cinc versions d'un vídeo de 200 MB són 1 GB permanent per a tothom qui cloni, per sempre.
Error 8: ignorar el commit-graph. És l'optimització amb millor relació benefici/cost de tota la lliçó i gairebé ningú no l'activa.
Consell 1: git maintenance start als teus repositoris de treball. Substitueix el gc manual, s'executa en segon pla i manté el commit-graph al dia.
Consell 2: core.fsmonitor true si tens molts fitxers, especialment a Windows o macOS. És la diferència entre 2,8 s i 0,15 s a cada git status.
Consell 3: mesura el cost dels fitxers sense seguiment amb git status --untracked-files=no. Si la diferència és gran, ja saps on has d'actuar.
Consell 4: desa el guió de l'apartat 3. Trobar els objectes més grans de l'historial és la pregunta que es fa una vegada l'any i sempre cal buscar com es feia.
Consell 5: posa un límit de mida al pre-commit i al CI. Costa deu línies i evita el problema sencer.
Consell 6: revisió trimestral de dos minuts. El guió de l'apartat 12 detecta els problemes quan encara són barats d'arreglar.
Exercicis
Exercici 1: diagnosticar un repositori inflat
- Crea un repositori i confirma un
app.jspetit. - Afegeix-hi un fitxer binari gran generat a l'atzar i confirma'l:
head -c 20000000 /dev/urandom > demo/video.bin - Modifica'l tres vegades (regenerant-lo) i confirma cada versió.
- Executa
git count-objects -vHi anotasize-pack. - Executa el guió de l'apartat 3 i identifica els objectes grans.
- Esborra el fitxer amb
git rm, confirma, executagit gci torna a mirarcount-objects. Explica el resultat. - Compara la mida amb el mateix experiment fet sobre un fitxer de text de 20 MB modificat tres vegades. Explica la diferència.
Exercici 2: mesurar i accelerar git status
- En un repositori de proves, crea 20.000 fitxers petits en subdirectoris:
for i in $(seq 1 200); do mkdir -p "dir$i" for j in $(seq 1 100); do echo "contingut $i-$j" > "dir$i/f$j.txt"; done done - Confirma'ls i mesura
git statustres vegades. - Mesura
git status --untracked-files=noi calcula quin percentatge del temps se'n va en el recorregut de directoris. - Activa
core.fsmonitor true, executagit statusdues vegades (la primera arrenca el dimoni) i torna a mesurar. - Prova
git config feature.manyFiles truei mesura de nou. - Genera el
commit-graphi comparagit log --graph --allabans i després.
Exercici 3: higiene de referències i manteniment
- Crea un repositori amb 50 branques locals, la meitat de les quals fusionades a
main. - Compta les referències soltes amb
find .git/refs -type f | wc -l. - Executa
git pack-refs --alli torna a comptar. Examina.git/packed-refs. - Esborra les branques fusionades amb
git branch --merged. Explica què passaria si la política d'integració fos el condensat. - Activa
git maintenance starti comprova quines tasques queden registrades ambgit config --get-all maintenance.repo --global. - Escriu el guió de revisió de l'apartat 12 en un fitxer, executa'l i interpreta cada bloc de la sortida.
Solucions
Solució 1:
mkdir -p /tmp/practica-rend/demo && cd /tmp/practica-rend && git init -b main
echo "console.log('gestor-tasques');" > app.js
git add . && git commit -m "chore: commit inicial"# 2 i 3. El binari, en quatre versions
for v in 1 2 3 4; do
head -c 20000000 /dev/urandom > demo/video.bin
git add . && git commit -q -m "chore: vídeo de la demo, versió $v"
done76 MB per a un projecte el codi del qual són 30 bytes. Les quatre versions es desen senceres: no hi ha compressió possible sobre dades aleatòries, ni deltes aprofitables.
# 5. Els objectes grans
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '$1=="blob" {printf "%8.1f MB %s %s\n", $3/1048576, substr($2,1,10), $4}' \
| sort -rn | head 19.1 MB a1b2c3d4e5 demo/video.bin
19.1 MB b2c3d4e5f6 demo/video.bin
19.1 MB c3d4e5f6a7 demo/video.bin
19.1 MB d4e5f6a7b8 demo/video.bin
0.0 MB e5f6a7b8c9 app.jsQuatre blobs diferents amb la mateixa ruta: les quatre versions, cadascuna completa.
# 6. Esborrar no arregla res
git rm demo/video.bin && git commit -q -m "chore: elimina el vídeo"
git gc -q --prune=now
git count-objects -vHLa mida no ha baixat ni un byte. Els quatre blobs continuen sent assolibles des dels commits anteriors, que continuen a la història de main. gc --prune=now només elimina objectes inassolibles, i aquests no ho són. És exactament el mateix mecanisme que impedia esborrar un secret a la lliçó 08-05: els objectes de Git són immutables i els arbres antics continuen apuntant-hi.
Per recuperar l'espai caldria:
git filter-repo --path demo/video.bin --invert-paths --force
git count-objects -vH # ara sí que baixa# 7. La comparació amb text
mkdir /tmp/practica-text && cd /tmp/practica-text && git init -b main
# 20 MB de text: unes 400.000 línies
seq 1 400000 | sed 's/$/ linia de text del gestor de tasques/' > dades.txt
git add . && git commit -q -m "dades v1"
for v in 2 3 4; do
sed -i "1000s/.*/$v linia MODIFICADA/" dades.txt
git commit -q -am "dades v$v"
done
git gc -q
git count-objects -vH5,7 MB davant de 76 MB. Amb 80 MB de contingut lògic en tots dos casos. Dos mecanismes expliquen la diferència:
- Compressió: el text repetitiu es comprimeix a una fracció de la seva mida; les dades aleatòries (com un vídeo o una imatge ja comprimida) no es comprimeixen gens.
- Deltes: les versions 2, 3 i 4 del text es desen com a diferències d'unes poques línies respecte a la versió completa. Al binari, cada versió és un flux de bytes completament diferent i Git no hi troba cap similitud aprofitable.
Aquesta és la raó tècnica de fons per la qual Git és excel·lent per a text i pèssim per a binaris grans, i per la qual existeix Git LFS (lliçó 10-03).
Solució 2:
mkdir /tmp/practica-status && cd /tmp/practica-status && git init -b main
for i in $(seq 1 200); do
mkdir -p "dir$i"
for j in $(seq 1 100); do echo "contingut $i-$j" > "dir$i/f$j.txt"; done
done
git add . && git commit -q -m "chore: 20.000 fitxers"# 3. Sense recórrer els que no tenen seguiment
/usr/bin/time -f "%e s" git status --untracked-files=no > /dev/nullEl recorregut de directoris costa 0,71 s de 0,92 s: el 77 % del temps. El diagnòstic és clar i apunta a fsmonitor.
# 4. fsmonitor
git config core.fsmonitor true
git status > /dev/null # arrenca el dimoni; aquesta primera encara és lenta
for i in 1 2 3; do /usr/bin/time -f "%e s" git status > /dev/null; doneDe 0,92 s a 0,12 s: gairebé vuit vegades més ràpid. El dimoni rep del sistema operatiu les notificacions de canvi, així que Git ja no necessita recórrer res.
# 5. feature.manyFiles
git config feature.manyFiles true
git config --list | grep -E 'index.version|untrackedCache|skipHash'La millora addicional és petita quan fsmonitor ja està actiu, perquè el coll d'ampolla principal ja està resolt. Sense fsmonitor, untrackedCache tot sol sol donar una millora notable (de l'ordre del 40-60 % en el recorregut).
# 6. commit-graph
for i in $(seq 1 300); do echo "$i" >> app.js; git commit -q -am "commit $i"; done
/usr/bin/time -f "%e s" git log --oneline --graph --all > /dev/null
git commit-graph write --reachable
/usr/bin/time -f "%e s" git log --oneline --graph --all > /dev/nullEn un historial de 300 commits la diferència és petita; amb desenes de milers és d'un ordre de magnitud. La raó: sense commit-graph, Git ha de llegir i descomprimir cada objecte de commit per conèixer-ne els pares i la data; amb ell, aquesta informació és en un índex binari pla.
Solució 3:
mkdir /tmp/practica-refs && cd /tmp/practica-refs && git init -b main
echo "inici" > app.js && git add . && git commit -q -m "chore: inici"
# 1. 50 branques, 25 de fusionades
for i in $(seq 1 50); do
git switch -q -c "GT-$i" main
echo "canvi $i" >> app.js
git commit -q -am "feat: canvi $i"
done
git switch -q main
for i in $(seq 1 25); do git merge -q --no-ff "GT-$i" -m "Merge GT-$i" 2>/dev/null; done# pack-refs with: peeled fully-peeled sorted a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0 refs/heads/GT-1 b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1 refs/heads/GT-10 c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2 refs/heads/GT-11
51 fitxers diminuts convertits en un de sol, ordenat. Amb milers de referències, la diferència en llegir-les és notable.
Què passaria amb política de condensat (lliçó 08-02): git branch --merged comprova si el commit de la punta de la branca és assolible des de main. Amb un squash merge, els commits originals de la branca no entren mai a main: es crea un commit nou amb el mateix contingut però amb un SHA i un llinatge diferents. Per tant:
git branch --merged mainno llistaria cap d'aquelles branques, encara que la seva feina sigui íntegrament amain.git branch -des negaria a esborrar-les ("not fully merged"), obligant a fer-D, que esborra sense comprovar res.
Amb el condensat, el criteri fiable no és --merged, sinó la branca remota: la plataforma l'elimina en fusionar la PR, i git fetch --prune neteja la teva còpia local. És un motiu més per tenir fetch.prune true a la configuració global.
maintenance.auto=false desactiva el gc automàtic després de cada ordre: ja no cal, perquè les tasques programades se n'encarreguen en segon pla.
# 6. El guió de revisió
# (desar el guió de l'apartat 12 com a revisio-repositori.sh)
chmod +x revisio-repositori.sh && ./revisio-repositori.shInterpretació de cada bloc:
| Bloc | Què cal buscar |
|---|---|
| Mida | Que el .git no sigui desproporcionat respecte al codi. Una relació >20:1 és sospitosa |
| Objectes grans | Qualsevol blob de més d'uns quants MB, i sobretot el mateix fitxer repetit (versions successives d'un binari) |
| Referències | Branques remotes molt per damunt de les reals; refs soltes per milers |
| Branques fantasma | Tot el que llisti remote prune --dry-run és brossa acumulada |
git status |
Si la diferència amb --untracked-files=no és gran, activar fsmonitor |
| Manteniment | commit-graph present, fetch.prune configurat |
Conclusió
L'essencial d'aquesta lliçó:
- Mesura abans d'optimitzar.
GIT_TRACE_PERFORMANCE=1diu exactament on se'n va el temps, igit count-objects -vHdóna la foto del repositori en una pantalla. La intuïció sobre què és lent gairebé sempre falla. git rev-list --objects --all+git cat-file --batch-checkés la recepta per trobar els objectes més grans de l'historial. Veure el mateix fitxer repetit diverses vegades és el símptoma inequívoc d'un binari versionat.- Git emmagatzema els objectes solts (ràpid d'escriure, ineficient) i en packfiles (comprimits conjuntament i amb deltes).
git gcempaqueta, consolida i neteja allò inassolible, però no pot eliminar el que continua sent a l'historial.--aggressivegairebé mai no cal: només després d'una reescriptura completa o d'una importació. git maintenance startés el substitut modern delgcmanual: tasques separades, programades i en segon pla. I porta amb ell elcommit-graph, que és l'optimització amb millor relació benefici/cost de tota la lliçó.git statusés lent pel recorregut de fitxers sense seguiment, no per l'historial. Es mesura amb--untracked-files=noi s'arregla ambcore.fsmonitor(Git 2.37+) icore.untrackedCache.feature.manyFilesagrupa els ajustos per a còpies de treball amb molts fitxers.- Els binaris grans són cars perquè no es comprimeixen, no admeten deltes, es desen sencers a cada versió i tothom se'ls descarrega. I esborrar-los no allibera espai, exactament pel mateix motiu que esborrar un secret no l'elimina: cal reescriure l'historial, amb totes les seves conseqüències. La solució correcta és Git LFS, a la lliçó 10-03.
- Higiene de referències:
fetch.prune truea la configuració global de tothom, esborrat de branques fusionades (amb compte si la política és el condensat) ipacked-refs, que elgcmanté sol. - I sobretot, prevenció:
.gitignoredes del primer commit, un límit de mida al hookpre-commiti al CI, commits petits, i una revisió trimestral de dos minuts.
Per a allò que va més enllà —monorepos, clons parcials amb --filter=blob:none, clons superficials, índex dispers— hi ha la lliçó 10-04.
El mòdul, en una idea
El mòdul 7 va acabar dient que a gestor-tasques li faltaven hàbits. Ara els té tots.
Missatges que expliquen el perquè i dels quals es dedueixen la versió i el changelog (08-01). Un historial llegible, bisecable i reversible, amb una política d'integració escrita i acordada (08-02). Només allò que hi ha de ser a dins (08-03), tractat com correspon, amb els finals de línia de la Carla resolts d'una vegada (08-04). Els secrets a fora, i un procediment escrit per al dia que torni a passar (08-05). I un repositori ràpid, mesurat i mantingut (08-06).
L'Ana, el Bruno, la Carla i el Diego tenen ara eina, procés i hàbits. Ho han fet tot bé.
I això és exactament el que no passa a la realitat.
Perquè algú farà git reset --hard sobre tres dies de feina sense confirmar. Algú confirmarà a la branca equivocada, o amb l'usuari equivocat, i no se n'adonarà fins després de publicar. Algú farà pull sobre una branca que ha divergit i es trobarà amb un embolic que no sabrà desfer. Algú esborrarà una branca que sí que importava i descobrirà, amb pànic, que git log ja no la troba. I algun dia, un .git/index corrupte farà que Git es negui a fer absolutament res.
Tot això té solució, i gairebé sempre una de més senzilla del que sembla en el moment de pànic. Aprendre a sortir dels embolics —desfer canvis, resoldre divergències amb el remot, recuperar commits perduts amb el reflog, reparar un repositori trencat i diagnosticar allò que no encaixa— és tot el mòdul 9.
Comencem pel catàleg dels problemes que tothom es troba tard o d'hora, a la lliçó 09-01: Problemes Habituals de Git.
Dominant Git: De Principiant a Avançat
Mòdul 1: Introducció a Git
- Què és Git?
- Instal·lant Git
- Terminologia Bàsica de Git
- El Model de Dades de Git
- Configurant Git
- Configuració Inicial
Mòdul 2: Operacions Bàsiques de Git
- Creant un Repositori
- Clonant un Repositori
- Flux de Treball Bàsic de Git
- Preparant i Confirmant Canvis
- Inspeccionant Canvis amb git diff
- Visualitzant l'Historial de Confirmacions
Mòdul 3: Branques i Fusió
- Entenent les Branques
- Creant i Canviant Branques
- Fusionant Branques
- Estratègies de Fusió
- Resolent Conflictes de Fusió
- Gestió de Branques
Mòdul 4: Treballant amb Repositoris Remots
- Entenent els Repositoris Remots
- Afegint un Repositori Remot
- Autenticació amb Repositoris Remots
- Obtenint i Baixant Canvis
- Enviant Canvis
- Rastrejant Branques
Mòdul 5: Operacions Avançades de Git
- Rebase
- Rebase Interactiu
- Cherry-Picking de Confirmacions
- Desant Canvis Temporals
- Etiquetant Confirmacions
- Revertint Confirmacions
Mòdul 6: Eines i Tècniques de Git
- Usant Git Hooks
- Git Bisect
- Git Blame
- Git Log i Àlies
- Submòduls de Git
- Múltiples Còpies de Treball amb git worktree
Mòdul 7: Estratègies de Col·laboració i Flux de Treball
- Forks i Pull Requests
- Revisions de Codi amb Git
- Flux de Treball Git Flow
- GitHub Flow
- Trunk Based Development
- Integració Contínua amb Git
Mòdul 8: Bones Pràctiques i Consells de Git
- Escrivint Bons Missatges de Confirmació
- Mantenint un Historial Net
- Ignorant Fitxers amb .gitignore
- Atributs de Fitxer amb .gitattributes
- Bones Pràctiques de Seguretat
- Consells de Rendiment
Mòdul 9: Resolució de Problemes i Depuració
- Problemes Habituals de Git
- Desfent Canvis
- Resolent Divergències amb el Remot
- Recuperant Confirmacions Perdudes
- Tractant amb Repositoris Corruptes
- Tècniques Avançades de Depuració
