A la lliçó anterior, cada vegada que Git va crear un commit de fusió ens va dir el mateix: Merge made by the 'ort' strategy. Hi vam passar de llarg, però aquesta paraula amaga una part important de com funciona Git.
Una estratègia de fusió és l'algorisme que Git empra per combinar dues (o més) línies de treball. No n'hi ha una de sola: Git en porta unes quantes, en tria una per defecte segons el cas, i et permet imposar-ne una altra. A més, cada estratègia admet opcions que en modifiquen el comportament en detalls concrets, com ara què fer davant d'un conflicte o si ignorar canvis d'espais en blanc.
A la pràctica, el 95 % de les fusions que faràs faran servir l'estratègia per defecte sense que hi hagis de pensar. Però el 5 % restant —una fusió amb centenars de conflictes trivials, una branca abandonada que cal «tancar» formalment, una funcionalitat feta amb vint commits caòtics que no vols ficar a main— és exactament on conèixer les alternatives estalvia hores.
Aquesta lliçó cobreix també una decisió que va més enllà de l'algorisme: quina forma d'integrar triar segons l'historial que vulguis acabar tenint. Fusió normal, --no-ff, squash… o rebase, que veurem al mòdul 5 però que convé situar ja al mapa.
Contingut
- Què és exactament una estratègia de fusió
ort: l'estratègia per defecteresolve: la veteranaoctopus: diverses branques alhora- L'estratègia
ours: descartar el contingut, conservar la història subtree: fusionar projectes imbricats- Opcions d'estratègia amb
-X - El parany: estratègia
oursdavant de l'opció-X ours - El squash merge
--no-commit: revisar abans de segellar- Taula de decisió: quina forma d'integrar triar
- Què és exactament una estratègia de fusió
Quan git merge ha de combinar de debò —és a dir, quan no és un fast-forward—, necessita respondre una pregunta per cada fitxer i per cada línia: donat el que hi havia a l'ancestre comú i el que hi ha a cada costat, què hi poso, aquí?
El conjunt de regles que respon aquesta pregunta és l'estratègia. Es tria amb -s o --strategy:
I les seves opcions internes amb -X o --strategy-option:
Totes dues es poden combinar, i -X es pot repetir:
Comparem primer totes les estratègies disponibles, i després mirem-les una per una:
| Estratègia | Branques que admet | Quan la fa servir Git | Per a què serveix |
|---|---|---|---|
ort |
Dues | Per defecte des de Git 2.34 | Fusió general; és la que vols gairebé sempre |
recursive |
Dues | Per defecte de Git 2.0 a 2.33 | Predecessora d'ort; avui és un àlies d'ort |
resolve |
Dues | Mai per defecte | Algorisme antic i simple; un únic ancestre comú |
octopus |
Tres o més | Per defecte en fusionar 3+ branques | Integrar diverses branques trivials de cop |
ours |
Dues o més | Mai per defecte | Registrar la fusió descartant el contingut de l'altra branca |
subtree |
Dues | Mai per defecte | Fusionar un projecte dins d'un subdirectori |
ort: l'estratègia per defecte
ort: l'estratègia per defecteort vol dir Ostensibly Recursive's Twin («el bessó aparent de recursive»), un nom amb sentit de l'humor que delata el seu origen: és una reescriptura des de zero de l'estratègia recursive, dissenyada per donar els mateixos resultats però molt més de pressa i amb menys casos rars.
És l'estratègia per defecte des de Git 2.34 (novembre del 2021). Abans ho era recursive. Avui, si escrius -s recursive, Git fa servir ort igualment.
El que fa, resumit:
- Calcula l'ancestre comú de les dues branques.
- Fa la fusió a tres bandes fitxer a fitxer i línia a línia, amb la taula de regles que vam veure a la lliçó 03-03.
- Detecta reanomenaments: si en una branca vas moure
estils.cssacss/estils.cssi a l'altra el vas modificar, aplica els canvis al fitxer a la seva nova ubicació en lloc de donar-te un conflicte absurd. - Si hi ha diversos ancestres comuns, els fusiona entre si recursivament fins a obtenir-ne un de virtual, i el fa servir com a base.
Aquest punt 4 és el que dona nom a la família i mereix un moment. Pot haver-hi més d'un ancestre comú quan l'historial té fusions creuades:
graph RL
A["A"]
B["B"] --> A
C["C"] --> A
D["D"] --> B
D --> C
E["E"] --> C
E --> B
F["F<br/>branca-1"] --> D
G["G<br/>branca-2"] --> E
Aquí, tant D com E són ancestres comuns de branca-1 i branca-2, i cap dels dos és avantpassat de l'altre. Una estratègia simple n'hauria de triar un d'arbitràriament, amb el risc de produir conflictes falsos. ort els fusiona entre si per construir una base virtual que combini la informació de tots dos.
No necessites provocar aquesta situació per treballar: només saber que existeix i que Git la resol sola. És la raó que les fusions en historials complexos funcionin sorprenentment bé.
resolve: la veterana
resolve: la veteranaresolve és l'estratègia clàssica: fusió a tres bandes amb un únic ancestre comú, triat si n'hi ha diversos. No fa recursió i detecta reanomenaments de manera molt més limitada.
Quan cal fer-la servir? Pràcticament mai. El seu nínxol és el cas rar en què ort produeix un resultat que no t'acaba de convèncer en un historial amb fusions creuades i vols provar una base diferent. És més un recurs de diagnòstic que una eina de treball.
És al temari perquè la veuràs esmentada en documentació antiga i perquè conèixer-la ajuda a entendre per què ort fa el que fa.
octopus: diverses branques alhora
octopus: diverses branques alhoragit merge no es limita a un argument. Pots anomenar diverses branques:
Quan n'hi ha tres o més, Git canvia automàticament a l'estratègia octopus, que crea un únic commit de fusió amb tants pares com branques més un.
Imaginem que l'equip ha acumulat tres correccions diminutes i independents, cadascuna a la seva branca:
Trying simple merge with correccio/typo-readme Trying simple merge with correccio/favicon Trying simple merge with correccio/idioma-html Merge made by the 'octopus' strategy. README.md | 2 +- index.html | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-)
tree 5f8b2e1c9a3d7f4b6e2a8c1d5f9b3e7a4c6d2f8b parent f7a3e92b5d1c8f4a6e3b7d9f2a5c1e8b4d6f3a9c parent 2f6a3c8e9b1d5f7a3c2e8b4d6f1a9c5e7b3d2f4a parent 7d2f9a1c5e8b3d6f2a4c9e1b7d5f3a8c2e6b4d9f parent 3c9d4a2f8e1b5d7c3a9f2e6b4d8c1a5f7e3b9d2c
Quatre pares. El primer és main i els altres tres, les puntes de les branques fusionades.
gitGraph commit id: "f7a3e92" branch typo-readme commit id: "2f6a3c8" checkout main branch favicon commit id: "7d2f9a1" checkout main branch idioma-html commit id: "3c9d4a2" checkout main merge typo-readme merge favicon merge idioma-html id: "pop"
La limitació crucial d'octopus: es nega a treballar si hi ha cap conflicte.
No hi ha resolució possible, no hi ha marcadors als fitxers, no hi ha res per arreglar: l'operació s'avorta sencera. La raó és de disseny: resoldre conflictes entre quatre versions simultànies seria inabastable per a una persona.
Per això el seu ús realista és molt concret: integrar de cop diverses branques que no es trepitgen entre si. Si alguna conflictua, les hauràs de fusionar d'una en una. En el dia a dia d'un equip petit la veuràs poc; en projectes amb moltíssimes aportacions independents (el nucli de Linux n'és l'exemple canònic, i no per casualitat, ja que va ser el projecte per al qual es va escriure Git) té tot el sentit.
- L'estratègia
ours: descartar el contingut, conservar la història
ours: descartar el contingut, conservar la històriaAquesta és l'estratègia més peculiar del conjunt i la que més es malinterpreta.
El que fa: crea un commit de fusió amb els dos pares normals, però l'arbre resultant és exactament el de la branca actual. El contingut de l'altra branca es descarta del tot.
Sona absurd. Per què fusionar una cosa que llençaràs? Per registrar a l'historial que aquesta branca ja està considerada, sense portar-ne el codi.
El cas d'ús real al projecte de l'Ana: l'equip va obrir fa un mes la branca experiment/emmagatzematge-indexeddb per provar un sistema d'emmagatzematge diferent. L'experiment es va descartar —al final es van quedar amb localStorage—, però la branca continua allà i de tant en tant algú pregunta si cal integrar-la.
git switch main
git merge -s ours experiment/emmagatzematge-indexeddb -m "Descarta l'experiment amb IndexedDB
S'ha provat IndexedDB per a l'emmagatzematge de tasques. Es descarta:
la complexitat no compensa per al volum de dades que manegem.
Es manté localStorage. La branca queda registrada com a integrada."Fixa't que no llista cap fitxer canviat: no ha canviat res.
Zero diferències respecte al commit anterior de main. El codi és idèntic. Però:
La branca consta com a fusionada, amb la qual cosa apareixerà a les llistes de branques que es poden esborrar sense perdre res (lliçó 03-06), deixarà de sortir als avisos de «branques pendents d'integrar», i l'historial conté una explicació permanent de per què es va descartar.
Un altre ús clàssic: quan dues branques han divergit tant que reconciliar-les a mà és inviable i vols que en guanyi una de sencera, deixant constància de la decisió.
Advertiment important: -s ours descarta tot el contingut de l'altra branca sense preguntar i sense avisar. No hi ha conflictes, no hi ha revisió, no hi ha marxa enrere automàtica. És una decisió deliberada, no una drecera per sortir d'una fusió difícil. I no la confonguis amb -X ours, que és una cosa completament diferent i que veurem a l'apartat 8.
subtree: fusionar projectes imbricats
subtree: fusionar projectes imbricatssubtree és una variant d'ort per al cas en què una branca conté un projecte que a l'altra viu dins d'un subdirectori.
Suposa que l'equip desenvolupa a part una petita biblioteca de components, al seu propi repositori, i que a gestor-tasques aquesta biblioteca viu a vendor/components/. En fusionar la branca de la biblioteca, una estratègia normal intentaria casar-ne els fitxers amb l'arrel del projecte i produiria un desastre. subtree detecta el desplaçament de rutes i aplica els canvis on toca.
També es pot indicar el prefix a mà, que és més fiable que deixar que Git l'endevini:
Aquest mecanisme és la base de l'ordre git subtree, una de les dues formes d'imbricar repositoris. L'altra són els submòduls, que tenen lliçó pròpia al mòdul 6. Per ara n'hi ha prou que sàpigues que l'estratègia existeix i per a quin escenari està pensada.
- Opcions d'estratègia amb
-X
-XLes opcions no canvien l'algorisme: l'ajusten. Aquestes són les que valen la pena:
| Opció | Efecte |
|---|---|
-X ours |
Davant d'un conflicte, resol automàticament a favor de la branca actual |
-X theirs |
Davant d'un conflicte, resol automàticament a favor de la branca fusionada |
-X ignore-space-change |
Ignora canvis en la quantitat d'espais en comparar |
-X ignore-all-space |
Ignora tots els espais en blanc |
-X ignore-space-at-eol |
Ignora espais al final de línia |
-X ignore-cr-at-eol |
Ignora el retorn de carro final (útil entre Windows i Unix) |
-X renormalize |
Normalitza els finals de línia abans de comparar, segons .gitattributes |
-X find-renames=<n> |
Ajusta el llindar de similitud per detectar reanomenaments |
-X no-renames |
Desactiva la detecció de reanomenaments |
-X patience |
Fa servir l'algorisme de diff «paciència», que de vegades alinea millor |
-X diff-algorithm=histogram |
Tria un altre algorisme de comparació |
El cas dels espais en blanc
Aquest és el que més vegades et salvarà. Situació real: en Bruno ha configurat el seu editor perquè reindenti automàticament en desar, i ha reindentat app.js sencer a la seva branca. L'Ana n'ha modificat tres línies, del mateix fitxer, a la seva.
Auto-merging app.js CONFLICT (content): Merge conflict in app.js Automatic merge failed; fix conflicts and then commit the result.
Vuitanta conflictes, i cap de real: són tots diferències de sagnat. Solució:
Auto-merging app.js Merge made by the 'ort' strategy. app.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+)
Els vuitanta conflictes falsos desapareixen i queden només els canvis reals.
Dos matisos importants:
- Aquestes opcions afecten com es comparen les línies, no el resultat escrit. El fitxer final conserva el sagnat d'una de les versions (la d'
oursals trams ambigus), així que revisa el resultat. - Si el problema del sagnat és recurrent a l'equip, la solució de fons no és
-Xsinó.gitattributesi un formatador automàtic compartit. És el tema de la lliçó 08-04.
-X theirs a la pràctica
Un exemple típic: un fitxer generat automàticament (un bundle.js, un fitxer de bloqueig de dependències) que conflictua sempre i en què la versió de la branca entrant és la correcta:
És còmode, però fes servir -X ours/-X theirs amb el cap: resolen tots els conflictes del mateix costat, sense distingir quins eren importants. Si el resultat t'importa, val més resoldre a mà (lliçó 03-05) o limitar la drecera als fitxers concrets que s'ho mereixen, cosa que també es pot fer i veurem allà.
- El parany: estratègia
ours davant de l'opció -X ours
ours davant de l'opció -X oursS'escriuen gairebé igual i fan coses radicalment diferents. És una de les confusions més cares de Git, així que la deixarem clavada.
-s ours (estratègia) |
-X ours (opció) |
|
|---|---|---|
| Què és | Un algorisme de fusió complet | Un ajust de l'estratègia ort |
| Què fa | Descarta tot el contingut de l'altra branca | Fusiona normal, i només davant d'un conflicte tria el teu costat |
| Canvis sense conflicte de l'altra branca | Es perden tots | S'incorporen amb normalitat |
| Resultat | L'arbre és idèntic al de la teva branca | L'arbre combina les dues branques |
| Quan fer-la servir | Tancar formalment una branca descartada | Resoldre en bloc conflictes sense importància |
Un exemple concret. La branca funcionalitat/filtre-pendents aporta dues coses: un fitxer nou filtres.js (que no conflictua amb res) i una modificació de la línia 12 d'app.js (que sí que conflictua amb un canvi de l'Ana).
Resultat: no apareix filtres.js i la línia 12 és la de l'Ana. S'ha descartat tot, fins i tot allò que no molestava ningú.
Resultat: filtres.js sí que apareix, perquè no hi havia conflicte amb ell, i la línia 12 és la de l'Ana, perquè aquí sí que n'hi havia. Fusió real amb desempat automàtic.
Regla mnemotècnica:
-sés l'estratègia, i una estratègia decideix tot.-Xés una opció, i una opció només decideix els empats.
I una precisió sobre el vocabulari, perquè també confon: en una fusió, ours és sempre la branca on ets i theirs la que has anomenat a l'ordre. Sona obvi, però durant un rebase els papers s'inverteixen respecte al que un esperaria, i aquest és un dels motius pels quals el rebase mereix la seva pròpia lliçó (05-01).
- El squash merge
Canviem de terç. El squash merge no és una estratègia: és una forma diferent d'integrar, i en molts equips és la més utilitzada de totes.
Situació al projecte: en Bruno ha desenvolupat l'exportació del llistat a CSV a la branca funcionalitat/exportar-csv. Ha funcionat, però l'historial de la branca és un desastre:
e9a2c5f Ara sí 7e2a9c4 Treu el console.log 1d6b4f8 wip 2 5a8e2b9 Arregla el separador 9f3a2c1 wip 4a1c7e3 Primer intent d'exportació
Sis commits, quatre dels quals no aporten res a ningú que llegeixi l'historial d'aquí a sis mesos. En Bruno no vol ficar això a main.
Updating f7a3e92..e9a2c5f Fast-forward Squash commit -- not updating HEAD app.js | 34 ++++++++++++++++++++++++++++++++++ index.html | 2 ++ 2 files changed, 36 insertions(+)
Llegeix bé: Squash commit -- not updating HEAD. Git ha calculat el resultat de la fusió i l'ha deixat a l'àrea de preparació, però no ha confirmat res.
On branch main Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: app.js modified: index.html
Ara en Bruno escriu un missatge decent i confirma:
git commit -m "Afegeix l'exportació del llistat de tasques a CSV
Genera un fitxer CSV amb el títol, l'estat i la data de
creació de cada tasca. Es descarrega des del botó nou de la
capçalera."En què es diferencia d'una fusió normal
graph TB
subgraph squash["SQUASH MERGE"]
S1["f7a3e92<br/>main"] --> S2["3b9e7d1<br/>main (un sol commit,<br/>UN pare)"]
S3["4a1c7e3 → e9a2c5f<br/>exportar-csv<br/>(desconnectada)"]
end
subgraph normal["FUSIÓ NORMAL"]
N1["f7a3e92"] --> N3["merge<br/>main (DOS pares)"]
N2["4a1c7e3 → e9a2c5f<br/>exportar-csv"] --> N3
end
La diferència essencial:
| Fusió normal | Squash merge | |
|---|---|---|
Commits nous a main |
1 (de fusió) | 1 (normal) |
| Pares d'aquest commit | Dos | Un |
Commits de la branca a l'historial de main |
Sí, tots sis | No, cap |
| Queda registrada la relació amb la branca? | Sí | No |
Què diu git branch --merged? |
Que està fusionada | Que no ho està |
| Autoria dels commits originals | Es conserva | Es perd (queda qui confirma) |
Aquest «no queda registrada la relació» té una conseqüència pràctica molt concreta. Després d'un squash, main no és descendent de funcionalitat/exportar-csv:
La branca d'en Bruno no apareix, encara que el seu contingut sigui íntegrament a main. Si intentes esborrar-la:
error: The branch 'funcionalitat/exportar-csv' is not fully merged. If you are sure you want to delete it, run 'git branch -D funcionalitat/exportar-csv'.
Git no menteix: des del punt de vista del graf, aquests sis commits no són a main. L'haurem d'esborrar amb -D sabent el que fem. Hi tornarem a la lliçó 03-06.
Quan fer servir squash
A favor:
mainté un commit per funcionalitat, net i amb un missatge escrit amb calma.- El desordre de la branca de treball (els
wip, els «ara sí») no contamina el projecte. git bisect(lliçó 06-02) funciona millor: cada commit demainés un estat complet i funcional.- És el model per defecte del botó «Squash and merge» de les plataformes d'allotjament, i per tant el més estès avui en equips que treballen amb propostes de canvi.
En contra:
- Es perd el detall del desenvolupament. Si la funcionalitat són 40 commits ben escrits, aixafar-los és destruir informació útil.
- Es perd l'autoria individual: un commit fet amb aportacions de tres persones queda a nom de qui el confirma.
- Les branques s'han d'esborrar amb
-D, sense la xarxa de seguretat de-d. - Si continues treballant a la branca després del squash i la tornes a fusionar, Git no sap que allò ja estava integrat i pot reproduir conflictes ja resolts.
Regla pràctica: squash per a branques curtes d'una sola persona amb commits desordenats; fusió amb --no-ff per a funcionalitats grans amb un historial que val la pena conservar.
--no-commit: revisar abans de segellar
--no-commit: revisar abans de segellargit merge --no-commit fa la fusió de debò però s'atura just abans de crear el commit:
On branch main All conflicts fixed but you are still merging. (use "git commit" to conclude merge) Changes to be committed: modified: app.js modified: estils.css modified: index.html
En aquest punt pots:
# Veure exactament què entrarà
git diff --cached
# Executar les proves amb el resultat ja combinat
npm test
# Ajustar alguna cosa abans de confirmarI després, confirmar o avortar:
git commit # segella la fusió (amb els seus dos pares)
# o bé
git merge --abort # desfà la fusió i torna a l'estat anteriorLa diferència amb --squash és fonamental i sovint es confon, perquè totes dues «paren abans de confirmar»:
--no-commit |
--squash |
|
|---|---|---|
| Es registra la fusió? | Sí: en confirmar surt un commit amb dos pares | No: surt un commit normal |
| Es pot avortar? | Sí, amb git merge --abort |
No: no hi ha cap fusió en curs per avortar |
Estat de .git/MERGE_HEAD |
Existeix | No existeix |
| Per a què serveix | Revisar o provar abans de segellar | Aixafar una branca en un commit |
Aquest fitxer MERGE_HEAD és el detall tècnic que ho explica tot: amb --no-commit hi ha una fusió en curs que Git recorda, i per això el commit resultant tindrà dos pares i --abort funciona. Amb --squash no hi ha cap fusió en curs: només hi ha uns canvis preparats a l'índex.
Usos habituals de --no-commit:
- Executar la bateria de proves sobre el resultat combinat abans de deixar-lo a
main. - Revisar una fusió gran fitxer a fitxer.
- Ajustar detalls menors (un
importduplicat, un espai) i ficar-los al mateix commit de fusió.
- Taula de decisió: quina forma d'integrar triar
Recollim tot el que hem vist en aquest mòdul, més el rebase que arriba al mòdul 5, en una única taula de decisió:
| Vull… | Faig servir | Historial resultant |
|---|---|---|
| Integrar i que no es noti que hi va haver branca | git merge (fast-forward) |
Lineal, sense node de fusió |
| Deixar constància que es va integrar una funcionalitat | git merge --no-ff |
Amb node de fusió i bombolla de commits |
| Un sol commit net per funcionalitat | git merge --squash + git commit |
Lineal, un commit per branca |
Prohibir els nodes de fusió a main |
git merge --ff-only |
Lineal, o falla |
| Provar el resultat abans de segellar-lo | git merge --no-commit |
El que decideixis després |
| Integrar diverses branques trivials alhora | git merge a b c (octopus) |
Un node amb molts pares |
| Tancar una branca descartada sense portar-ne el codi | git merge -s ours |
Amb node de fusió, sense canvis |
Posar la meva branca al dia amb main sense node de fusió |
git rebase main → 05-01 |
Lineal, amb commits reescrits |
Sobre l'última fila, el just per situar-la: el rebase no fusiona, sinó que reescriu els commits de la teva branca com si els haguessis fet a partir de l'estat actual de main. El resultat és un historial lineal sense nodes de fusió, a canvi que els commits canvien de hash i per tant són commits nous. Això té implicacions serioses quan la feina ja s'ha compartit, i per això té lliçó pròpia. Aquí només necessites saber que és la tercera alternativa al costat de la fusió i el squash.
I una recomanació de partida
Si estàs començant i no tens una política d'equip a seguir:
- Fusió normal per a tot, deixant que Git decideixi entre fast-forward i tres bandes.
--no-ffquan integris alguna cosa que vulguis poder identificar com una unitat en el futur.--squashquan la branca sigui teva, curta i desordenada.- No toquis
-sni-Xfins que un problema concret et demani una d'aquestes opcions.
I sobretot, que tot l'equip faci el mateix. Un historial coherent val més que un historial òptim però irregular. Les convencions de flux amb nom —Git Flow, GitHub Flow, Trunk Based Development— són precisament això: paquets de decisions ja preses. Tenen el seu lloc al mòdul 7.
Errors Habituals i Consells
Error 1: confondre -s ours amb -X ours. És l'error més car d'aquesta lliçó. -s ours descarta silenciosament tota la feina de l'altra branca, inclosos els fitxers que no donaven cap problema. Si el que volies era desempatar conflictes, l'opció és -X ours. Si mai en dubtes, git diff HEAD~1 HEAD després de la fusió et dirà de seguida si has portat alguna cosa o res.
Error 2: fer servir -X theirs com a drecera universal. Resol tots els conflictes del mateix costat, inclosos aquells en què la teva versió era la correcta. Es perd feina sense que res n'avisi. Reserva aquestes opcions per a conflictes que saps que són irrellevants.
Error 3: creure que --squash fusiona. No ho fa: prepara els canvis i se'n va. Si oblides el git commit posterior, et quedes amb un munt de canvis preparats a main i una sensació rara que «la fusió no ha funcionat». I encara que confirmis, la branca continuarà figurant com a no fusionada.
Error 4: fer squash d'una branca i continuar treballant-hi. Com que el commit resultant no té cap relació amb els originals, la següent fusió d'aquella branca tornarà a portar tot l'anterior i reproduirà conflictes ja resolts. Després d'un squash, la branca es tanca: s'esborra i se n'obre una altra.
Error 5: buscar l'estratègia perfecta per a un conflicte complicat. No existeix. Si ort dona conflictes, és perquè hi ha una decisió humana per prendre. Canviar d'estratègia només desplaça el problema o l'amaga. La resposta correcta és la lliçó següent.
Consell 1: -X ignore-space-change és el que faràs servir més vegades. Quan una fusió peti amb desenes de conflictes que en mirar-los són només sagnat, avorta i torna-ho a provar amb aquesta opció. La millora és espectacular.
Consell 2: comprova què ha fet una fusió rara. Després de qualsevol fusió amb -s o -X, mira el resultat abans de continuar:
Consell 3: --no-commit és la teva xarxa de seguretat. En fusions grans o delicades, atura't abans de confirmar, executa les proves i revisa. Costa trenta segons i evita ficar a main una combinació que ningú no ha comprovat.
Consell 4: documenta la política de l'equip. Escriu al README.md o al CONTRIBUTING.md del projecte com s'integra a main. És la mena de decisió que, sense escriure, cada persona interpreta d'una manera.
Exercicis
Exercici 1: demostrar la diferència entre -s ours i -X ours
Munta un repositori de proves on una branca aporti dues coses: un fitxer nou que no conflictua i una modificació que sí que conflictua amb la branca principal. Fusiona de les dues maneres (en dos intents independents) i demostra amb ordres què queda en cada cas.
Exercici 2: squash davant de fusió normal
Crea una branca amb tres commits i integra-la a main de les dues maneres, en dos repositoris diferents o desfent pel mig. Després respon:
- Quants commits té
mainen cada cas? - Quants pares té l'últim commit de
mainen cada cas? - Què diu
git branch --mergeden cada cas? - Es pot esborrar la branca amb
-den cada cas?
Exercici 3: triar la forma d'integrar
Per a cada situació, tria entre fusió normal, --no-ff, --squash, -s ours, octopus o --no-commit, i justifica-ho:
- Una branca d'un company amb 25 commits ben escrits i missatges acurats, que implementa el mòdul d'informes.
- La teva pròpia branca amb 7 commits, quatre dels quals es diuen
wip. - Cinc branques de traducció, cadascuna tocant un fitxer d'idioma diferent.
- Una branca d'un experiment que s'ha decidit no tirar endavant, i de la qual vols deixar constància.
- Una fusió que afecta la lògica de facturació i que vols validar amb les proves abans que entri a
main.
Solucions
Solució 1:
mkdir /tmp/practica-ours && cd /tmp/practica-ours
git init -b main
echo "linia original" > comu.txt
git add . && git commit -m "Base"
# La branca aporta un fitxer nou I un canvi conflictiu
git switch -c aportacio
echo "contingut nou" > exclusiu.txt
echo "versio de la branca" > comu.txt
git add . && git commit -m "Afegeix exclusiu.txt i canvia comu.txt"
# main canvia la MATEIXA línia de comu.txt
git switch main
echo "versio de main" > comu.txt
git commit -am "Canvia comu.txt a main"Intent A, amb l'estratègia:
exclusiu.txt no hi és. S'ha descartat tot el contingut de la branca, inclòs el fitxer que no donava cap problema.
Zero canvis respecte a main. És la signatura inconfusible de -s ours.
Intent B, amb l'opció:
git reset --hard HEAD~1 # desfem l'intent A
git merge -X ours aportacio -m "Fusió amb opció -X ours"
ls
cat comu.txtexclusiu.txt sí que hi és, perquè no hi havia conflicte amb ell. I comu.txt té la versió de main perquè allà sí que n'hi havia i -X ours va desempatar al nostre favor.
Conclusió: -s ours ho descarta tot; -X ours fusiona de debò i només desempata.
Solució 2:
mkdir /tmp/practica-squash && cd /tmp/practica-squash
git init -b main
echo "base" > f.txt && git add . && git commit -m "Base"
git switch -c funcionalitat
echo "a" >> f.txt && git commit -am "Pas 1"
echo "b" >> f.txt && git commit -am "Pas 2"
echo "c" >> f.txt && git commit -am "Pas 3"
git switch main
echo "canvi a main" > altre.txt && git add . && git commit -m "Avança main"Fusió normal:
Squash (repetint el muntatge des de zero o desfent amb git reset --hard HEAD~1):
git merge --squash funcionalitat
git commit -m "Afegeix la funcionalitat completa"
git log --oneline | wc -lUn sol pare.
Respostes:
- 6 commits amb fusió normal (base + 3 de la branca + avanç de main + fusió); 3 amb squash (base + avanç de main + el commit aixafat).
- Dos pares amb fusió normal; un amb squash.
- Amb fusió normal, la branca apareix com a fusionada; amb squash, no.
- Amb fusió normal,
-dfunciona; amb squash, cal fer servir-D.
Solució 3:
-
Fusió amb
--no-ff. Vint-i-cinc commits ben escrits són informació valuosa que no s'ha de destruir, i el node de fusió documenta la integració del mòdul com una unitat identificable. -
--squash. Branca curta, pròpia i desordenada: el cas de manual. Un commit net amainamb un missatge escrit amb calma val més que set commits dels quals quatre no diuen res. -
octopus(git merge trad-es trad-ca trad-en trad-fr trad-de). Cinc branques que toquen fitxers diferents i per tant no poden conflictuar: és exactament l'escenari per al qual serveix. Si alguna conflictués,octopusfallaria i caldria integrar-les d'una en una. -
-s ours. Registra a l'historial que la branca s'ha considerat i descartat, sense portar-ne el codi, i deixa d'aparèixer com a pendent d'integrar. Aprofita el missatge del commit per explicar per què es va descartar. -
--no-commit. Permet executar la bateria de proves sobre el resultat ja combinat i, si alguna cosa falla, sortir netament ambgit merge --abortsense quemainarribi a contenir res.
Conclusió
Ja saps que «fusionar» no és una sola cosa:
- Una estratègia és l'algorisme que combina les branques, i es tria amb
-s.ortés la de per defecte des de Git 2.34: fusió a tres bandes amb detecció de reanomenaments i resolució recursiva quan hi ha diversos ancestres comuns.resolveés la seva predecessora simple;octopusfusiona tres o més branques de cop però falla davant de qualsevol conflicte;subtreecasa projectes que viuen en subdirectoris. - L'estratègia
ours(-s ours) crea un commit de fusió i descarta tot el contingut de l'altra branca. Serveix per tancar formalment una branca que s'ha decidit no integrar. - Les opcions
-Xajusten l'estratègia, no la substitueixen.-X ours/-X theirsdesempaten només els conflictes;-X ignore-space-changeelimina d'una revolada els conflictes falsos per sagnat. -s oursi-X oursno tenen res a veure. L'estratègia decideix tot; l'opció decideix només els empats.- El squash merge (
--squash) deixa el resultat preparat a l'índex sense confirmar i sense registrar la fusió: el commit final té un sol pare i la branca no consta com a fusionada. Ideal per a branques curtes i desordenades; dolent per a funcionalitats l'historial de les quals val la pena conservar. --no-commitfa la fusió de debò però s'atura abans de confirmar, i permet revisar i provar. A diferència de--squash, la fusió continua en curs i--abortfunciona.- L'elecció de com integrar és una decisió d'equip sobre l'historial que es vol tenir, no una qüestió tècnica.
El que ve
Hem donat per fet durant tota la lliçó una cosa que al món real no sempre passa: que la fusió surt bé. Però l'Ana i en Bruno no tindran tanta sort la propera vegada. Tots dos són a punt de tocar les mateixes línies de la funció que pinta el llistat de tasques a app.js, cadascun a la seva branca i per motius diferents.
Quan això passa, Git no pot decidir. S'atura, escriu uns marcadors estranys dins del fitxer i et passa la pilota. A la lliçó següent, Resolent Conflictes de Fusió, provocarem un conflicte de debò i el resoldrem pas a pas: aprendràs a llegir els marcadors <<<<<<<, ======= i >>>>>>> (i l'estil diff3, que a més t'ensenya què hi havia a l'ancestre comú), a orientar-te amb git status i git diff mentre la fusió està a mitges, a fer servir les dreceres --ours/--theirs, a avortar amb git merge --abort quan la cosa es torci, i a configurar una eina gràfica per als conflictes difícils.
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ó
