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 status triga 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

  1. Mesurar abans d'optimitzar
  2. git count-objects -vH: la foto del repositori
  3. Trobar els objectes més grans de l'historial
  4. Empaquetatge: objectes solts i packfiles
  5. git gc: què fa realment
  6. git maintenance: el substitut modern
  7. Accelerar git status: fsmonitor i untrackedCache
  8. feature.manyFiles i altres ajustos
  9. El cost dels fitxers binaris grans
  10. Higiene de referències: prune i packed-refs
  11. Altres ordres lentes i les seves causes
  12. Bons costums que eviten el problema

  1. 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 status
12: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:

GIT_TRACE2_PERF_BRIEF=1 GIT_TRACE2_PERF=/dev/stdout git status 2>&1 | head -30

I per comparar abans i després d'un canvi, mesura diverses vegades:

for i in 1 2 3; do /usr/bin/time -f "%e s" git status > /dev/null; done

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

  1. 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:

git count-objects -vH
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 .
897M	.git
4.2M	.

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.

  1. 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"
    done
chmod +x objectes-grans.sh
./objectes-grans.sh 10
 214MiB 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.mp4 apareix 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.psd també 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 -15
   357891234  mp4
   154329871  psd
    88129043  sql
    33021884  zip
     4102993  json
      982341  js

Aquesta 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.

  1. 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:

ls .git/objects/a1/
b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0

É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:

  1. Compressió conjunta, més eficaç que comprimir cada objecte per separat.
  2. 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 -5

Quan 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.

  1. git gc: què fa realment

git gc (garbage collection) és l'ordre de manteniment clàssica. Fa cinc coses:

  1. Empaqueta els objectes solts en packfiles.
  2. Consolida diversos packfiles en menys.
  3. Elimina objectes inassolibles que hagin superat el període de gràcia.
  4. Empaqueta les referències a .git/packed-refs (apartat 10).
  5. Fa caducar les entrades antigues del reflog (90 dies per defecte per a allò assolible, 30 per a la resta).
# El manteniment normal
git gc

# Veure què faria, sense fer-ho
git gc --auto --dry-run
git count-objects -vH     # abans
git gc
git count-objects -vH     # després
# 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

git gc --aggressive     # pot trigar HORES en un repositori gran

--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 , una vegada
Després d'importar des d'un altre sistema de control de versions , 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:

git repack -a -d -f --depth=250 --window=250
  • -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:

# AIXÒ DESTRUEIX la xarxa de seguretat
git reflog expire --expire=now --all
git gc --prune=now

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).

  1. git maintenance: el substitut modern

Des 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 gc monolític.
  • Incremental: reempaqueta a poc a poc en lloc de tot de cop.
  • No fa caducar el reflog per sorpresa.
# Activar el manteniment programat per a aquest repositori
git maintenance start

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 unregister

El 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 true

L'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 s

Accelera 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

  1. Accelerar git status: fsmonitor i untrackedCache

Aquest é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:

  1. Quins fitxers seguits han canviat? → comparar l'índex amb el disc.
  2. Què hi ha preparat? → comparar l'índex amb HEAD.
  3. 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:

git config core.fsmonitor true

La primera execució arrenca un dimoni en segon pla:

git fsmonitor--daemon status
fsmonitor-daemon is watching '/home/carla/projectes/gestor-tasques'

L'efecte és dràstic:

# Abans
time git status      # 2,8 s

# Després (de la segona execució endavant)
time git status      # 0,15 s
# Aturar el dimoni
git fsmonitor--daemon stop

# Desactivar-ho
git config --unset core.fsmonitor

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 true

Requereix 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:

time git status                                 # 2,8 s
time git status --untracked-files=no            # 0,2 s

Si la diferència és gran, el teu problema és el recorregut de directoris i fsmonitor és la solució.

  1. feature.manyFiles i altres ajustos

Git agrupa configuracions recomanades en "macros" de funcionalitat:

git config feature.manyFiles true

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 2g

I un ajust que no hauries de tocar a la lleugera:

# NO: baixa la compressió a canvi de velocitat d'escriptura
git config --global core.compression 1

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.

  1. 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 10M

Amb 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 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:

*.mp4   filter=lfs diff=lfs merge=lfs -text
*.psd   filter=lfs diff=lfs merge=lfs -text

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.

  1. Higiene de referències: prune i packed-refs

Les 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:

git branch -r | wc -l
1247

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 conservador

Reprenent 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 -d

Compte amb --merged si 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 a main. 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:

find .git/refs -type f | wc -l

git pack-refs les consolida en un únic fitxer .git/packed-refs:

# Empaquetar totes les referències
git pack-refs --all

# Veure el resultat
head -5 .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:

git worktree prune

  1. 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 dirty

Amb components-ui com a submòdul, aquests dos ajustos es noten a cada git status.

  1. 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 $errors

