En tancar el mòdul 9 vam deixar l'Ana, el Bruno, la Carla i el Diego dominant Git sobre gestor-tasques: saben construir l'historial, reescriure'l amb criteri, col·laborar amb un procés i sortir dels embolics. Però gestor-tasques són quatre fitxers i quatre persones. El món real és més gran i més estrany.
Aquesta lliçó no ensenya ordres noves. Ensenya una cosa que a hores d'ara val més: criteri. Mirarem com fan servir Git quatre tipus de projecte molt diferents —des del més gran i més antic fins a un exactament igual que el vostre— i n'extraurem, de cadascun, quin problema resol el seu flux, quin preu paga per tenir-lo i què cal perquè funcioni.
La conclusió que busquem ja te l'avanço, perquè és l'única que cal endur-se'n: no existeix «el flux correcte de Git». Existeix el flux que encaixa amb la mida de l'equip, amb la confiança entre els seus membres, amb el ritme de publicació i amb el que costa equivocar-se. Copiar el flux del kernel de Linux en un equip de quatre persones és tan absurd com coordinar el kernel per pull requests d'una plataforma web.
Tot el que es descriu aquí és públic i ben documentat: els fluxos de treball del kernel són escrits a la seva pròpia documentació, els projectes de codi obert publiquen les seves guies de contribució, i les pràctiques de monorepo empresarial s'han descrit en articles i xerrades tècniques durant anys. Quan doni una magnitud, la donaré com a ordre de magnitud aproximat, mai com a dada exacta: el que importa és l'escala, no la xifra.
Contingut
- Com llegir un estudi de cas
- Cas 1: el kernel de Linux, el flux per correu electrònic
- La jerarquia de mantenidors i l'«arbre d'arbres»
- Per què el kernel no fa servir pull requests de plataforma
- Què exigeix el model del kernel a qui hi contribueix
- Cas 2: un projecte de codi obert de mida mitjana
- La infraestructura social d'un projecte obert
- Cas 3: una empresa amb monorepo
- Cas 4: un equip petit de producte (el nostre)
- Els quatre models, comparats
- La lliçó transversal: el flux es tria, no s'hereta
- Com llegir un estudi de cas
Abans de mirar cap projecte convé fixar les preguntes. Tot flux de treball amb Git, per complex que sembli, respon a cinc preguntes:
| Pregunta | Què determina |
|---|---|
| Com proposa un canvi algú de fora del nucli? | El mecanisme de proposta: correu, pull request, branca directa |
| Qui decideix que un canvi hi entra? | L'estructura d'autoritat: un mantenidor, dos revisors, un propietari de directori |
| Sobre quina branca es treballa i durant quant de temps? | El model de branques i la seva vida mitjana |
| Quin aspecte té l'historial resultant? | Si és lineal, si té fusions, si cada commit compila |
| Què cal perquè això funcioni? | La infraestructura i la disciplina necessàries |
La cinquena és la que gairebé sempre s'ignora, i la que explica per què un flux copiat d'un altre lloc fracassa. Un flux no és només una seqüència d'ordres: és una seqüència d'ordres més les eines, les normes i els hàbits que la sostenen.
Respondrem aquestes cinc preguntes per a cada cas.
- Cas 1: el kernel de Linux, el flux per correu electrònic
El kernel de Linux és el projecte per al qual es va escriure Git, el 2005, quan l'equip va perdre l'accés a l'eina que feia servir fins llavors. És també el projecte que continua fent servir el flux més antic i, per a gairebé tothom, el més sorprenent: els canvis es proposen per correu electrònic, en llistes públiques, en forma de pedaços de text.
Les magnituds, com a ordres de magnitud: desenes de milions de línies de codi, desenes de milers de fitxers, més de vint anys d'historial a Git (i més de trenta de projecte), i de l'ordre de milers de persones contribuint-hi cada any. Cap equip petit no s'assembla a això, i per això el cas és instructiu: mostra què passa quan portes la col·laboració amb Git al seu límit.
El cicle d'un pedaç
flowchart TD
A["Desenvolupador<br/>fa commits locals"] --> B["git format-patch<br/>genera fitxers .patch"]
B --> C["git send-email<br/>a la llista del subsistema"]
C --> D["Revisió pública<br/>a la llista de correu"]
D -->|"canvis demanats"| E["Nova versió: v2, v3…"]
E --> C
D -->|"acceptat"| F["Mantenidor:<br/>git am aplica el pedaç"]
F --> G["Arbre del subsistema"]
G --> H["git request-pull<br/>al mantenidor superior"]
H --> I["Arbre principal (Linus)"]
Fixa't en un detall important: el pedaç viatja com a text, no com una referència a un repositori. Qui el rep no necessita afegir un remot, ni tenir accés al repositori de qui l'envia, ni tan sols saber si aquest repositori existeix. Rep un correu, i aquest correu conté el canvi sencer.
git format-patch: convertir commits en correus
Cada fitxer és un correu complet: capçaleres, assumpte pres de la primera línia del missatge del commit, cos del missatge, i el diff al final. Si apliquem això al nostre projecte per veure-ho per dins:
From 8a1f6c3d4e5b6a7c8d9e0f1a2b3c4d5e6f7a8b9c Mon Sep 17 00:00:00 2001 From: Ana Ferrer <[email protected]> Date: Fri, 31 Jul 2026 13:11:58 +0200 Subject: [PATCH 1/2] GT-142 mostra el comptador de pendents S'afegeix un comptador a la capçalera amb el nombre de tasques no completades, que es recalcula en marcar o desmarcar una tasca. Signed-off-by: Ana Ferrer <[email protected]> --- app.js | 12 ++++++++++++ index.html | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-)
Aquest From 8a1f6c3d... de la primera línia no és una capçalera de correu real: és el hash del commit original, que Git hi desa perquè qui l'apliqui pugui saber d'on va sortir. La data Mon Sep 17 00:00:00 2001 és un valor sentinella històric que Git escriu sempre; no significa res.
Opcions que es fan servir de debò:
# Numerar i afegir una carta de presentació (0000-cover-letter.patch)
git format-patch main --cover-letter --numbered
# Marcar que és la segona versió de la sèrie després de la revisió
git format-patch main -v2
# Només els tres últims commits
git format-patch -3
# Amb el nom de la branca a l'assumpte, útil per a sèries llargues
git format-patch main --subject-prefix="PATCH gestor-tasques"La carta de presentació (--cover-letter) és el correu 0/N de la sèrie: explica el conjunt, no cada pedaç. Al kernel és on s'argumenta el perquè del canvi complet, i on es resumeix què ha canviat respecte de la versió anterior de la sèrie.
git send-email: enviar-los
git send-email [email protected] \
[email protected] \
0001-*.patch 0002-*.patchgit send-email els envia com un fil de correu: la carta de presentació és el missatge arrel i els pedaços en pengen com a respostes. Cal configurar un servidor SMTP:
git config --global sendemail.smtpServer smtp.exemple.cat
git config --global sendemail.smtpUser [email protected]
git config --global sendemail.smtpEncryption tls
git config --global sendemail.smtpServerPort 587És l'ordre que més gent ha instal·lat i mai no ha arribat a fer servir. Però convé conèixer-la: quan algú et digui «envia'm el pedaç», això és el que vol dir.
git am: aplicar el pedaç rebut
A l'altra banda, el mantenidor desa el correu i l'aplica:
# Aplicar una sèrie de pedaços en ordre
git am 0001-*.patch 0002-*.patch
# Aplicar directament des d'una bústia de correu
git am /ruta/a/la/bustia.mbox
# Afegir la meva pròpia signatura en aplicar-lo (pràctica estàndard al kernel)
git am --signoff 0001-*.patcham vol dir apply mailbox. Crea un commit per pedaç conservant l'autor original i posant el mantenidor com a committer. Aquí es veu per primera vegada amb sentit pràctic la distinció autor/committer que va aparèixer al model de dades (lliçó 01-04): el kernel la fa servir constantment, perquè qui escriu el codi i qui l'integra gairebé mai no són la mateixa persona.
Si un pedaç no s'aplica netament:
# Veure on ha fallat
git am --show-current-patch=diff
# Intentar una aplicació amb més marge fent servir la informació de blobs
git am -3
# Arreglar a mà, marcar com a resolt i continuar
git add .
git am --continue
# O abandonar la sèrie sencera i tornar a l'estat previ
git am --abortgit am -3 (fusió a tres bandes) és el que salva la majoria dels casos: fa servir els hashos de blob que el pedaç porta incrustats per reconstruir el context i fer una fusió real en lloc d'una aplicació textual. És exactament el mecanisme de tres bandes de la lliçó 03-03, aplicat a un pedaç.
git request-pull: demanar que integrin el teu arbre
Quan el mantenidor d'un subsistema ja té els pedaços al seu repositori i vol que el nivell superior els integri, no envia pedaços: envia una petició d'extracció per correu, generada per Git:
The following changes since commit 3f2a1b9c...:
Linux 6.10 (2026-06-14 18:02:11 -0700)
are available in the Git repository at:
https://git.exemple.cat/subsistema-xarxa.git per-a-6.11
for you to fetch changes up to 9d4e7f2a...:
net: afegeix prova de regressio (2026-07-28 11:40:03 +0200)
----------------------------------------------------------------
Ana Ferrer (2):
net: corregeix fuita de memoria al driver
net: afegeix prova de regressio
drivers/net/exemple.c | 24 +++++++++++++++++-------
1 file changed, 17 insertions(+), 7 deletions(-)Això és el pull request original, el de debò: un correu que diu «en aquest repositori, en aquesta branca, des d'aquest commit base, hi ha aquests canvis; emporta-te'ls». Les plataformes web que fem servir cada dia no van inventar el concepte: hi van posar una interfície a sobre. Tornarem sobre request-pull a la lliçó 10-02, com a pont entre els dos mons.
- La jerarquia de mantenidors i l'«arbre d'arbres»
El kernel no té un repositori: en té centenars. Cada subsistema —xarxa, sistemes de fitxers, gràfics, una arquitectura de processador concreta— té el seu propi repositori públic i el seu propi mantenidor.
flowchart BT
D1["Col·laborador"] --> S1["Arbre d'un driver"]
D2["Col·laborador"] --> S1
D3["Col·laborador"] --> S2["Arbre d'un altre driver"]
S1 --> M1["Arbre del subsistema<br/>(xarxa)"]
S2 --> M1
S3["Arbre del subsistema<br/>(sistemes de fitxers)"] --> T["Arbre principal"]
M1 --> T
S4["Arbre del subsistema<br/>(gràfics)"] --> T
T --> R["Publicació<br/>vX.Y"]
El flux de la informació és de baix cap a dalt, per confiança. Cada nivell confia en el de sota perquè coneix aquestes persones i n'ha revisat la feina durant anys, i respon davant del de dalt pel que hi integra. L'arbre principal no revisa cada pedaç: revisa peticions d'extracció de mantenidors en qui confia.
Això té tres conseqüències tècniques molt visibles a l'historial:
- L'historial principal és ple de commits de fusió entre arbres de subsistema. No és desordre: cada fusió documenta «en aquesta data vaig integrar la feina d'aquest subsistema». El missatge d'aquestes fusions sol ser el text del
request-pullcorresponent. - Els pedaços individuals arriben ja nets, perquè s'han discutit i reescrit a la llista abans d'entrar en cap arbre. La netedat no s'aconsegueix reescrivint després: s'aconsegueix no deixant entrar el que és brut.
- El ritme és de finestra. Hi ha períodes en què l'arbre principal accepta canvis nous i períodes en què només accepta correccions, tancant amb una publicació. Aquest ritme fa predictible la feina de tots els nivells inferiors.
Signed-off-by i el certificat d'origen
Cada commit del kernel porta una o diverses línies al final:
Signed-off-by: Ana Ferrer <[email protected]> Signed-off-by: Bruno Salas <[email protected]>
No és una signatura criptogràfica (això és git commit -S, lliçó 08-05). És una declaració legal: qui la posa certifica que té dret a aportar aquest codi sota la llicència del projecte. S'anomena Developer Certificate of Origin, és un text públic i curt, i s'afegeix amb:
Cada nivell de la jerarquia hi afegeix la seva en integrar, de manera que el commit acaba portant la cadena completa de custòdia: qui el va escriure i per quines mans ha passat fins a l'arbre principal. És traçabilitat social escrita al mateix objecte commit.
- Per què el kernel no fa servir pull requests de plataforma
És la pregunta òbvia. La resposta no és «per costum» ni «per resistència al canvi», encara que la inèrcia hi pesi. Hi ha raons estructurals:
| Raó | Explicació |
|---|---|
| Independència de proveïdor | Dependre d'una plataforma concreta significa que la seva política, la seva disponibilitat i el seu preu condicionen el projecte. El correu el serveix qualsevol. |
| La revisió és el producte | En una llista, la discussió tècnica queda arxivada, és citable, es pot respondre en línia sobre el codi mateix i la llegeix gent que no seguia aquell canvi. És una revisió pública per defecte, no privada per defecte. |
| Escala de participants | Milers de persones revisant en paral·lel centenars de sèries simultànies funciona millor amb filtres de correu que amb notificacions d'una interfície web. |
| Ningú no necessita permisos | Per proposar un canvi no cal cap compte, ni cap fork, ni que ningú et doni accés a res. N'hi ha prou de saber l'adreça de la llista. |
| El pedaç és autocontingut | Es pot aplicar, desar, reenviar, citar i arxivar sense dependre que cap servidor continuï existint d'aquí a quinze anys. |
I també hi ha costos, que convé dir amb la mateixa claredat:
- La barrera d'entrada és alta. Configurar el correu sortint perquè no destrossi els pedaços és un ritu de pas famós pel dolor que provoca.
- No hi ha estat centralitzat de «en quin punt és aquesta proposta». Existeixen eines externes de seguiment de pedaços per pal·liar-ho, però no és el mateix que un plafó.
- La integració contínua és més difícil de lligar a una proposta concreta, perquè la proposta és un correu, no una branca d'un servidor.
Els últims anys han aparegut eines que automatitzen les parts tedioses (enviar sèries, recuperar-les des dels arxius públics, gestionar versions successives) sense abandonar el correu. La tendència no és substituir el flux, sinó fer-lo menys aspre.
- Què exigeix el model del kernel a qui hi contribueix
Aquest és el punt de la lliçó on el cas deixa de ser una curiositat i comença a ser útil per a tu, encara que no enviïs mai cap pedaç al kernel.
Exigència 1: cada commit ha de ser correcte per si sol. No «el conjunt funciona»: cada commit compila i passa les proves, perquè git bisect (lliçó 06-02) ha de poder aturar-s'hi en qualsevol d'ells. Això obliga a partir la feina en passos coherents, i és el que converteix una sèrie de pedaços en una cosa revisable.
Exigència 2: el missatge es llegeix sol. Un mantenidor rep el pedaç en un correu, sense accés al teu tiquet, sense el teu context i sense poder preguntar-te al passadís. El missatge és la documentació del canvi. Tot el que vam veure a la lliçó 08-01 —el perquè abans que el què, l'imperatiu, el cos que explica l'alternativa descartada— aquí no és una bona pràctica: és un requisit de funcionament.
Exigència 3: una sèrie és una narració. Els pedaços s'ordenen de manera que cadascun prepari el següent: primer la refactorització que no canvia el comportament, després el canvi de comportament, després les proves. És exactament l'ús del rebase interactiu de la lliçó 05-02, amb un propòsit editorial.
Exigència 4: acceptes reescriure. La versió 5 d'una sèrie és normal. Es refà amb rebase -i, es torna a enviar amb -v5, i a la carta de presentació s'explica què ha canviat respecte de la v4. Per a això serveix git range-diff (lliçó 07-02), que compara dues versions d'una mateixa sèrie.
Aplicat al nostre projecte, així es prepararia l'Ana per enviar una sèrie en aquest estil:
# 1. Netejar la sèrie: reordenar, fusionar els «corregeix typo», reescriure missatges
git rebase -i main
# 2. Comprovar que cada commit compila, un per un
git rebase main --exec "npm test"
# 3. Generar la v2 i comparar-la amb la v1 que ja vaig enviar
git format-patch main -v2 --cover-letter
git range-diff main v1-final mainEl --exec del segon pas és una joia poc coneguda: executa aquesta ordre després d'aplicar cada commit del rebase, i s'atura al primer que falli. És la manera mecànica de garantir l'exigència 1.
- Cas 2: un projecte de codi obert de mida mitjana
Baixem una escala. Parlem d'un projecte amb centenars de col·laboradors, desenes d'ells habituals, un grapat de mantenidors, i una plataforma d'allotjament (git.exemple.cat al nostre univers fictici). Una biblioteca popular, un marc de treball, una eina de línia d'ordres coneguda.
Aquest és exactament el model del Diego, el col·laborador extern de la lliçó 07-01, multiplicat per dos-cents.
El flux
flowchart LR
A["Fork personal"] --> B["Branca de tema"]
B --> C["Pull request<br/>al repositori principal"]
C --> D["CI automàtica"]
C --> E["Revisió humana"]
D --> F{"Tot verd?"}
E --> F
F -->|sí| G["Condensat i fusió<br/>a main"]
F -->|no| B
Res d'això no és nou per a tu: és el mòdul 7 sencer. El que canvia amb l'escala no és la mecànica, sinó quanta feina fa la infraestructura perquè els mantenidors no s'ofeguin.
L'aritmètica del mantenidor
La dada que explica tota la resta: en un projecte així, la revisió és el recurs escàs. Hi ha centenars de persones capaces d'escriure una proposta i potser cinc amb dret i temps per integrar-la. Cada minut que un mantenidor gasta explicant com s'executen les proves, demanant que es faci rebase d'una branca o tancant propostes duplicades és un minut que no dedica a revisar codi.
D'aquí que tot el disseny del procés persegueixi un objectiu: que la proposta arribi ja en condicions.
- La infraestructura social d'un projecte obert
Aquestes són les peces concretes, i mereixen atenció perquè són directament aplicables a qualsevol repositori, inclòs el vostre.
CONTRIBUTING.md
Un fitxer a l'arrel del repositori que les plataformes mostren automàticament a qui obrirà una proposta. Conté allò que un mantenidor està cansat de repetir:
# Com contribuir a gestor-tasques
## Abans de començar
- Obre una incidència abans d'escriure codi per a un canvi gran.
- Mira si ja existeix una proposta oberta sobre el mateix.
## Preparar l'entorn
git clone https://git.exemple.cat/gestor-tasques.git
npm install
npm test
## Convencions
- Missatges de commit: Conventional Commits (feat:, fix:, docs:…).
- Una proposta, un tema. Si la teva branca toca dues coses, parteix-la en dues.
- Fes rebase sobre `main` abans de demanar revisió; no fusionis `main` a la teva branca.
## Què has d'esperar
- La CI ha d'estar en verd abans que la revisem.
- Un mantenidor respondrà en uns dies laborables.
- Integrem amb condensat: la teva branca es converteix en un commit a `main`.Fixa't en l'última línia. Dir per endavant quina política d'integració s'aplica (fusió, condensat o rebase, lliçó 08-02) evita la discussió més recurrent a les propostes de gent nova.
Plantilles de proposta i d'incidència
Fitxers dins d'un directori de configuració de la plataforma que precarreguen el formulari. La plantilla de proposta sol ser una llista de comprovació:
## Què canvia
<!-- Descripció breu del canvi i del problema que resol -->
## Tiquet relacionat
Tanca GT-
## Comprovacions
- [ ] He executat `npm test` en local i passa
- [ ] He afegit proves per al comportament nou
- [ ] He actualitzat el README si el canvi afecta l'ús
- [ ] Els meus commits segueixen Conventional CommitsEl seu efecte real no és documental, és psicològic: qui obre la proposta veu les caselles buides i se li acut revisar-les abans que li ho demanin.
Etiquetes
Un sistema d'etiquetes ben pensat converteix una llista inabastable d'incidències en un mapa navegable:
| Família d'etiquetes | Exemples | Per a què serveix |
|---|---|---|
| Tipus | error, millora, documentació |
Classificar d'un cop d'ull |
| Estat | necessita-revisió, esperant-resposta, bloquejat |
Saber de qui és el torn |
| Dificultat | primera-contribució, ajuda-buscada |
Canalitzar la gent nova |
| Àrea | interfície, emmagatzematge, ci |
Encaminar cap al mantenidor adequat |
L'etiqueta primera-contribució (o el seu equivalent habitual en anglès) és la més rendible de totes: és com un projecte converteix lectors en col·laboradors.
Automatització d'allò repetitiu
En un projecte d'aquesta escala, els robots fan la feina que a gestor-tasques fa una persona:
name: Comprovacions de proposta
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
validar:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Comprovar el format dels missatges de commit
run: |
git log --format=%s origin/main..HEAD | while read -r assumpte; do
echo "$assumpte" | grep -qE '^(feat|fix|docs|refactor|test|chore)(\(.+\))?: .+' \
|| { echo "Missatge no conforme: $assumpte"; exit 1; }
done
- name: Verificar que la branca està al dia amb main
run: |
git merge-base --is-ancestor origin/main HEAD \
|| echo "::warning::Fes rebase sobre main abans de demanar revisió"
- name: Proves
run: npm ci && npm testAquest git merge-base --is-ancestor és una ordre de lampisteria (lliçó 09-06) que respon amb un codi de sortida: és origin/main un avantpassat de HEAD? Si ho és, la branca està al dia.
Quin historial produeix aquest model
Gairebé sempre lineal a main, amb un commit per proposta integrada, missatge normalitzat i una referència al número de la proposta. És el resultat de la política de condensat, i té una virtut enorme a aquesta escala: git log --oneline a main es llegeix com un registre de canvis del projecte, no com el diari de dues-centes persones.
El preu, ja discutit a la lliçó 08-02, és que es perd el detall intern de cada proposta. En un projecte obert s'accepta de bon grat, perquè aquest detall continua existint a la proposta mateixa, arxivada a la plataforma.
- Cas 3: una empresa amb monorepo
Tercer model, radicalment diferent: una sola empresa, un sol repositori, molts productes a dins. L'aplicació web, el servei de dades, l'aplicació mòbil, les biblioteques compartides, les definicions d'infraestructura: tot al mateix arbre de directoris i al mateix historial.
Diverses empreses grans han descrit públicament aquest model durant anys. Les magnituds, sempre com a ordre de magnitud: centenars de milers a milions de fitxers, milions de commits, i milers de persones treballant sobre el mateix repositori. A aquesta escala, Git de sèrie no n'hi ha prou, i aquest és el tema de la lliçó 10-04. Aquí ens interessa el perquè, no el com.
Per què algú tria això
Raó 1: el canvi atòmic entre projectes. Si la biblioteca compartida canvia d'interfície i hi ha trenta consumidors, en un monorepo això és un sol commit que actualitza la biblioteca i els trenta alhora. Mai no existeix un instant en què el repositori sigui incoherent. Amb repositoris separats, aquest mateix canvi són trenta-una propostes coordinades, versions intermèdies de compatibilitat i setmanes de feina.
Aquest és exactament el problema que vam deixar obert a la lliçó 06-05 amb components-ui: quan canvia el submòdul, cal actualitzar el punter al repositori pare, i durant una estona les dues coses no quadren. En un monorepo aquest problema no existeix perquè no hi ha dos repositoris.
Raó 2: la refactorització global és possible. Reanomenar una funció usada a tota l'empresa és una cerca i substitució més un commit. Amb repositoris separats, és un projecte.
Raó 3: una sola versió de tot. No hi ha «quina versió de la biblioteca fa servir el servei de facturació». Fa servir la que és a main, igual que tothom.
Raó 4: visibilitat i reutilització. Qualsevol pot llegir qualsevol codi, trobar qui el manté i proposar-hi un canvi.
El preu
| Cost | Com es paga |
|---|---|
| Git de sèrie s'ofega | Clons parcials, sparse-checkout, servidors especialitzats, memòries cau de compilació (10-04) |
| Tothom ho veu tot | Pot ser inacceptable si hi ha codi amb requisits de confidencialitat |
| La CI no ho pot executar tot | Cal CI selectiva: calcular què queda afectat pel canvi |
| Eines pròpies | Sistema de compilació amb graf de dependències, eines de propietat de codi, automatització de migracions |
| Ningú no pot clonar i començar | La incorporació de gent nova requereix formació en l'entorn |
Propietat del codi per directoris
La peça organitzativa clau. Un fitxer de text al repositori declara qui ha d'aprovar els canvis de cada zona de l'arbre:
# CODEOWNERS # Regla per defecte: l'equip de plataforma * @plataforma # Cada equip és amo del seu directori /serveis/facturacio/ @equip-facturacio /serveis/tasques/ @equip-tasques /biblioteques/components-ui/ @equip-disseny @equip-tasques # El que és crític exigeix aprovació addicional /infra/produccio/ @plataforma @seguretat /biblioteques/autenticacio/ @seguretat
La plataforma llegeix aquest fitxer, calcula quins directoris toca cada proposta i assigna automàticament els revisors obligatoris. Combinat amb les branques protegides de la lliçó 07-06, el resultat és que l'autonomia es recupera sense renunciar al repositori únic: cada equip mana en el que és seu, però tocar la biblioteca d'autenticació requereix el vistiplau de seguretat.
És la resposta a l'objecció més freqüent contra el monorepo. «Si està tot junt, qualsevol pot canviar qualsevol cosa?» Tècnicament sí; a la pràctica, no sense l'aprovació de qui és amo d'aquella zona.
Integració contínua selectiva
Si el repositori té mil projectes, executar les proves dels mil a cada proposta és impossible. La solució és calcular el conjunt afectat:
name: CI selectiva
on: [pull_request]
jobs:
detectar:
runs-on: ubuntu-latest
outputs:
afectats: ${{ steps.calc.outputs.llista }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- id: calc
name: Quins projectes toca aquesta proposta
run: |
# Fitxers modificats respecte del punt de divergència amb main
CANVIS=$(git diff --name-only origin/main...HEAD)
# Primer nivell de directori = projecte
PROJECTES=$(echo "$CANVIS" | cut -d/ -f1-2 | sort -u | tr '\n' ' ')
echo "llista=$PROJECTES" >> "$GITHUB_OUTPUT"
provar:
needs: detectar
if: needs.detectar.outputs.afectats != ''
runs-on: ubuntu-latest
steps:
- run: echo "Provant només ${{ needs.detectar.outputs.afectats }}"Aquest origin/main...HEAD amb tres punts és el mateix de la revisió de codi de la lliçó 07-02: els canvis de la branca des que se'n va separar, no les diferències amb l'estat actual de main. És un detall que aquí importa molt: amb dos punts, qualsevol avanç de main faria creure a la canonada que la proposta toca projectes que no ha tocat.
A la pràctica, un sistema seriós no fa servir directoris sinó el graf de dependències del sistema de compilació: si canvia components-ui, també es proven tots els projectes que en depenen. Però el principi és el mateix, i arrenca sempre en un git diff --name-only.
- Cas 4: un equip petit de producte (el nostre)
Quart cas: quatre persones, un producte, una publicació cada poques setmanes. És a dir, gestor-tasques. És també, de bon tros, el cas més comú: la immensa majoria dels equips del món s'assemblen molt més a aquest que als tres anteriors.
Recapitulant el que aquest equip ha anat construint al llarg del curs, sense repetir-ne el detall:
- Un repositori a
git.exemple.cat, méscomponents-uicom a submòdul (06-05). - Branques de tema curtes amb nom de tiquet (
GT-231-filtre-ocultes), integrades amainmitjançant proposta i revisió (07-01, 07-02), amb un flux proper a GitHub Flow (07-04). - Integració contínua a cada proposta i branca protegida a
main(07-06). - Conventional Commits i una política d'integració acordada (08-01, 08-02).
- Etiquetes anotades amb SemVer per publicar (05-05).
- El Diego, col·laborador extern, treballa per fork i proposta (07-01).
L'interessant d'aquest cas és el que NO té, i no necessita:
| No té | Per què no li cal |
|---|---|
| Jerarquia de mantenidors | Quatre persones es parlen entre elles; la jerarquia seria teatre |
| Flux per correu | Tots tenen accés d'escriptura al mateix servidor |
CODEOWNERS |
Qualsevol pot revisar qualsevol fitxer, i és sa que sigui així |
| CI selectiva | Les proves del projecte sencer triguen dos minuts |
Clons parcials o sparse-checkout |
El repositori pesa menys que una fotografia |
| Git LFS | No hi ha binaris grans… encara (10-03) |
| Branques de publicació de llarga vida | Només hi ha una versió en producció alhora |
Cadascuna d'aquestes absències és una decisió de disseny, no una mancança. Afegir-ne qualsevol «perquè els projectes seriosos ho fan» empitjoraria el flux: més passos, més cerimònia, més llocs on equivocar-se, i zero problemes resolts.
Aquest és el biaix professional més car que existeix en aquesta matèria: confondre la complexitat del procés amb la maduresa de l'equip.
- Els quatre models, comparats
| Kernel de Linux | Projecte obert mitjà | Monorepo d'empresa | Equip de producte | |
|---|---|---|---|---|
| Mida | Milers de persones, centenars de subsistemes | Centenars de col·laboradors, pocs mantenidors | Centenars o milers de persones, un repositori | 3–15 persones |
| Mecanisme de proposta | Pedaç per correu (format-patch/send-email/am) |
Fork + pull request en plataforma | Branca al repositori central + proposta | Branca al repositori central + proposta |
| Qui decideix | Jerarquia de mantenidors per confiança | Mantenidors del projecte | Propietaris del directori (CODEOWNERS) |
Qualsevol company de l'equip |
| Flux de branques | Arbres independents que es fusionen cap amunt; finestres d'integració | Branques de tema curtes sobre main |
Branques de tema molt curtes; main sempre desplegable |
Branques de tema curtes sobre main |
| Historial resultant | Moltes fusions entre arbres; cada commit vàlid per si sol | Lineal, un commit per proposta (condensat) | Lineal i d'altíssim volum | Lineal o gairebé, llegible a ull nu |
| Reescriptura | Constant i esperada (v2, v3…) abans d'integrar |
Freqüent abans d'integrar | Poca: s'integra ràpid i petit | Ocasional, per netejar la branca |
| Publicació | Finestres i cicle de candidates | Etiquetes SemVer | Contínua, sense versió global | Etiquetes SemVer |
| Què cal | Disciplina extrema en commits i missatges; cultura de revisió pública; tolerància a la reescriptura | CONTRIBUTING.md, plantilles, etiquetes, CI fiable, mantenidors amb temps |
Eines pròpies, CI selectiva, escalat de Git, propietat del codi | Confiança mútua, CI bàsica, convencions acordades |
| Falla quan | Algú envia un pedaç sense context o sense partir | Els mantenidors se saturen o la CI és lenta i inestable | Les eines no segueixen el ritme del repositori | S'afegeix cerimònia que ningú no necessita |
Llegeix-la en vertical, no en horitzontal: cada columna és un sistema coherent, on cada casella sosté les altres. La disciplina extrema del kernel és possible perquè hi ha una jerarquia de confiança que l'exigeix; la jerarquia és sostenible perquè els pedaços arriben impecables. Treure una casella de la seva columna i ficar-la en una altra sol produir un desastre.
- La lliçó transversal: el flux es tria, no s'hereta
Si hagués de reduir aquesta lliçó a una frase seria aquesta: el flux de treball és una resposta a unes restriccions concretes. Canvia les restriccions i la resposta correcta canvia.
Les restriccions que de debò importen són quatre:
1. Quanta confiança hi ha entre qui hi contribueix? Amb confiança alta (un equip intern), l'accés d'escriptura directe i la revisió lleugera funcionen. Amb confiança baixa o desconeguda (qualsevol d'internet), cal un mecanisme on la proposta arribi sense permisos i l'aprovació sigui explícita: fork i proposta, o pedaç per correu.
2. Quant costa un error en producció? Si tornar a desplegar costa trenta segons, et pots permetre integrar ràpid i corregir després (07-05). Si l'error implica retirar microprogramari de dispositius, necessites finestres, candidates i branques d'estabilització (07-03).
3. Quantes versions tens vives alhora? Una sola versió en producció permet un main únic. Mantenir tres versions simultànies amb correccions de seguretat obliga a branques de llarga vida i a cherry-pick sistemàtic (05-03).
4. Quant pesa el repositori i quanta gent el toca? És el que decideix si necessites les tècniques de la lliçó 10-04 o si Git de sèrie et sobra durant els pròxims deu anys.
L'antipatró
L'error clàssic té un guió sempre igual:
Algú llegeix un article sobre com treballa una empresa enorme. Proposa adoptar-ho. S'instal·len cinc eines, es defineixen set tipus de branca i una política d'aprovació de tres nivells. Dos mesos després, en un equip de cinc persones, integrar un canvi de dues línies triga tres dies i ningú no recorda per què.
El procés d'una organització de cinc mil persones és la cicatriu de problemes que aquella organització va tenir. Si no tens aquests problemes, el que estàs copiant no és una solució: és una cicatriu aliena.
La recepta sana
- Comença amb el flux més simple que funcioni. Per a gairebé tothom: branques de tema curtes, propostes amb revisió,
mainprotegit, CI que passa en minuts. - Afegeix procés només quan faci mal alguna cosa concreta. I que la conversa comenci pel dolor, no per l'eina: «se'ns va trencar producció tres vegades aquest mes» és un motiu; «hauríem de fer servir Git Flow» no ho és.
- Escriu les decisions. Al
CONTRIBUTING.mdo alREADME.md. Un flux que només viu al cap de dues persones no sobreviu a la tercera contractació. - Revisa'l de tant en tant. Un procés adequat per a quatre persones pot ser insuficient per a dotze i absurd quan tornin a ser quatre.
Errors Habituals i Consells
Error 1: copiar el flux d'un projecte que no s'assembla al teu. És l'error central d'aquesta lliçó. Abans d'adoptar res, pregunta't quin problema resolia en el seu context original i si tu el tens.
Error 2: creure que format-patch/am és cosa de museu. És la manera més portable que existeix de moure un canvi entre dos repositoris sense connectar-los. Serveix per enviar una correcció a un company sense xarxa compartida, per moure un commit entre dos repositoris que no es coneixen, o per arxivar un canvi com a text. Ho reprenem a la lliçó 10-02.
Error 3: confondre git request-pull amb el pull request d'una plataforma. El primer genera un text que tu envies pel mitjà que vulguis; el segon és un objecte gestionat per un servidor, amb estat, comentaris i comprovacions. Conceptualment són el mateix; operativament, no.
Error 4: pensar que el monorepo és «tot junt sense ordre». Un monorepo que funciona té més estructura que un conjunt de repositoris separats: propietat per directoris, graf de dependències explícit i CI selectiva. El que elimina no és l'ordre, sinó la coordinació entre repositoris.
Error 5: fer servir el prestigi d'un projecte com a argument tècnic. «El kernel ho fa així» no és una raó. La raó és per què ho fa així, i si això s'aplica al teu cas.
Consell 1: aprèn a llegir l'historial d'un projecte aliè. És la manera més ràpida d'entendre com treballa un equip abans de contribuir-hi:
git clone --filter=blob:none https://git.exemple.cat/projecte.git
cd projecte
# Hi ha fusions o l'historial és lineal?
git log --oneline --graph -40
# Quina proporció de commits són fusions?
git rev-list --count --merges HEAD
git rev-list --count HEAD
# Quina convenció de missatges fan servir?
git log --format=%s -40
# Qui integra de debò? (committer, no autor)
git log --format='%cn' -300 | sort | uniq -c | sort -rn | head
# Fan servir Signed-off-by?
git log --format=%b -60 | grep -c 'Signed-off-by'Aquest --filter=blob:none del clon és un clon parcial: porta el graf complet però no el contingut dels fitxers. Per inspeccionar un historial és perfecte i moltíssim més ràpid. S'explica a la lliçó 10-04.
Consell 2: abans de contribuir a un projecte, llegeix-ne el CONTRIBUTING.md sencer. Sona obvi i gairebé ningú no ho fa. És la diferència entre una proposta que s'integra i una que es queda sis mesos esperant.
Consell 3: si el teu equip no té el flux escrit, escriu-l'hi tu. Mitja pàgina al README.md: quines branques hi ha, com s'anomenen, com s'integra, com es publica. És l'aportació amb millor relació esforç/benefici que pots fer.
Exercicis
Exercici 1: moure un canvi sense remot compartit
L'Ana i el Bruno són en un tren sense connexió, compartint una xarxa local. L'Ana té tres commits a la branca GT-244-ordre-per-prioritat que el Bruno necessita al seu repositori. No hi ha cap servidor accessible.
Descriu dues maneres d'aconseguir-ho amb el que has après al curs, i explica quan preferiries cadascuna.
Exercici 2: diagnosticar un flux pel seu historial
Has clonat un projecte desconegut i n'obtens aquestes dades:
$ git rev-list --count HEAD 18452 $ git rev-list --count --merges HEAD 6127 $ git log --format=%s -8 Merge tag 'xarxa-per-a-6.11' de git.exemple.cat/subsistema-xarxa Merge tag 'fitxers-per-a-6.11' de git.exemple.cat/subsistema-fitxers net: corregeix fuita de memoria al driver net: afegeix prova de regressio Merge tag 'grafics-per-a-6.11' de git.exemple.cat/subsistema-grafics docs: actualitza la guia de contribucio Merge tag 'usb-per-a-6.11' de git.exemple.cat/subsistema-usb Merge tag 'audio-per-a-6.11' de git.exemple.cat/subsistema-audio $ git log --format='%an|%cn' -6 Ana Ferrer|Bruno Salas Carla Vidal|Bruno Salas Diego Rueda|Ana Ferrer Ana Ferrer|Bruno Salas
A quin dels quatre models correspon? Justifica-ho amb tres indicis diferents.
Exercici 3: triar flux per a tres equips
Per a cada situació, decideix el model de treball i justifica'l en tres o quatre línies. Digues també què no hi afegiries.
A. Cinc persones, aplicació web interna, desplegament diverses vegades al dia, tots a la mateixa oficina, el repositori pesa 40 MB.
B. Una biblioteca de codi obert amb vint col·laboradors habituals i uns dos-cents ocasionals a l'any. Dos mantenidors amb poc temps. Es fa servir en producció en molts llocs, així que una versió trencada és un problema seriós.
C. Empresa de 400 persones, dotze productes que comparteixen quatre biblioteques internes. Avui tenen setze repositoris i es passen la vida coordinant versions entre ells. Cada canvi d'una biblioteca compartida triga setmanes a arribar a tots els consumidors.
Solucions
Solució 1
Manera A: git bundle (lliçó 09-05).
# Ana: empaqueta els tres commits en un fitxer
git bundle create /tmp/GT-244.bundle main..GT-244-ordre-per-prioritat
# (copia el fitxer a la màquina del Bruno per la xarxa local o un USB)
# Bruno: comprova què necessita el paquet i què conté
git bundle verify /tmp/GT-244.bundle
git bundle list-heads /tmp/GT-244.bundle
# Bruno: se'l porta com si fos un remot
git fetch /tmp/GT-244.bundle GT-244-ordre-per-prioritat:GT-244-ordre-per-prioritatManera B: format-patch + am (aquesta lliçó).
# Ana
git format-patch main -o /tmp/pedacos-gt244/
# Bruno
git checkout -b GT-244-ordre-per-prioritat main
git am /tmp/pedacos-gt244/*.patchQuan cadascuna:
bundle |
format-patch/am |
|
|---|---|---|
| Què transporta | Objectes Git reals, amb els seus hashos | El contingut dels canvis, com a text |
| Hashos resultants | Idèntics als de l'Ana | Diferents: canvia el committer i la data |
| Llegible a ull nu | No, és binari | Sí, és text revisable |
| Aplicable sobre una altra base | No: exigeix tenir el commit base | Sí, i amb -3 fins i tot amb divergències |
| Volum | Eficient per a molts commits | Un fitxer per commit |
Tria bundle quan vulguis la història exacta (mateixos hashos, mateixa signatura, mateixos pares). Tria format-patch quan vulguis el canvi per revisar-lo, discutir-lo o aplicar-lo sobre una base diferent. I hi ha una tercera via si les dues màquines es veuen a la xarxa: afegir el repositori del company com a remot per ruta de sistema de fitxers o per SSH i fer un fetch normal (lliçó 04-02) —Git és distribuït, i qualsevol clon pot ser el remot d'un altre.
Solució 2
És el cas 1: el model del kernel, o qualsevol projecte organitzat com a arbre d'arbres. Tres indicis independents:
-
La proporció de fusions és del 33 % (6.127 de 18.452). Un historial amb un terç de commits de fusió no surt d'un flux de branques de tema amb condensat, que produeix un historial gairebé lineal. Surt d'un model on s'integren repetidament arbres complets.
-
Els missatges de fusió diuen
Merge tag '...-per-a-6.11' de git.exemple.cat/subsistema-.... Estan integrant etiquetes de repositoris diferents, cadascun d'un subsistema, tots preparats per a la mateixa versió. És literalment el resultat d'una sèrie degit request-pullseguits d'ungit merged'arbres independents. -
Autor i committer difereixen sistemàticament, i els committers són un conjunt petit i repetit (Bruno, Ana) mentre que els autors són variats (Carla, Diego). Això és l'empremta dactilar de
git am: hi ha persones que escriuen pedaços i persones diferents que els apliquen. En un flux de pull requests de plataforma amb condensat, el committer sol ser el servidor mateix o el mateix autor.
Un quart indici de reforç: els missatges dels commits normals porten prefix de subsistema (net:, docs:), la convenció habitual quan l'arbre s'organitza per àrees.
Solució 3
A. Equip petit de producte (cas 4).
Branques de tema curtes sobre main, proposta amb una revisió, CI que executi les proves, main protegit, integració amb condensat o rebase, desplegament des de main. És GitHub Flow (07-04) o fins i tot Trunk Based (07-05) si tenen bona cobertura de proves i fan servir banderes de funcionalitat.
No hi afegiria: Git Flow amb develop i branques de publicació (no hi ha múltiples versions vives), CODEOWNERS (cinc persones que seuen juntes), CI selectiva, ni cap tècnica d'escalat. 40 MB no és un problema de rendiment: és un repositori petit.
B. Projecte obert mitjà (cas 2).
Fork i proposta, CONTRIBUTING.md explícit, plantilles de proposta i d'incidència, etiquetes per al triatge i per captar gent nova, CI obligatòria i ràpida, integració amb condensat, publicacions amb etiqueta anotada i SemVer.
Com que «una versió trencada és un problema seriós», hi afegiria una branca de manteniment per versió major viva (1.x, 2.x) on es porten les correccions amb cherry-pick (05-03), i publicaria candidates abans d'una versió major. Aquest és l'únic punt on prenc prestada una peça de Git Flow, i per un motiu concret.
No hi afegiria: un flux per correu (la barrera d'entrada enfonsaria les contribucions ocasionals, que aquí són valuoses), ni develop (amb main estable i branques de manteniment n'hi ha prou), ni jerarquia de mantenidors (amb dues persones no hi ha jerarquia que valgui).
C. Candidat clar a monorepo (cas 3).
El símptoma que descriuen —setmanes per propagar un canvi d'una biblioteca compartida a dotze consumidors— és exactament el problema que el monorepo elimina d'arrel: un commit atòmic actualitza la biblioteca i tots els seus consumidors alhora.
Abans de decidir, comprovaria tres coses:
- Que no hi ha codi amb requisits de confidencialitat que impedeixin la visibilitat total.
- Que estan disposats a invertir en eines: CI selectiva i un sistema de compilació amb graf de dependències. Sense això, el monorepo es converteix en una canonada de dues hores per canvi.
- Que el volum resultant és manejable, o que accepten les tècniques de la lliçó 10-04.
Com a pas intermedi i reversible, començaria fusionant només les quatre biblioteques compartides en un repositori únic amb CODEOWNERS per directori, mesurant-ne l'efecte, i consolidant els productes després si l'experiència és bona.
No hi afegiria d'entrada: submòduls (06-05) per lligar les biblioteques als productes. Resoldrien la traçabilitat però no el problema real, que és la coordinació: continuarien necessitant dotze actualitzacions de punter, una per producte.
Conclusió
Quatre projectes, quatre respostes diferents a les mateixes cinc preguntes.
- El kernel de Linux treballa amb pedaços per correu:
git format-patchconverteix commits en correus,git send-emailels envia a llistes públiques,git amels aplica conservant l'autor original, igit request-pulldemana a un nivell superior que integri un arbre sencer. Sobre això s'aixeca una jerarquia de mantenidors basada en la confiança, ambSigned-off-by:com a cadena de custòdia. El preu d'admissió és alt: cada commit ha de valer per si sol i cada missatge s'ha de poder llegir sense context. - Un projecte obert mitjà fa servir fork i proposta, i dedica tot el seu disseny a protegir el recurs escàs, que és el temps dels mantenidors:
CONTRIBUTING.md, plantilles, etiquetes i automatització d'allò repetitiu. L'historial resultant és lineal i llegible. - Una empresa amb monorepo tria el repositori únic pel canvi atòmic entre projectes i la refactorització global, i paga aquest benefici amb eines pròpies, propietat del codi per directoris i CI selectiva calculada a partir de
git diff --name-only origin/main...HEAD. - Un equip petit de producte —el nostre— funciona amb branques curtes, propostes i
mainprotegit, i la seva virtut més gran és tot el que no té.
I la lliçó transversal, que és l'única que cal memoritzar:
El flux de treball es tria a partir de quatre restriccions —confiança entre qui hi contribueix, cost d'un error en producció, nombre de versions vives i mida del repositori— i no a partir del prestigi de qui el fa servir.
El procés d'una organització enorme és la cicatriu dels problemes que aquella organització va tenir. Si no tens aquests problemes, copiar-lo és adoptar-ne el cost sense el benefici.
El que ve
En els quatre casos ha aparegut alguna cosa que no és Git: llistes de correu, plataformes de propostes, sistemes de compilació, eines de seguiment de pedaços, canonades d'integració. Git gairebé mai no es fa servir sol. Viu envoltat d'editors que mostren diferències en color, de clients gràfics, de sistemes de tiquets que volen saber quin commit va tancar la incidència GT-231, de linters que s'executen abans de cada confirmació i de plataformes que converteixen un historial en una conversa.
Aquesta capa d'integració canvia per complet l'experiència diària i, mal entesa, és també on més gent perd el control del que està fent: el botó «Sincronitza» d'un editor amaga com a mínim dues ordres, i no sempre les que un espera.
A la lliçó 10-02: Integrant Git amb Altres Eines veurem què aporta de debò cada peça de l'ecosistema, què convé continuar fent al terminal i per què el consell de fons és sempre el mateix: automatitza allò repetitiu, però entén sempre quina ordre hi ha a sota.
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ó
