La lliçó anterior va resoldre una de les tres maneres en què un repositori es torna inmanejable: els fitxers molt grans. En queden dues, i són independents entre si. Un repositori pot tenir moltíssim historial encara que cada fitxer sigui diminut, i pot tenir moltíssims fitxers encara que l'historial sigui curt. Cada problema té el seu símptoma i el seu remei, i aplicar el remei equivocat no arregla res.
Aquesta lliçó tanca dues promeses del curs. La lliçó 06-05 va deixar oberta la comparativa entre submòduls, subtree, paquets i monorepo, ajornant el monorepo fins aquí. La 08-06 va ensenyar a mesurar i a optimitzar un repositori normal que s'ha tornat lent, i va remetre aquí els repositoris veritablement enormes. I el cas 3 de la 10-01 va descriure el monorepo empresarial explicant-ne el perquè i deixant el com per a aquesta lliçó.
Comencem per deixar clara una cosa, perquè és la més important de totes: gairebé res del que hi ha aquí no ho necessita gestor-tasques, ni probablement ho necessites tu. Això és material per al dia en què et toqui un repositori de cinquanta mil fitxers, i perquè sàpigues reconèixer quan ha arribat aquest dia. Aplicar aquestes tècniques a un repositori sa hi afegeix complexitat, raresa i una superfície nova d'errors, a canvi de res.
Contingut
- Les tres dimensions del creixement
- Mesurar abans d'optimitzar (i què mesurar en cada cas)
- Clons parcials:
--filter=blob:nonei--filter=tree:0 - Clon parcial enfront de clon superficial
- Sparse checkout i l'índex dispers
- Acceleradors del costat del client
- Monorepo enfront de multirepo
- Què cal perquè un monorepo funcioni
- Quan NO cal res d'això
- Una recepta per símptoma
- Les tres dimensions del creixement
L'error més habitual en enfrontar-se a un repositori lent és tractar-lo com un problema. No ho és. Són tres, es manifesten de manera diferent i s'arreglen amb eines diferents.
| Dimensió | Què significa | Símptomes típics | Què NO ho arregla | Remei |
|---|---|---|---|---|
| Molt historial | Centenars de milers o milions de commits; molts anys de vida | git clone lentíssim; git log --graph lent; git blame lent; git branch --contains etern |
Esborrar fitxers; LFS | commit-graph; clon parcial --filter=tree:0; clon superficial per a casos d'usar i llençar |
| Molts fitxers | Desenes o centenars de milers de fitxers a la còpia de treball | git status triga segons o minuts; git checkout lent; l'editor indexa eternament |
Reduir l'historial; gc |
sparse-checkout --cone + índex dispers; fsmonitor; untrackedCache |
| Fitxers molt grans | Binaris de desenes o centenars de MB, amb moltes versions | Repositori de gigabytes; clon lentíssim encara que hi hagi pocs commits; gc costós |
sparse-checkout; commit-graph |
Git LFS (10-03); treure el binari del repositori; clon parcial --filter=blob:none |
Les tres són independents. Un repositori pot patir-ne una, dues o les tres alhora, i cal diagnosticar cadascuna per separat.
Un exemple que aclareix la independència: un repositori amb vint anys d'història i seixanta fitxers de text pateix la dimensió 1 i cap més. git clone triga deu minuts i git status és instantani. Aplicar-li sparse-checkout no serviria absolutament de res.
flowchart TD
P["El meu repositori va lent"] --> Q1{"Quina ordre<br/>és lenta?"}
Q1 -->|"git status<br/>git checkout"| D2["Dimensió 2:<br/>molts fitxers"]
Q1 -->|"git log<br/>git blame<br/>git branch --contains"| D1["Dimensió 1:<br/>molt historial"]
Q1 -->|"git clone<br/>git gc"| Q2{"El repositori<br/>pesa molt?"}
Q2 -->|"sí, GB"| D3["Dimensió 3:<br/>fitxers grans"]
Q2 -->|"no, però hi ha<br/>molts commits"| D1
D1 --> R1["commit-graph<br/>--filter=tree:0"]
D2 --> R2["sparse-checkout --cone<br/>fsmonitor"]
D3 --> R3["Git LFS (10-03)<br/>--filter=blob:none"]
- Mesurar abans d'optimitzar (i què mesurar en cada cas)
La regla de la lliçó 08-06 continua vigent i aquí importa més: la intuïció sobre què és lent és gairebé sempre equivocada. Abans d'aplicar cap d'aquestes tècniques, mesura.
La foto general
count: 0 size: 0 bytes in-pack: 4128394 packs: 3 size-pack: 8.42 GiB prune-packable: 0 garbage: 0 size-garbage: 0 bytes
Les tres mesures que separen les dimensions
# Dimensió 1: quant historial hi ha?
git rev-list --count --all
git rev-list --count HEAD
# Dimensió 2: quants fitxers hi ha a la còpia de treball?
git ls-files | wc -l
# Dimensió 3: quant ocupen els objectes més grans?
git lfs migrate info --everything --above=1Mb 2>/dev/null || \
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '$1=="blob" {print $3, $4}' | sort -rn | head -10Tres xifres que orienten la decisió, amb ordres de magnitud aproximats:
| Mesura | Sense problema | Comença a fer mal | Necessita tècniques d'aquesta lliçó |
|---|---|---|---|
Commits (rev-list --count --all) |
< 20.000 | 50.000 – 300.000 | > 500.000 |
Fitxers (ls-files | wc -l) |
< 10.000 | 20.000 – 80.000 | > 100.000 |
Mida (size-pack) |
< 500 MB | 1 – 3 GB | > 5 GB |
Són referències, no llindars exactes: depenen molt del maquinari, del sistema de fitxers i del sistema operatiu. Un repositori de 30.000 fitxers va fi en un portàtil modern amb Linux i pot ser penós a Windows amb un antivirus revisant cada accés.
On se'n va el temps exactament
d0 | main | region_enter | r1 | 0.001 | index:do_read_index d0 | main | region_leave | r1 | 1.842 | index:do_read_index d0 | main | region_enter | r1 | 1.843 | dir:untracked d0 | main | region_leave | r1 | 9.104 | dir:untracked d0 | main | region_leave | r1 | 11.203 | status
Lectura del diagnòstic: 1,8 segons llegint l'índex i 9,1 segons recorrent directoris buscant fitxers sense seguiment. És un problema de dimensió 2 pur. Ni el commit-graph ni el clon parcial hi farien res; el que cal és untrackedCache, fsmonitor i, si escau, sparse-checkout.
I gestor-tasques, per tenir-ne la referència:
git rev-list --count --all # 412
git ls-files | wc -l # 9
git count-objects -vH # size-pack: 2.14 MiBCap de les tres dimensions. Res d'aquesta lliçó no li aplica.
- Clons parcials:
--filter=blob:none i --filter=tree:0
--filter=blob:none i --filter=tree:0El clon parcial és, de llarg, la tècnica més útil i menys coneguda d'aquesta lliçó.
La idea
Un clon normal descarrega tots els objectes assolibles: tots els commits, tots els arbres i tots els blobs de tota la història. Un clon parcial descarrega el graf però omet certs objectes, i els demana al servidor sota demanda quan alguna ordre els necessita.
Això requereix que el servidor ho admeti —ho fan les plataformes principals i les versions modernes de Git— i funciona sobre una connexió persistent anomenada promisor remote: el remot "promet" tenir els objectes que falten.
flowchart LR
subgraph normal["Clon complet"]
A1["Tots els commits"]
A2["Tots els arbres"]
A3["Tots els blobs<br/>de tota la història"]
end
subgraph parcial["Clon amb --filter=blob:none"]
B1["Tots els commits"]
B2["Tots els arbres"]
B3["Només els blobs de<br/>la revisió extreta"]
B4["La resta: sota demanda"]
end
--filter=blob:none: sense contingut històric
Descarrega tots els commits i tots els arbres, però cap blob llevat dels necessaris per extreure la revisió de treball.
Què continua funcionant sense descarregar res:
git log --oneline --graph --all # el graf és complet
git log --format=%s # els missatges són als commits
git branch -a --contains a1b2c3d # topologia pura
git tag --contains a1b2c3d
git log --name-only # els noms són als arbres
git rev-list --count --allQuè provoca una descàrrega sota demanda:
git log -p # necessita el contingut per al diff
git diff HEAD~50 # necessita blobs antics
git blame app.js # necessita totes les versions del fitxer
git checkout v1.0.0 # necessita els blobs d'aquesta revisióVeure-ho en marxa:
remote: Enumerating objects: 47, done.
remote: Counting objects: 100% (47/47), done.
Receiving objects: 100% (47/47), 182.41 KiB | 2.11 MiB/s, done.
4f8a2e6c (Ana Ferrer 2026-07-31 13:12:04 +0200 1) function calculaPendents(tasques) {
...Git ha anat a buscar les 47 versions del fitxer que necessitava per al blame, i després ha respost. És més lent la primera vegada i queda a la memòria cau per a les següents.
Els objectes que ja s'han portat s'hi queden:
Aquest --missing=print llista amb un ? al davant els objectes que falten localment. És la manera de saber quant queda per descarregar.
--filter=tree:0: només commits
Encara més agressiu: omet també els arbres. Només es descarreguen els objectes commit.
Amb això continua funcionant git log --oneline, git log --graph, git rev-list i qualsevol consulta purament topològica o de missatges. Però gairebé qualsevol operació sobre fitxers dispara descàrregues, inclòs git log --name-only, perquè els noms viuen als arbres.
És l'opció per a casos molt concrets: analitzar el graf d'un repositori enorme, comptar commits, extreure estadístiques d'autoria, generar un registre de canvis. Per treballar cada dia, --filter=blob:none és gairebé sempre l'elecció correcta.
Altres filtres
# Ometre blobs de més d'1 MB (útil si hi ha binaris solts)
git clone --filter=blob:limit=1m https://git.exemple.cat/projecte.git
# Aplicar el filtre a un repositori ja clonat
git config remote.origin.promisor true
git config remote.origin.partialclonefilter blob:noneblob:limit=1m és un punt intermedi interessant: porta tot el codi (que és petit) i deixa fora els binaris pesants. Sense necessitar LFS.
Comparació en xifres
Per a un repositori hipotètic de gran mida, amb ordres de magnitud aproximats:
| Tipus de clon | Es descarrega | Temps relatiu | Historial complet |
|---|---|---|---|
| Complet | ~8 GB | 100 % | Sí |
--filter=blob:none |
~500 MB | ~8 % | Sí (graf complet) |
--filter=tree:0 |
~150 MB | ~3 % | Sí (només commits) |
--depth=1 |
~200 MB | ~4 % | No |
La fila que importa és la segona: el 8 % de la descàrrega conservant l'historial complet. Això és el que fa del clon parcial l'opció per defecte raonable per a repositoris grans.
- Clon parcial enfront de clon superficial
A la lliçó 02-02 vam veure el clon superficial --depth=1. Convé contrastar-los, perquè resolen coses diferents i el superficial té paranys que el parcial no té.
--depth=N (superficial) |
--filter=blob:none (parcial) |
|
|---|---|---|
| Què omet | Commits anteriors a la profunditat indicada | Blobs no necessaris ara |
| El graf | Truncat: els commits antics no existeixen | Complet |
git log |
Només els N últims commits | Tot l'historial |
git blame |
Només fins al tall | Complet (descarregant) |
git bisect |
Inutilitzable | Funciona |
git describe |
Falla o dona resultats estranys | Funciona |
git merge-base amb una branca antiga |
Falla: no hi ha avantpassat comú | Funciona |
git log v1.0.0..main |
Falla: l'etiqueta no existeix | Funciona |
| Fusionar / rebasar sobre base antiga | Problemàtic | Normal |
| Recuperar el que falta | git fetch --unshallow (descàrrega completa) |
Automàtic i transparent |
| Suport del servidor | Universal | Requereix servidor modern |
| Ús típic | CI que només compila la revisió actual | Treball diari en repositoris grans |
Per què el parcial sol ser millor
1. No trenca la semàntica de Git. Un clon superficial és un repositori incomplet: hi ha commits que senzillament no existeixen, i qualsevol operació que necessiti assolir-los falla. Un clon parcial és un repositori complet amb contingut diferit: tot existeix, algunes coses es descarreguen en demanar-les.
Aquest és l'argument de fons. Amb --depth, Git et mentirà sobre la història i tu hauràs de recordar que està truncada. Amb --filter, Git et dirà la veritat i descarregarà allò que li falti.
2. La recuperació és transparent. Si un clon superficial necessita alguna cosa antiga, cal executar git fetch --unshallow, que descarrega el repositori sencer de cop. Un clon parcial porta només els objectes concrets que li calen, sense que tu hi intervinguis.
3. Els errors del superficial són confusos. Aquest és el clàssic en una canonada de CI:
O pitjor, en una comparació de petició d'extracció:
Tots dos fallen perquè el punt de divergència és fora de la profunditat descarregada. És el motiu que tantes configuracions de CI portin fetch-depth: 0 (descarregar-ho tot), que resol el problema per la via cara. L'alternativa bona:
- uses: actions/checkout@v4
with:
fetch-depth: 0
filter: blob:none # historial complet, sense contingut històricHistorial complet per a les comparacions i el describe, sense descarregar gigabytes de blobs antics. És la configuració recomanada per a CI en repositoris grans.
Quan continua sent millor el superficial
- El servidor no admet clons parcials.
- Una tasca de CI que només compila la revisió actual i no consulta res de l'historial:
--depth=1és mínim i suficient. - Un contenidor de desplegament on només vols els fitxers d'una versió concreta. Encara que per a això,
git archivesol ser encara millor:
Això no crea repositori: descarrega només els fitxers d'aquella etiqueta.
- Sparse checkout i l'índex dispers
El clon parcial ataca el que es descarrega. sparse-checkout ataca el que apareix al disc, que és un problema diferent: la dimensió 2.
El problema
Un monorepo amb 300.000 fitxers. L'Ana treballa només a serveis/tasques/, que en són 400. Però la seva còpia de treball té els 300.000, i en conseqüència:
git statusrecorre 300.000 entrades.- L'editor indexa 300.000 fitxers.
- Cada
git checkoutentre branques comprova 300.000 rutes. - La cerca de text travessa codi que no li interessa.
La solució
# Activar el mode dispers, en mode con
git sparse-checkout init --cone
# Declarar quins directoris vull al disc
git sparse-checkout set serveis/tasques biblioteques/components-ui
# Veure què hi ha declarat
git sparse-checkout list
# Afegir-ne més sense reescriure l'anterior
git sparse-checkout add serveis/notificacions
# Tornar a l'estat normal
git sparse-checkout disableDesprés d'això, la còpia de treball conté només aquests directoris (més els fitxers de l'arrel), i git status passa de recórrer 300.000 entrades a recórrer'n unes poques milers.
I una manera més directa de clonar amb això ja actiu:
git clone --filter=blob:none --sparse https://git.exemple.cat/monorepo.git
cd monorepo
git sparse-checkout set serveis/tasquesAquesta combinació —clon parcial + sparse checkout— és el muntatge estàndard per treballar en un monorepo gran: descarregues poc i materialitzes menys.
Per què el mode con és el que escala
sparse-checkout té dos modes, i la diferència és crucial.
| Mode original (patrons) | Mode con (--cone) |
|
|---|---|---|
| Què accepta | Patrons tipus .gitignore, arbitràriament complexos |
Només rutes de directori |
| Com es decideix cada fitxer | Avaluant la llista de patrons fitxer a fitxer | Comprovant el prefix de directori |
| Cost | O(fitxers × patrons) | O(directoris), amb cerca per prefix |
| Permet índex dispers | No | Sí |
| Expressivitat | Alta | Limitada a directoris sencers |
El mode con renuncia a l'expressivitat per guanyar una propietat estructural: com que els patrons són sempre directoris sencers, Git pot raonar sobre subarbres complets en lloc de sobre fitxers solts. I això habilita el que és veritablement important.
L'índex dispers
Recorda de la lliçó 01-04 i de la 08-06 que l'índex (.git/index) conté una entrada per fitxer seguit. En un monorepo de 300.000 fitxers, l'índex té 300.000 entrades i ocupa desenes de megabytes. Llegir-lo i escriure'l és la primera causa de lentitud de git status, i passa encara que els fitxers no siguin al disc: l'índex els continua llistant.
L'índex dispers (sparse index) ho resol. En lloc d'una entrada per fitxer, en guarda una de sola per cada directori que és fora del con, apuntant directament al seu objecte arbre.
Índex normal (300.000 entrades): serveis/tasques/app.js serveis/tasques/index.html ... serveis/facturacio/main.js <- fora del con serveis/facturacio/model.js <- fora del con ... (280.000 més, totes fora del con) Índex dispers (~4.000 entrades): serveis/tasques/app.js serveis/tasques/index.html ... serveis/facturacio/ (tree 8a1f6c3d) <- UNA entrada serveis/pagaments/ (tree 2e9f4c7b) <- UNA entrada
S'activa així:
I es comprova:
git ls-files --sparse | grep '/$' | head
test-tool read-cache --table | wc -l # si tens les eines de provaL'efecte sobre git status en un monorepo gran és d'un ordre de magnitud. I el motiu és purament estructural: una entrada d'arbre representa un subarbre sencer, gràcies al fet que el hash d'un arbre resumeix tot el seu contingut (lliçó 01-04). Si el hash de l'arbre de serveis/facturacio/ no ha canviat, Git sap amb certesa matemàtica que res d'aquest subdirectori no ha canviat, sense mirar ni un sol fitxer.
Aquest és un exemple preciós de per què entendre el model de dades importa: l'índex dispers no és un truc, és una conseqüència directa de l'adreçament per contingut.
L'efecte sobre git status i altres ordres
Amb sparse-checkout actiu, git status inclou un avís:
On branch main You are in a sparse checkout with 3% of tracked files present. nothing to commit, working tree clean
Aquest avís és important i cal saber llegir-lo. No estàs veient el repositori sencer. Si busques un fitxer i no apareix, potser no és que no existeixi: és que és fora del teu con.
Ordres que es comporten de manera diferent:
# Llista només el que hi ha al con
git ls-files
# Llista TOT el que està seguit, dins i fora del con
git ls-files --sparse
# Un fitxer fora del con continua existint a l'historial
git show HEAD:serveis/facturacio/main.js # funciona perfectament
# git grep busca només al que està materialitzat per defecte
git grep "calculaTotal"I la raresa que més desconcerta: si fas git checkout d'una branca que esborra un fitxer fora del teu con, no veuràs res, perquè aquest fitxer mai no va ser al teu disc. Tot és coherent, però requereix tenir el model clar.
- Acceleradors del costat del client
Aquests ja van aparèixer a la lliçó 08-06; aquí van amb el matís de l'escala.
commit-graph: accelerar el recorregut del graf
És un fitxer d'índex auxiliar que guarda, precalculada, la topologia de l'historial: per a cada commit, els seus pares, la seva data i el seu número de generació (la distància al commit arrel).
# Generar-lo
git commit-graph write --reachable --changed-paths
# Que es mantingui sol
git config fetch.writeCommitGraph true
git config core.commitGraph trueSense ell, respondre "és A avantpassat de B?" obliga a llegir objectes commit del disc i a recórrer el graf. Amb ell, el número de generació permet descartar branques senceres del recorregut sense llegir res.
El --changed-paths mereix atenció especial en repositoris enormes: guarda un filtre de Bloom amb les rutes modificades a cada commit. Serveix per accelerar dràsticament:
En un monorepo, git log sobre un fitxer concret passa d'examinar milions de commits a descartar-los gairebé tots amb una comprovació de bits. És la diferència entre trenta segons i mig segon.
És l'optimització de dimensió 1 amb millor relació cost/benefici, i no té cap inconvenient: és un fitxer de memòria cau regenerable que no altera res del repositori.
fsmonitor: no recórrer el disc
fsmonitor engega un dimoni que escolta les notificacions del sistema de fitxers i manté la llista del que ha canviat. Així git status no ha de recórrer els directoris: pregunta al dimoni.
untrackedCache guarda a l'índex l'última exploració de cada directori juntament amb la seva marca de temps, per no tornar a explorar els que no s'han tocat.
Efecte conjunt en un repositori de 100.000 fitxers, com a ordre de magnitud: git status pot passar de diversos segons a fraccions de segon. La combinació amb l'índex dispers és multiplicativa, perquè ataquen parts diferents del cost: fsmonitor redueix el que cal mirar al disc, l'índex dispers redueix el que cal llegir de l'índex.
Nota de plataforma: fsmonitor integrat funciona a macOS i Windows de sèrie; a Linux depèn de la versió de Git i pot requerir configuració addicional. Mesura-ho abans i després al teu entorn concret.
git maintenance: que es mantingui sol
Programa tasques periòdiques al planificador del sistema: actualitzar el commit-graph, empaquetar objectes solts de manera incremental, fer prefetch dels remots i netejar referències.
Enfront del gc automàtic de sempre, té dos avantatges: s'executa quan no estàs treballant en lloc d'interrompre't, i inclou tasques que gc no fa, com el prefetch (que descarrega en segon pla el que altres publiquen, de manera que el teu git fetch sigui instantani).
Configuració recomanada per a un repositori gran:
git maintenance register
git config maintenance.commit-graph.enabled true
git config maintenance.prefetch.enabled true
git config maintenance.incremental-repack.enabled true
git config maintenance.loose-objects.enabled true
git config maintenance.gc.enabled false # el substitueixen les anteriors
git maintenance startAjustos agrupats
Git ofereix dos "paquets" de configuració que activen diverses coses alhora:
# Repositoris amb molts fitxers
git config feature.manyFiles true
# Els ajustos experimentals recomanats de la versió
git config feature.experimental truefeature.manyFiles activa index.version=4 (format d'índex més compacte), core.untrackedCache=true i index.skipHash=true. És la drecera raonable per a la dimensió 2. El que fa exactament pot canviar entre versions de Git, així que convé comprovar-ho amb git help config.
- Monorepo enfront de multirepo
Aquí es tanca la comparativa que la lliçó 06-05 va deixar oberta i que el cas 3 de la 10-01 va deixar pendent.
Les dues postures
Multirepo: cada projecte o biblioteca té el seu repositori. La relació entre ells s'estableix amb versions publicades (paquets), amb submòduls o amb subtree.
Monorepo: un únic repositori conté molts projectes, que es relacionen per rutes de directori i es compilen junts.
flowchart TB
subgraph multi["Multirepo"]
M1["repo: app-web"] -->|"depèn de v2.1.0"| M4["repo: components-ui"]
M2["repo: servei-dades"] -->|"depèn de v2.0.3"| M4
M3["repo: app-mobil"] -->|"depèn de v1.9.0"| M4
end
subgraph mono["Monorepo"]
R["repo únic"] --- A["/app-web"]
R --- B["/servei-dades"]
R --- C["/app-mobil"]
R --- D["/biblioteques/components-ui"]
end
Fixa't en les versions del diagrama de l'esquerra: tres consumidors fent servir tres versions diferents de la mateixa biblioteca. Això és el normal en multirepo, i és alhora el seu avantatge més gran (autonomia) i el seu problema més gran (deriva).
La comparativa completa
| Criteri | Monorepo | Multirepo |
|---|---|---|
| Canvi atòmic entre projectes | Sí: un commit canvia la biblioteca i els seus 30 consumidors. Mai no hi ha estat incoherent | No: 31 peticions d'extracció coordinades, versions de transició, setmanes |
| Refactorització global | Cerca i substitució + un commit | Un projecte en si mateix |
| Versionatge de dependències internes | No existeix: tothom fa servir main |
Cada consumidor fixa una versió; apareix deriva |
| Autonomia dels equips | Menor: comparteixen main, CI i eines |
Major: cada equip decideix el seu ritme i la seva publicació |
| Propietat del codi | Necessita CODEOWNERS per directori (10-01) |
Natural: per repositori |
| Control d'accés | Difícil de granular: qui clona ho veu tot | Natural: permisos per repositori |
| Integració contínua | Ha de ser selectiva, calculant el que està afectat | Simple: cada repositori, el seu |
| Mida del repositori | Creix sense límit; exigeix les tècniques d'aquesta lliçó | Cadascun es manté petit |
| Eines necessàries | Sistema de compilació amb graf de dependències, CI selectiva, escalat de Git | Gestor de paquets, registre d'artefactes |
| Descobriment de codi | Excel·lent: tot és cercable | Costa saber què existeix i on |
| Incorporació de gent nova | Un clon (gran) i ja ho tens tot | Cal esbrinar quins repositoris clonar |
| Publicació | Contínua; no hi ha versió global | Versions independents per projecte |
git bisect entre projectes |
Funciona: un sol historial | Impossible sense coordinació manual |
| Historial | Enorme i barrejat; cal filtrar per ruta | Net i específic per projecte |
| Cost de començar | Alt: no funciona sense invertir en eines | Baix: és el que surt per defecte |
Quan triar cadascun
El monorepo té sentit quan:
- Els projectes canvien junts sovint: el símptoma inequívoc és que una petició d'extracció necessita canvis coordinats en diversos repositoris.
- Hi ha biblioteques internes compartides i la deriva de versions ja fa mal.
- Tothom pot veure tot el codi.
- Hi ha capacitat d'invertir en eines i de mantenir-les.
- Es vol que la refactorització global sigui possible.
El multirepo té sentit quan:
- Els projectes són realment independents i evolucionen a ritmes diferents.
- Hi ha requisits d'accés que obliguen a separar.
- Els equips són autònoms i publiquen pel seu compte.
- Alguns components es publiquen fora de l'organització.
- No hi ha capacitat de mantenir eines pròpies.
- Es prefereix la solució que funciona sola.
La pregunta que decideix
De tots els criteris, un pesa més que els altres:
Amb quina freqüència un canvi necessita tocar diversos repositoris alhora?
Si la resposta és "gairebé mai", multirepo, sense dubtar-ho. Si és "constantment, i és el nostre coll d'ampolla més gran", el monorepo resol exactament aquest problema i mereix la inversió.
I recorda la conclusió de la lliçó 06-05: els submòduls no resolen això. Lliguen una versió concreta d'una biblioteca a un consumidor —donen traçabilitat— però cada actualització continua sent un commit a cada consumidor. Són un multirepo amb traçabilitat, no un monorepo barat.
El camí intermedi
No és una decisió binària ni irreversible. Els patrons intermedis més habituals:
-
Monorepo per domini. Un repositori per àrea gran (
plataforma,producte,dades), no un per servei ni un per a tot. Captura la major part del benefici d'atomicitat sense arribar a escales que exigeixin eines exòtiques. És el punt òptim per a la majoria d'organitzacions mitjanes. -
Monorepo per a les biblioteques compartides. S'agrupen només les biblioteques internes, que són les que pateixen la deriva de versions, i els productes continuen separats. És un pas reversible i amb benefici immediat.
-
Migració gradual. Es pot fusionar un repositori dins d'un altre conservant el seu historial:
cd ~/monorepo
git remote add components-ui https://git.exemple.cat/components-ui.git
git fetch components-ui
# Portar el seu historial reubicat sota un subdirectori
git merge -s ours --no-commit --allow-unrelated-histories components-ui/main
git read-tree --prefix=biblioteques/components-ui/ -u components-ui/main
git commit -m "chore: GT-270 incorpora components-ui com a biblioteques/components-ui
Es conserva l'historial complet del repositori original."La combinació merge -s ours + read-tree --prefix és el mecanisme clàssic: el merge connecta els dos historials sense portar contingut, i el read-tree col·loca l'arbre de l'altre repositori sota el prefix indicat. El resultat és que git log --follow biblioteques/components-ui/boto.js continua funcionant cap enrere. És també, essencialment, el que fa git subtree add per sota (lliçó 06-05).
- Què cal perquè un monorepo funcioni
Si la decisió és monorepo, aquestes peces no són opcionals. Un monorepo sense elles és simplement un repositori lent i caòtic.
- Escalat de Git (tot l'anterior)
# Clon estàndard per a l'equip
git clone --filter=blob:none --sparse https://git.exemple.cat/monorepo.git
cd monorepo
git sparse-checkout set serveis/tasques biblioteques/components-ui
# Acceleradors
git config core.fsmonitor true
git config core.untrackedCache true
git config index.sparse true
git config feature.manyFiles true
git maintenance startAixò convé posar-ho en un guió d'incorporació al mateix repositori, perquè ningú no recordarà sis ordres.
- Propietat del codi
El fitxer CODEOWNERS que vam veure a la lliçó 10-01. Sense ell, el monorepo perd la propietat clara que el multirepo dona de franc.
- Integració contínua selectiva
Ja en vam veure l'esquelet a la 10-01. La versió que importa aquí és la basada en el graf de dependències, no en directoris:
name: CI selectiva del monorepo
on: [pull_request]
jobs:
afectats:
runs-on: ubuntu-latest
outputs:
projectes: ${{ steps.calc.outputs.llista }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
filter: blob:none
- id: calc
run: |
# Rutes canviades des del punt de divergència
CANVIS=$(git diff --name-only origin/main...HEAD)
# El sistema de compilació tradueix rutes -> projectes afectats,
# incloent-hi els que depenen del que ha canviat
AFECTATS=$(./eines/afectats.sh $CANVIS)
echo "llista=$AFECTATS" >> "$GITHUB_OUTPUT"
echo "Projectes afectats: $AFECTATS"
provar:
needs: afectats
if: needs.afectats.outputs.projectes != ''
runs-on: ubuntu-latest
strategy:
matrix:
projecte: ${{ fromJson(needs.afectats.outputs.projectes) }}
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: |
${{ matrix.projecte }}
biblioteques
- run: ./eines/provar.sh ${{ matrix.projecte }}Fixa't en el sparse-checkout dins de la tasca de CI: cada execució materialitza només el projecte que provarà. És la mateixa tècnica de l'apartat 5, aplicada a la canonada.
El que fa afectats.sh és el que distingeix una CI selectiva bona d'una d'ingènua: si canvia biblioteques/components-ui, no n'hi ha prou de provar aquesta biblioteca; cal provar tots els projectes que en depenen. Això exigeix un graf de dependències explícit, que el proporciona el sistema de compilació.
- Sistema de compilació amb memòria cau
En un monorepo, compilar-ho tot a cada canvi és inviable. Cal un sistema que entengui el graf de dependències i que guardi els resultats a la memòria cau indexats pel hash de les entrades: si res del que entra en un objectiu no ha canviat, es reutilitza el resultat anterior.
És, curiosament, el mateix principi que sosté Git: identificar pel hash del contingut i no recalcular el que no ha canviat.
- Convencions i automatització de migracions
Amb molts projectes en un repositori, calen convencions fortes: estructura de directoris homogènia, noms predictibles, i eines per aplicar canvis massius ("reanomenar aquesta funció a 200 llocs") de manera automàtica i revisable.
- Quan NO cal res d'això
Aquest apartat és tan important com els anteriors.
gestor-tasques no necessita res d'aquesta lliçó. Ni clons parcials, ni sparse-checkout, ni fsmonitor, ni monorepo. Té nou fitxers, 412 commits i pesa 2 MB. git status respon en mil·lisegons.
I no és un cas excepcional: la immensa majoria dels repositoris del món estan en aquesta situació. Un projecte amb 5.000 fitxers, 30.000 commits i 200 MB va perfectament amb Git de sèrie en qualsevol ordinador modern.
El cost d'optimitzar sense necessitat
| Tècnica | Cost si no la necessites |
|---|---|
| Clon parcial | Latència inesperada en operacions normals; comportament estrany sense connexió; requereix servidor compatible |
sparse-checkout |
Fitxers que "no existeixen" i confonen tothom; eines que fallen sense explicació; grep que no troba coses que sí que hi són |
--depth |
Historial truncat, bisect inutilitzable, comparacions de petició d'extracció que fallen |
| Monorepo | Repositori gran sense cap dels avantatges, perquè els projectes no canviaven junts |
fsmonitor |
Un dimoni més consumint recursos, sense benefici mesurable |
La tècnica més perillosa de la llista és sparse-checkout. Un company nou que hereta una configuració dispersa sense saber-ho pot passar-se hores sense entendre per què un fitxer que veu al web no és al seu disc.
La regla
Si
git statusrespon en menys d'un segon igit clonetriga menys d'un minut, no tens cap problema que aquestes tècniques resolguin.
Mesura primer (apartat 2). Aplica el remei de la dimensió que faci mal. Mesura després. I si no fa mal res, no facis res.
- Una recepta per símptoma
Taula de consulta ràpida.
| Símptoma | Dimensió | Diagnòstic | Remei |
|---|---|---|---|
git clone triga moltíssim i el repositori pesa GB |
3 (i/o 1) | git count-objects -vH; buscar blobs grans |
LFS (10-03); --filter=blob:none; treure binaris |
git status triga segons |
2 | GIT_TRACE2_PERF mostra dir:untracked alt |
fsmonitor, untrackedCache, feature.manyFiles; si continua, sparse-checkout --cone --sparse-index |
git log -- ruta/fitxer triga moltíssim |
1 | Molts commits | commit-graph write --reachable --changed-paths |
git blame triga moltíssim |
1 | Moltes revisions del fitxer | commit-graph; blame -w -M no ajuda al rendiment |
git checkout entre branques és lent |
2 | Molts fitxers per actualitzar | sparse-checkout; fsmonitor |
| La CI clona 8 GB cinquanta vegades al dia | 1 i 3 | Configuració de la canonada | filter: blob:none + fetch-depth: 0; memòria cau de repositori; lfs: false (10-03) |
git gc triga una hora |
3 | Objectes grans incompressibles | LFS; git maintenance incremental en comptes de gc complet |
| Un canvi necessita 12 peticions d'extracció coordinades | Organitzativa | Multirepo amb acoblament fort | Considerar monorepo o monorepo per domini |
| El repositori va bé | Cap | — | No facis res |
Errors Habituals i Consells
Error 1: optimitzar sense mesurar. L'error central de la lliçó. Aplicar sparse-checkout a un problema d'historial no fa res. Diagnostica la dimensió primer.
Error 2: fer servir --depth=1 a la CI i després necessitar l'historial. Produeix fatal: no merge base i fatal: No names found. Fes servir fetch-depth: 0 amb filter: blob:none.
Error 3: sparse-checkout sense --cone. El mode de patrons és més lent, no permet índex dispers i és molt més fàcil de configurar malament. Fes servir --cone llevat que tinguis una raó molt concreta.
Error 4: oblidar que sparse-checkout està actiu. Documenta la configuració dispersa al README.md i recorda que git status avisa amb "You are in a sparse checkout with N% of tracked files present".
Error 5: creure que el clon parcial funciona sense connexió. Si treballes en un avió amb un clon parcial i executes git log -p sobre història antiga, fallarà: necessita anar al servidor. Abans de desconnectar-te, porta el que hagis de necessitar:
Error 6: triar monorepo per moda. Sense CI selectiva, sense CODEOWNERS i sense sistema de compilació amb memòria cau, un monorepo és pitjor que el que tenies. La pregunta no és "què fan les empreses grans?", sinó "amb quina freqüència un canvi nostre toca diversos repositoris?".
Error 7: creure que els submòduls són un monorepo barat. No ho són: no donen atomicitat. Cada actualització de la biblioteca continua exigint un commit a cada consumidor (lliçó 06-05).
Consell 1: commit-graph per a tothom. És l'única optimització de la lliçó sense cap inconvenient. Activa-la encara que el teu repositori sigui mitjà:
Consell 2: git maintenance start als teus repositoris grans. Millor que esperar el gc automàtic, i sense interrompre't.
Consell 3: mesura amb hyperfine o amb un bucle senzill. Abans i després, diverses vegades:
Consell 4: posa un guió d'incorporació al repositori. Si el teu monorepo necessita sis ordres de configuració, posa-les a eines/configurar.sh i menciona-ho al README.md.
Consell 5: la CI també pot fer servir sparse checkout. Els fluxos moderns permeten declarar quins directoris materialitzar. En un monorepo, això redueix cada tasca de minuts a segons.
Exercicis
Exercici 1: diagnòstic per dimensions
Per a cada repositori, digues quina dimensió pateix, què NO ho arreglaria i què sí:
A.
$ git rev-list --count --all 1847293 $ git ls-files | wc -l 1204 $ git count-objects -vH size-pack: 1.84 GiB $ time git status real 0m0.089s $ time git log --oneline -- src/nucli.c real 0m47.203s
B.
$ git rev-list --count --all 8420 $ git ls-files | wc -l 284917 $ git count-objects -vH size-pack: 3.21 GiB $ time git status real 0m41.887s
C.
$ git rev-list --count --all 1204 $ git ls-files | wc -l 89 $ git count-objects -vH size-pack: 6.72 GiB $ time git clone . real 8m12.443s
Exercici 2: configurar el lloc de treball en un monorepo
L'Ana s'incorpora a un equip amb un monorepo: 340.000 fitxers, 2,1 milions de commits, 12 GB. Ella treballarà només a serveis/tasques/ i farà servir biblioteques/components-ui/.
Escriu la seqüència completa d'ordres, des del clon fins a un lloc operatiu, explicant què ataca cadascuna i a quina dimensió. Inclou també què li diries sobre les rareses amb què es trobarà.
Exercici 3: decidir monorepo o multirepo
Una empresa de 120 persones té 23 repositoris: 8 serveis, 6 aplicacions client, 9 biblioteques internes.
Símptomes que reporten:
- Canviar la interfície de la biblioteca
autenticaciotriga sis setmanes a arribar a tots els consumidors. - Hi ha quatre versions diferents de
components-uien producció alhora. - Ningú no sap quins serveis fan servir quines biblioteques.
- Un col·laborador nou triga tres dies a clonar i configurar el que necessita.
- Dos dels serveis els manté una empresa externa que no ha de veure la resta del codi.
- Les biblioteques
graficsiutilitatses publiquen públicament com a codi obert.
Decideix què recomanaries, amb quins passos i en quin ordre. Assenyala explícitament què no ficaries al monorepo i per què.
Solucions
Solució 1
A. Dimensió 1 pura: molt historial.
Indicis: 1,8 milions de commits però només 1.204 fitxers. git status respon en 89 ms (la dimensió 2 no existeix). El repositori pesa 1,84 GB, cosa coherent amb 1,8 milions de commits de codi de text, no amb binaris.
El símptoma decisiu: git log sobre un fitxer triga 47 segons. Git ha de recórrer milions de commits comprovant si cadascun va tocar src/nucli.c.
Què NO ho arreglaria: sparse-checkout (només hi ha 1.204 fitxers i status ja és instantani), LFS (no hi ha binaris), fsmonitor (res per recórrer al disc).
Què sí:
# El remei principal: filtres de Bloom de rutes modificades
git commit-graph write --reachable --changed-paths
git config core.commitGraph true
git config fetch.writeCommitGraph true
# Que es mantingui sol
git maintenance start
# I per a clons nous, sobretot a la CI
git clone --filter=blob:none https://git.exemple.cat/projecte.gitAmb --changed-paths, aquest git log -- src/nucli.c de 47 segons hauria de baixar a menys d'un segon: el filtre de Bloom descarta la immensa majoria dels commits sense obrir-ne els arbres.
B. Dimensió 2 dominant: molts fitxers.
Indicis: 285.000 fitxers, només 8.420 commits (l'historial és curt), i git status triga 42 segons. Els 3,21 GB són consistents amb molts fitxers, no necessàriament amb binaris grans.
Què NO ho arreglaria: commit-graph (amb 8.420 commits, l'historial no és el problema), clon superficial (tampoc).
Què sí, en ordre de menor a major intrusió:
# 1. Primer el que és barat i sense efectes secundaris
git config core.untrackedCache true
git config core.fsmonitor true
git config feature.manyFiles true
# 2. Mesurar de nou
for i in 1 2 3; do /usr/bin/time -f "%e s" git status >/dev/null; done
# 3. Si continua fent mal, materialitzar només el necessari
git sparse-checkout init --cone --sparse-index
git config index.sparse true
git sparse-checkout set la/meva/area/de/treballL'ordre importa: els passos 1 i 2 no canvien el que veus al disc i poden resoldre-ho. El pas 3 sí que ho canvia i té cost de comprensió per a tot l'equip.
Convé a més comprovar si aquests 3,21 GB amaguen un problema de dimensió 3:
C. Dimensió 3 pura: fitxers molt grans.
Indicis inequívocs: 89 fitxers, 1.204 commits, 6,72 GB. L'aritmètica no deixa lloc a dubtes: 6,72 GB entre 89 fitxers són uns 75 MB per fitxer de mitjana. Són binaris.
Què NO ho arreglaria: absolutament res de sparse-checkout ni de commit-graph. Ni tan sols un clon superficial ajudaria gaire, perquè els blobs grans de l'última revisió ja pesen.
Què sí:
# 1. Confirmar el diagnòstic
git lfs migrate info --everything --above=1Mb
# 2. Decidir per fitxer (lliçó 10-03):
# generat? -> .gitignore
# no cal el seu historial? -> fora del repositori
# binari de producte amb historial? -> LFS
# 3. Migrar, amb tota la coordinació de la lliçó 10-03
git lfs migrate import --everything --include="*.psd,*.mp4,*.zip"
# 4. Pal·liatiu immediat mentre es decideix la migració
git clone --filter=blob:none https://git.exemple.cat/projecte.gitEl pas 4 és un alleujament útil: el clon parcial evita descarregar totes les versions històriques dels binaris, encara que no arregli la mida del repositori del costat del servidor.
Solució 2
# ============================================================
# 1. CLON: parcial + dispers des del principi
# Dimensions 1 i 3 (el que es descarrega)
# ============================================================
git clone --filter=blob:none --sparse \
https://git.exemple.cat/monorepo.git
cd monorepo--filter=blob:none evita descarregar el contingut històric de 2,1 milions de commits: de 12 GB passem a una fracció petita, conservant el graf complet. --sparse fa que el checkout inicial materialitzi només l'arrel, en lloc d'escriure 340.000 fitxers al disc.
# ============================================================
# 2. CON: què es materialitza al disc
# Dimensió 2 (el que hi ha a la còpia de treball)
# ============================================================
git sparse-checkout init --cone --sparse-index
git config index.sparse true
git sparse-checkout set serveis/tasques biblioteques/components-ui
git sparse-checkout list--cone permet l'índex dispers; --sparse-index i index.sparse fan que l'índex guardi una entrada per directori fora del con en lloc d'una per fitxer. De ~340.000 entrades a unes poques milers.
# ============================================================
# 3. ACCELERADORS DEL CLIENT
# ============================================================
git config core.fsmonitor true # dimensió 2: no recórrer el disc
git config core.untrackedCache true # dimensió 2: memòria cau de l'exploració
git config feature.manyFiles true # dimensió 2: índex v4, skipHash
git config core.commitGraph true # dimensió 1: recorregut del graf
git config fetch.writeCommitGraph true
# ============================================================
# 4. MANTENIMENT AUTOMÀTIC
# ============================================================
git maintenance register
git config maintenance.prefetch.enabled true
git config maintenance.commit-graph.enabled true
git config maintenance.incremental-repack.enabled true
git maintenance start
# ============================================================
# 5. VERIFICAR
# ============================================================
git ls-files | wc -l # només el que hi ha al con
git ls-files --sparse | wc -l # entrades reals de l'índex
for i in 1 2 3; do /usr/bin/time -f "%e s" git status >/dev/null; doneQuè li diria a l'Ana sobre les rareses:
-
"No veuràs la majoria dels fitxers."
git statust'avisarà: "You are in a sparse checkout with 2% of tracked files present". No estan esborrats: no estan materialitzats.git show HEAD:serveis/facturacio/main.jsfunciona perfectament. -
"Si necessites una altra àrea, afegeix-la."
git sparse-checkout add serveis/notificacions. No cal clonar de nou. -
"Algunes operacions aniran a la xarxa."
git log -psobre història antiga,git blamed'un fitxer amb moltes versions, ogit checkoutd'una etiqueta vella descarregaran blobs. La primera vegada és lenta; després queda a la memòria cau. -
"Abans d'anar-te'n sense connexió, porta el que hagis de necessitar." Un clon parcial necessita el servidor per al que no té.
-
"El teu editor i les teves cerques només veuen el teu con." Això és bo per al rendiment, però si busques una funció i no apareix, potser és fora de la teva àrea.
git grep --no-indexo buscar al web del repositori són la sortida. -
"Si alguna cosa es comporta de manera estranya, mira primer
git sparse-checkout list." És el primer sospitós de gairebé qualsevol raresa.
I li passaria un guió eines/configurar-lloc.sh amb tot l'anterior, perquè ningú no recorda catorze ordres.
Solució 3
Recomanació: monorepo parcial, en tres fases, començant per les biblioteques.
El símptoma dominant és inequívoc: sis setmanes per propagar un canvi de biblioteca i quatre versions de components-ui en producció. Això és exactament el problema que el monorepo elimina d'arrel, i confirma que hi ha acoblament fort entre les biblioteques i els seus consumidors.
Però hi ha dues restriccions que impedeixen un monorepo total, i cal respectar-les.
Què NO entra al monorepo, i per què:
| Què | Per què es queda fora |
|---|---|
| Els 2 serveis de l'empresa externa | Control d'accés. Un monorepo dona accés a tot a qui el clona. És la restricció més dura i no es negocia: són requisits contractuals o de confidencialitat, no preferències tècniques. |
Les biblioteques grafics i utilitats |
Es publiquen públicament. Un projecte de codi obert necessita el seu propi repositori: historial net i específic, incidències i peticions d'extracció pròpies, i cap risc d'exposar codi intern. Entren al monorepo com a dependència externa versionada, igual que qualsevol paquet de tercers. |
Fase 1: monorepo de biblioteques internes (7 biblioteques).
És el pas amb millor relació benefici/risc, i és reversible.
# Crear el repositori i absorbir cada biblioteca conservant-ne l'historial
mkdir plataforma && cd plataforma && git init
for BIB in autenticacio components-ui dades registre cues config validacio; do
git remote add "$BIB" "https://git.exemple.cat/$BIB.git"
git fetch "$BIB"
git merge -s ours --no-commit --allow-unrelated-histories "$BIB/main"
git read-tree --prefix="biblioteques/$BIB/" -u "$BIB/main"
git commit -m "chore: incorpora $BIB conservant-ne l'historial"
doneBenefici immediat: un canvi que toca autenticacio i dades alhora passa a ser un commit, i git bisect funciona entre biblioteques.
Aquestes biblioteques continuen publicant-se com a paquets versionats per als consumidors, de manera que res no canvia per als 12 productes. Per això és reversible.
Fase 2: absorbir els consumidors més acoblats.
Mesurar primer quins són:
# Quines peticions d'extracció dels últims sis mesos van tocar diversos
# repositoris el mateix dia i pel mateix tiquet?
for REPO in servei-a servei-b app-web app-mobil; do
echo "== $REPO"
git -C "$REPO" log --since=6.months --format='%ad %s' --date=short \
| grep -oE '^[0-9-]+ .*(TICKET-[0-9]+)' | head
doneEls que apareguin repetidament al costat de canvis de biblioteca són els candidats. S'absorbeixen aquests, no tots.
Fase 3: consolidar la resta, si l'experiència ha estat bona.
Inversió obligatòria abans de la fase 2 (sense això, el monorepo empitjora les coses):
CODEOWNERSper directori — recupera la propietat que el multirepo donava de franc.- CI selectiva amb graf de dependències — sense ella, cada petició d'extracció executa la suite sencera.
- Sistema de compilació amb memòria cau pel hash de les entrades — o els temps de compilació es disparen.
- Guió de configuració del lloc — amb
--filter=blob:none --sparse, com a la solució 2.
Què resol cada símptoma reportat:
| Símptoma | Com queda |
|---|---|
| Sis setmanes per propagar un canvi de biblioteca | Resolt: un commit atòmic actualitza biblioteca i consumidors |
Quatre versions de components-ui en producció |
Resolt: no hi ha versions internes, tothom fa servir main |
| Ningú no sap què fa servir què | Resolt: el graf de dependències és explícit i cercable |
| Tres dies de configuració inicial | Resolt: un clon parcial i dispers, amb guió |
| L'empresa externa no ha de veure la resta | Respectat: els seus 2 serveis queden fora |
grafics i utilitats són públiques |
Respectat: queden fora, com a dependències versionades |
Resultat final: 1 monorepo (7 biblioteques + els productes interns que s'absorbeixin), 2 repositoris privats per a l'empresa externa, 2 repositoris públics. De 23 repositoris a 5, sense violar cap restricció.
El que NO faria: ficar-ho tot de cop. La fase 1 és reversible i dona benefici en setmanes; una migració total de 23 repositoris sense eines és una manera habitual de tenir un repositori de 40 GB, una CI de dues hores i un equip furiós.
Conclusió
Escalar Git comença per deixar de tractar-lo com un sol problema.
- Són tres dimensions independents: molt historial (
git logiblamelents), molts fitxers (git statuslent) i fitxers molt grans (repositori de gigabytes). Cadascuna té el seu símptoma, la seva mesura i el seu remei, i aplicar el remei equivocat no arregla res. Es diagnostiquen ambgit rev-list --count --all,git ls-files | wc -ligit count-objects -vH, i s'afina ambGIT_TRACE2_PERF. - Els clons parcials són la tècnica més útil i menys coneguda:
--filter=blob:nonedescarrega el graf complet i omet el contingut històric, demanant-lo sota demanda;--filter=tree:0omet també els arbres. Conservenlog,bisect,describeimerge-base. - Enfront del clon superficial
--depth, el parcial és gairebé sempre millor:--depthprodueix un repositori incomplet on falten commits i les operacions fallen (fatal: no merge base), mentre que el parcial produeix un repositori complet amb contingut diferit. Per a la CI:fetch-depth: 0+filter: blob:none. sparse-checkout --conelimita el que es materialitza al disc, i l'índex dispers guarda una entrada per directori fora del con en lloc d'una per fitxer. El mode con renuncia a expressivitat per poder raonar sobre subarbres complets, cosa que només és possible perquè el hash d'un arbre resumeix tot el seu contingut (lliçó 01-04).- Els acceleradors del client —
commit-graph --changed-paths,fsmonitor,untrackedCache,feature.manyFilesigit maintenance— ataquen parts diferents del cost i es combinen bé. Elcommit-graphés l'únic sense inconvenients: activa'l sempre. - Monorepo enfront de multirepo es decideix amb una sola pregunta: amb quina freqüència un canvi necessita tocar diversos repositoris alhora? El monorepo compra atomicitat i refactorització global; ho paga amb eines pròpies,
CODEOWNERS, CI selectiva i escalat de Git. I hi ha camins intermedis: monorepo per domini, monorepo només de biblioteques, migració gradual ambmerge -s ours+read-tree --prefix. - I el més important: la majoria dels repositoris no necessiten res d'això.
gestor-tasquesté nou fitxers, 412 commits i 2 MB. Aplicar-lisparse-checkoutseria afegir confusió a canvi de res.
La regla que resumeix la lliçó, i que és la mateixa de la 08-06 portada a una altra escala:
Mesura, diagnostica la dimensió, aplica el remei d'aquesta dimensió, i torna a mesurar. Si
git statusrespon en menys d'un segon, no tens cap problema que aquestes tècniques resolguin.
El que ve
Ja sabem operar Git en repositoris de qualsevol mida. Queda l'última peça del món real, i és la que converteix Git en alguna cosa més que una eina de desenvolupadors.
A la lliçó 07-06 vam traçar una frontera: allà parlàvem d'integració contínua —comprovar automàticament que el que s'integra funciona— i vam deixar per a més endavant com aquest codi arriba als usuaris. També a la 07-04 vam ajornar la mecànica dels entorns i la promoció, i a la 05-05, en etiquetar v1.4.0, vam dir que les etiquetes anotades serien la peça que dispara un desplegament.
Tot això convergeix aquí. En moltes organitzacions, Git ja no el fa servir només una persona en un terminal: el fan servir cent canonades que clonen, etiqueten, construeixen i publiquen sense intervenció humana. El repositori deixa de contenir només codi i passa a contenir la definició de l'entorn, la configuració i la infraestructura. I un git push a la branca correcta deixa de ser "he desat la meva feina" per convertir-se en "això és a producció d'aquí a quatre minuts".
La lliçó 10-05: Git a DevOps tanca aquesta promesa: Git com a font única de veritat, els tres esglaons de CI, lliurament i desplegament, què dispara cadascun, per què l'artefacte s'ha d'identificar pel hash del commit, què és GitOps, on viuen els secrets en un món automatitzat, i per què revertir en producció gairebé mai no és un git revert.
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ó