I 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

  1. Crea un repositori i confirma un app.js petit.
  2. Afegeix-hi un fitxer binari gran generat a l'atzar i confirma'l:
    head -c 20000000 /dev/urandom > demo/video.bin
    
  3. Modifica'l tres vegades (regenerant-lo) i confirma cada versió.
  4. Executa git count-objects -vH i anota size-pack.
  5. Executa el guió de l'apartat 3 i identifica els objectes grans.
  6. Esborra el fitxer amb git rm, confirma, executa git gc i torna a mirar count-objects. Explica el resultat.
  7. 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

  1. 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
    
  2. Confirma'ls i mesura git status tres vegades.
  3. Mesura git status --untracked-files=no i calcula quin percentatge del temps se'n va en el recorregut de directoris.
  4. Activa core.fsmonitor true, executa git status dues vegades (la primera arrenca el dimoni) i torna a mesurar.
  5. Prova git config feature.manyFiles true i mesura de nou.
  6. Genera el commit-graph i compara git log --graph --all abans i després.

Exercici 3: higiene de referències i manteniment

  1. Crea un repositori amb 50 branques locals, la meitat de les quals fusionades a main.
  2. Compta les referències soltes amb find .git/refs -type f | wc -l.
  3. Executa git pack-refs --all i torna a comptar. Examina .git/packed-refs.
  4. Esborra les branques fusionades amb git branch --merged. Explica què passaria si la política d'integració fos el condensat.
  5. Activa git maintenance start i comprova quines tasques queden registrades amb git config --get-all maintenance.repo --global.
  6. 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"
done
# 4. La foto
git gc -q
git count-objects -vH
count: 0
size: 0 bytes
in-pack: 18
packs: 1
size-pack: 76.30 MiB

76 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.js

Quatre 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 -vH
size-pack: 76.30 MiB

La 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 -vH
size-pack: 5.71 MiB

5,7 MB davant de 76 MB. Amb 80 MB de contingut lògic en tots dos casos. Dos mecanismes expliquen la diferència:

  1. 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.
  2. 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"
# 2. Mesura base
for i in 1 2 3; do /usr/bin/time -f "%e s" git status > /dev/null; done
0.94 s
0.91 s
0.92 s
# 3. Sense recórrer els que no tenen seguiment
/usr/bin/time -f "%e s" git status --untracked-files=no > /dev/null
0.21 s

El 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; done
0.13 s
0.11 s
0.12 s

De 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.

git fsmonitor--daemon status
fsmonitor-daemon is watching '/tmp/practica-status'
# 5. feature.manyFiles
git config feature.manyFiles true
git config --list | grep -E 'index.version|untrackedCache|skipHash'
index.version=4
core.untrackedcache=true
index.skiphash=true

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/null

En 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
# 2. Referències soltes
find .git/refs -type f | wc -l
51
# 3. Empaquetar-les
git pack-refs --all
find .git/refs -type f | wc -l
0
head -4 .git/packed-refs
# 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.

# 4. Esborrar les fusionades
git branch --merged main | grep -vE '^\*|main' | wc -l
25
git branch --merged main | grep -vE '^\*|main' | xargs -r git branch -q -d
git branch | wc -l
26

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 main no llistaria cap d'aquelles branques, encara que la seva feina sigui íntegrament a main.
  • git branch -d es 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.

# 5. Manteniment programat
git maintenance start
git config --get-all maintenance.repo --global
/tmp/practica-refs
git config --list | grep maintenance
maintenance.auto=false
maintenance.strategy=incremental
maintenance.repo=/tmp/practica-refs

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.sh

Interpretació 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=1 diu exactament on se'n va el temps, i git count-objects -vH dó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 gc empaqueta, consolida i neteja allò inassolible, però no pot eliminar el que continua sent a l'historial. --aggressive gairebé mai no cal: només després d'una reescriptura completa o d'una importació.
  • git maintenance start és el substitut modern del gc manual: tasques separades, programades i en segon pla. I porta amb ell el commit-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=no i s'arregla amb core.fsmonitor (Git 2.37+) i core.untrackedCache. feature.manyFiles agrupa 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 true a la configuració global de tothom, esborrat de branques fusionades (amb compte si la política és el condensat) i packed-refs, que el gc manté sol.
  • I sobretot, prevenció: .gitignore des del primer commit, un límit de mida al hook pre-commit i 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

Mòdul 2: Operacions Bàsiques de Git

Mòdul 3: Branques i Fusió

Mòdul 4: Treballant amb Repositoris Remots

Mòdul 5: Operacions Avançades de Git

Mòdul 6: Eines i Tècniques de Git

Mòdul 7: Estratègies de Col·laboració i Flux de Treball

Mòdul 8: Bones Pràctiques i Consells de Git

Mòdul 9: Resolució de Problemes i Depuració

Mòdul 10: Git al Món Real

© Copyright 2026. Tots els drets reservats