Tancàvem el mòdul 4 amb un advertiment: a partir d'ara l'historial és compartit. L'Ana, en Bruno i la Carla treballen sobre el mateix repositori de git.exemple.cat, i cada push hi deixa commits que les altres dues persones poden baixar-se en qualsevol moment. Aquesta és exactament la circumstància que converteix git rebase en l'eina més útil i més temuda de Git.
git rebase serveix per a una cosa molt concreta: agafar una sèrie de commits i tornar-los a aplicar sobre una altra base. Serveix perquè una branca que va néixer fa tres dies torni a estar «al damunt» de main sense embrutar l'historial amb fusions; serveix per reordenar i netejar la teva feina abans d'ensenyar-la a ningú; i serveix per moure una branca que penjava del lloc equivocat. El que fa por d'ella és l'altra cara: no mou commits, els substitueix per uns de nous. I substituir commits que ja estan publicats és, com veurem, una manera força eficaç d'arruïnar la tarda als teus companys.
Aquesta lliçó explica el mecanisme amb prou detall perquè no hi hagi màgia: quins objectes es creen, quins hashos canvien, què es conserva, com es resolen els conflictes que apareixen a mitges i —sobretot— quan convé fer servir rebase i quan convé fer servir merge.
Contingut
- La regla d'or del mòdul
- Què fa
git rebaserealment - Un rebase pas a pas a
gestor-tasques - Què es conserva i què canvia als commits reaplicats
- Rebase davant de merge
git rebase --onto: moure un tram concret- Conflictes durant un rebase:
--continue,--skip,--abort - La inversió d'
oursitheirs - Trencar la regla d'or: què passa exactament
git pull --rebaserebase.autoStashi altres opcions còmodes- La xarxa de seguretat
- La regla d'or del mòdul
Abans d'escriure ni una sola ordre, la regla que governa tot el mòdul 5:
No reescriguis historial que ja has publicat i que altres persones poden tenir descarregat.
Tot el que veurem a les lliçons 05-01, 05-02 i 05-03 crea commits nous que en substitueixen d'altres. Mentre aquests commits visquin només al teu disc, reescriure'ls és gratis: ningú més no els ha vist. Tan bon punt els envies a git.exemple.cat, deixen de ser teus: passen a formar part d'una realitat compartida, i canviar-los obliga els altres a refer la seva còpia.
La regla té una excepció molt acotada i molt coneguda: la teva pròpia branca de treball, publicada només per tenir-ne còpia o per a una revisió, que ningú més no fa servir com a base. És un cas legítim i habitual, però exigeix avisar i exigeix --force-with-lease (lliçó 04-05). Tornarem a la regla a cada apartat que reescrigui commits, fins que resulti carregós. Aquest és l'objectiu.
- Què fa
git rebase realment
git rebase realmentLa frase que se sol repetir és «el rebase mou els teus commits al damunt d'una altra branca». És còmoda i és falsa, i la diferència importa.
Recorda el model de dades de la lliçó 01-04: un commit és un objecte immutable el hash SHA-1 del qual es calcula a partir de tot el seu contingut, i aquest contingut inclou el hash del seu pare. Per tant:
- Si canvies el pare d'un commit, canvia el seu contingut.
- Si canvia el seu contingut, canvia el seu hash.
- Si canvia el seu hash, ja no és el mateix commit: és un altre objecte diferent.
Un commit no es pot «moure» igual que no es pot canviar el passat. El que fa git rebase és:
- Calcular la llista de commits que són a la teva branca i no a la nova base.
- Desar el canvi (el diff) que introdueix cadascun.
- Situar-se a la nova base.
- Aplicar aquests canvis un a un, creant un commit nou per cadascun.
- Moure la referència de branca a la punta de la cadena nova.
Els commits antics continuen existint a la base de dades d'objectes —els objectes de Git no s'esborren a l'instant—, però es queden sense cap branca que els assoleixi. Són, a efectes pràctics, història morta.
gitGraph commit id: "c5d9b1e" commit id: "b3f7c21" branch funcionalitat/etiquetes-color commit id: "7c2e5f9" commit id: "a4b1d83" checkout main commit id: "e91d4a8"
Aquest és el punt de partida: la Carla va crear la seva branca quan main era a b3f7c21, i mentre treballava l'Ana va publicar e91d4a8. Després del rebase:
gitGraph commit id: "c5d9b1e" commit id: "b3f7c21" commit id: "e91d4a8" branch funcionalitat/etiquetes-color commit id: "d5e8f31" commit id: "92c7a06"
Fixa't bé en els identificadors: 7c2e5f9 i a4b1d83 han desaparegut del dibuix, i al seu lloc hi ha d5e8f31 i 92c7a06. El contingut dels canvis és el mateix, els missatges són els mateixos, l'autor és el mateix. Els commits, no.
- Un rebase pas a pas a
gestor-tasques
gestor-tasquesLa Carla porta dos dies amb funcionalitat/etiquetes-color: vol que cada tasca pugui portar un color. Mentrestant, l'Ana ha publicat un canvi a main. La Carla vol posar-se al dia sense crear un commit de fusió.
# 1. Punt de partida: portar l'últim del servidor
git switch funcionalitat/etiquetes-color
git fetch origin
git log --oneline --graph --all -6* e91d4a8 (origin/main, main) Extreu la creació de l'element de tasca a la seva funció | * a4b1d83 (HEAD -> funcionalitat/etiquetes-color) Pinta l'etiqueta de color al llistat | * 7c2e5f9 Afegeix el camp color al model de tasca |/ * b3f7c21 Desa les tasques a localStorage * c5d9b1e Documenta la instal·lació al README
La bifurcació és clara: dos commits per una banda, un per l'altra, i b3f7c21 com a ancestre comú. Comprovem quins commits es reaplicaran abans de tocar res:
# 2. Quins commits reaplicarà el rebase (els que són a la meva branca i no a main)
git log --oneline main..HEADAquesta és exactament la llista de què parlàvem: el rang main..HEAD que vas aprendre a la lliçó 02-06. Ara sí:
* 92c7a06 (HEAD -> funcionalitat/etiquetes-color) Pinta l'etiqueta de color al llistat * d5e8f31 Afegeix el camp color al model de tasca * e91d4a8 (origin/main, main) Extreu la creació de l'element de tasca a la seva funció * b3f7c21 Desa les tasques a localStorage * c5d9b1e Documenta la instal·lació al README
Ja no hi ha bifurcació: una sola línia recta. I els hashos dels dos commits de la Carla han canviat. Val la pena verificar amb git show que el canvi és idèntic:
# 5. El commit vell continua existint, encara que cap branca no l'assoleixi
git show --stat 7c2e5f9 | head -6
git show --stat d5e8f31 | head -6commit 7c2e5f9... Author: Carla Vidal <[email protected]> Date: Mon Jul 27 09:14:22 2026 +0200 Afegeix el camp color al model de tasca
commit d5e8f31... Author: Carla Vidal <[email protected]> Date: Mon Jul 27 09:14:22 2026 +0200 Afegeix el camp color al model de tasca
Mateix autor, mateixa data d'autoria, mateix missatge, mateix diff. Objecte diferent.
Un detall important per al final: com que la branca de la Carla no estava publicada, aquí no hi ha res a negociar. Si ho estigués, el següent git push seria rebutjat per non-fast-forward (lliçó 04-05) i caldria fer servir --force-with-lease.
- Què es conserva i què canvia als commits reaplicats
Aquesta taula resol la majoria dels dubtes de qui comença:
| Element del commit | Es conserva després del rebase? |
|---|---|
| Missatge | Sí (llevat que el canviïs amb -i) |
| Autor i correu (author) | Sí |
| Data d'autoria (author date) | Sí |
| Confirmador (committer) | No: passes a ser-ho tu |
| Data de confirmació (committer date) | No: s'hi posa la d'ara |
| Contingut del canvi (el diff) | Sí, si no hi ha conflicte |
Arbre resultant (tree) |
Depèn de la base: gairebé sempre canvia |
Pare (parent) |
No: aquest és l'objectiu |
| Hash | No |
Dues conseqüències pràctiques:
git logmostra per defecte la data d'autoria, així que després d'un rebase l'historial continua ensenyant les dates originals. Si vols veure la de confirmació:git log --pretty=fuller.- Si al teu equip algú mira «qui va confirmar què», el rebase et posa a tu com a committer de commits aliens. No és cap problema, però convé saber-ho.
- Rebase davant de merge
Totes dues integren la feina d'una branca amb la d'una altra. La diferència no és tècnica sinó narrativa: quina història vols que expliqui el repositori.
| Aspecte | git merge |
git rebase |
|---|---|---|
| Què fa | Crea un commit nou amb dos pares | Crea commits nous, un per cada original |
| Historial resultant | Bifurcat, amb nusos de fusió | Lineal |
| Commits originals | Es conserven intactes | Se substitueixen (hashos nous) |
| Traçabilitat | Reflecteix el que va passar de debò: qui va treballar en paral·lel i quan es va ajuntar | Reflecteix una versió idealitzada: sembla que tot es va fer en ordre |
| Moment del conflicte | Un de sol, en fusionar; es resol un cop | Un per cada commit que xoqui; es pot repetir |
Llegibilitat de git log |
Pitjor amb moltes branques curtes | Millor: es llegeix de dalt a baix |
git bisect (lliçó 06-02) |
Funciona, però amb nusos | Més net |
| Seguretat sobre feina publicada | Total: no reescriu res | Perillosa: reescriu |
| Reversibilitat | git revert -m 1 (lliçó 05-06) |
No s'aplica: cal revertir commit a commit |
I el criteri, resumit en tres regles que sí que pots memoritzar:
- Rebase per al que encara és teu. La teva branca local, abans de publicar-la o abans de demanar-ne la integració: posa-la al dia sobre
mainambrebase. - Merge per ajuntar històries que ja són públiques. Integrar una branca de funcionalitat acabada a
mainés un fet que mereix quedar registrat; allà un merge (sovint--no-ff, lliçó 03-03) és l'opció honesta. - Davant del dubte, merge. És l'opció que mai no destrueix res.
Hi ha equips que porten això a l'extrem en un sentit («historial lineal sempre») i d'altres en el contrari («no reescriure mai res»). Totes dues postures són defensables i les veurem amb nom propi al mòdul 7 (fluxos de treball) i a la lliçó 08-02, quan parlem de la política d'historial net. Aquí ens ocupa el mecanisme.
git rebase --onto: moure un tram concret
git rebase --onto: moure un tram concretgit rebase <base> cobreix el 90 % dels casos, però té un supòsit implícit: que vols reaplicar tot el que hi ha entre l'ancestre comú i la teva branca. De vegades no és això el que vols.
Li va passar a la Carla. Va començar funcionalitat/ordre-per-data sense adonar-se que estava situada a funcionalitat/etiquetes-color en lloc de a main. Ara la seva branca nova arrossega els dos commits de les etiquetes de color, que no hi tenen res a veure.
gitGraph commit id: "e91d4a8" branch funcionalitat/etiquetes-color commit id: "d5e8f31" commit id: "92c7a06" branch funcionalitat/ordre-per-data commit id: "3f9a2c4" commit id: "6b1e7d5"
Vol que funcionalitat/ordre-per-data pengi directament de main (que és a e91d4a8) i contingui només 3f9a2c4 i 6b1e7d5. Per a això existeix la forma de tres arguments:
La manera de llegir-la en veu alta és aquesta:
<fins-a>— la branca que vull moure (si l'omets, la branca actual).<des-de>— el punt a partir del qual comencen els commits que vull endur-me. Aquest commit no s'inclou; funciona com el límit exclusiu d'un rangdes-de..fins-a.<nova-base>— on vull que quedin enganxats.
Aplicat al cas de la Carla:
# 1. Veure quins commits m'enduc (mateixa semàntica de rang)
git log --oneline funcionalitat/etiquetes-color..funcionalitat/ordre-per-data# 2. El rebase --onto
git rebase --onto main funcionalitat/etiquetes-color funcionalitat/ordre-per-datagitGraph commit id: "e91d4a8" branch funcionalitat/etiquetes-color commit id: "d5e8f31" commit id: "92c7a06" checkout main branch funcionalitat/ordre-per-data commit id: "0c4f8a3" commit id: "5d2b9e7"
Els dos commits de la data s'han reaplicat sobre main amb hashos nous, i la branca de les etiquetes de color s'ha quedat on era, intacta.
Altres usos habituals de --onto, perquè reconeguis el patró:
# Treure els 3 primers commits d'una branca (començar a comptar 3 més enllà)
git rebase --onto main main~3 la-meva-branca
# Portar els commits d'una branca a una altra branca diferent
git rebase --onto correccio/urgent main funcionalitat/alguna-cosa
# Treure UN commit del mig: tot el que hi ha darrere seu, al damunt del seu pare
git rebase --onto 4e7f2a9 8b6d3c2 mainAquest últim és un bon exercici mental: 8b6d3c2 és el commit que volem eliminar, 4e7f2a9 és el seu pare, i estem dient «agafa tot el que hi ha després de 8b6d3c2 i enganxa-ho directament sobre el seu pare». El rebase interactiu (lliçó 05-02) té una manera molt més còmoda de fer el mateix, però convé entendre que per sota és això.
- Conflictes durant un rebase
Un rebase aplica commits un a un, així que es pot aturar un cop per cada commit. La mecànica de resolució —els marcadors <<<<<<<, git status, editar, git add— és exactament la que vas aprendre a la lliçó 03-05; no la repetim. El que canvia és com se surt de l'embús.
Suposem que la Carla rebasa i el segon commit xoca:
Auto-merging app.js CONFLICT (content): Merge conflict in app.js error: could not apply 92c7a06... Pinta l'etiqueta de color al llistat hint: Resolve all conflicts manually, mark them as resolved with hint: "git add/rm <conflicted_files>", then run "git rebase --continue". hint: You can instead skip this commit: run "git rebase --skip". hint: To abort and get back to the original branch: run "git rebase --abort". Could not apply 92c7a06... Pinta l'etiqueta de color al llistat
interactive rebase in progress; onto e91d4a8 Last command done (2 commands done): pick d5e8f31 Afegeix el camp color al model de tasca pick 92c7a06 Pinta l'etiqueta de color al llistat No commands remaining. You are currently rebasing branch 'funcionalitat/etiquetes-color' on 'e91d4a8'. (fix conflicts and then run "git rebase --continue") (use "git rebase --skip" to skip this patch) (use "git rebase --abort" to checkout the original branch) Unmerged paths: (use "git restore --staged <file>..." to unstage) (use "git add <file>..." to mark resolution) both modified: app.js
Aquest bloc de git status és el teu quadre de comandaments: et diu en quin commit de la llista vas i què queda. Les tres sortides:
| Ordre | Què fa | Quan fer-la servir |
|---|---|---|
git rebase --continue |
Confirma el commit amb la teva resolució i continua amb el següent | El cas normal, després de resoldre i fer git add |
git rebase --skip |
Descarta per complet el commit conflictiu i continua | Quan aquell canvi ja és a la base i reaplicar-lo no aporta res |
git rebase --abort |
Desfà tot el rebase i retorna la branca al seu estat original | Quan t'has embolicat o el conflicte és pitjor del que esperaves |
Tres advertiments importants:
--skipno vol dir «salta't el conflicte»: vol dir «llença aquest commit a les escombraries». Si el commit tenia canvis que no són enlloc més, els perds de la branca resultant. Fes-lo servir només quan estiguis segur que el seu contingut ja està aplicat.--abortés completament segur. Retorna la branca exactament on era, amb els commits originals i els seus hashos originals. Quan dubtis, avorta i pensa amb el repositori en calma.- No facis
git commita mà durant un rebase. Després de resoldre i fergit add, ésgit rebase --continuequi crea el commit. Si fasgit commit,--continueet dirà que no queda res per confirmar (i en versions antigues pot deixar la llista descompensada).
Un detall útil: si després de resoldre el conflicte el resultat és idèntic a la base (és a dir, el teu commit ja no aporta res), git rebase --continue ho detecta i et proposa saltar-lo. És normal i no indica cap problema.
- La inversió d'
ours i theirs
ours i theirsAquí hi ha el parany que confon tothom, veterans inclosos.
En una fusió normal (lliçó 03-05), ours és la teva branca —on ets— i theirs és la branca que portes. Intuïtiu.
En un rebase s'inverteix. I no és cap caprici: és la conseqüència lògica del mecanisme. Recorda que un rebase primer es col·loca a la nova base i després aplica els teus commits al damunt, un a un, com si fossin pedaços aliens. Per tant, a cada pas:
| Banda | En un merge |
En un rebase |
|---|---|---|
ours / --ours / etapa :2: |
La teva branca actual | La nova base (main, el que ja hi havia) |
theirs / --theirs / etapa :3: |
La branca que fusiones | El teu commit, el que s'està reaplicant |
HEAD durant el conflicte |
La teva branca | La base més els commits ja reaplicats |
Dit d'una manera memorable: durant un rebase, «els seus» ets tu.
La conseqüència pràctica és directa. Si durant un rebase vols quedar-te amb la teva versió del fitxer:
# MALAMENT: això es queda amb la versió de main, no amb la teva
git checkout --ours app.js
# BÉ: durant un rebase, el teu commit és "theirs"
git checkout --theirs app.js
git add app.js
git rebase --continueEl mateix val per a -X ours / -X theirs quan els passes al rebase, i per als noms de les etapes de l'índex (:2: i :3:) que vas veure a 03-05. Si mai dubtes, no ho endevinis: mira el contingut abans de decidir.
git show :2:app.js | head -20 # "ours" = la base
git show :3:app.js | head -20 # "theirs" = el teu commitUn consell que estalvia molts disgustos: configura l'estil zdiff3 (lliçó 03-05) també aquí. Veure l'ancestre comú dins del marcador fa l'orientació molt menys dependent de recordar qui és qui:
I si el mateix conflicte es repeteix commit rere commit dins del mateix rebase, existeix git rerere (reuse recorded resolution), que memoritza com vas resoldre un conflicte i l'aplica sol la propera vegada que aparegui idèntic. Es va esmentar a 03-05 i la seva configuració se surt d'aquesta lliçó.
- Trencar la regla d'or: què passa exactament
Vegem-ho amb noms i cognoms, perquè el mecanisme del desastre explica la regla millor que qualsevol advertiment.
- En Bruno publica
funcionalitat/exportacio-csvamb tres commits:2a7f4c1,9e3b8d6,5c1a9f2. - La Carla fa
git fetchi crea la seva branca a partir d'aquella feina. Ara té aquests tres commits al seu disc. - En Bruno rebasa la seva branca sobre
mainperquèmainha avançat. Els seus tres commits es converteixen end8b2e50,71f6c34,ae95d18. - En Bruno fa
git push --force-with-lease. El servidor ara apunta aae95d18. - La Carla fa
git pull.
El que veu la Carla:
* branch funcionalitat/exportacio-csv -> FETCH_HEAD + ae95d18...5c1a9f2 funcionalitat/exportacio-csv -> origin/funcionalitat/exportacio-csv (forced update)
I a partir d'aquí, segons la seva configuració de pull:
- Amb
pull.ff only(la que vam configurar a 01-06): el pull falla amb un avís de divergència. És el millor dels casos: la Carla se n'assabenta i pot preguntar. - Amb merge: Git fusiona la cadena vella amb la nova i apareixen els tres commits duplicats, cadascun dues vegades amb hashos diferents, més un commit de fusió, més un grapat de conflictes absurds on cada canvi xoca amb si mateix.
- Amb rebase: la Carla reaplica els seus commits (que inclouen els tres vells d'en Bruno) sobre la cadena nova, i obté el mateix festival de duplicats.
I si la Carla resol malament i publica, els commits vells tornen al servidor i el rebase d'en Bruno queda desfet. És el cicle clàssic: algú rebasa, algú ho reverteix sense voler, i l'historial acaba amb tot duplicat.
Com s'evita, en ordre d'eficàcia:
- No reescriure branques compartides. Si més d'una persona hi treballa, s'integra amb merge i punt.
- Si la branca és teva i publicada només com a còpia, rebasa quan vulguis, però avisa abans de forçar i fes servir sempre
--force-with-lease(mai--force), que es nega si algú ha publicat alguna cosa que tu no tens. - Si has estat la víctima, la sortida és
git reset --hard origin/<branca>per descartar la teva còpia vella i tornar a aplicar el teu al damunt. La casuística completa, amb els passos per no perdre feina, és a la lliçó 09-03.
git pull --rebase
git pull --rebaseA la lliçó 04-04 vam deixar pendent aquesta opció. Ja la podem entendre del tot.
git pull és fetch + integració. Amb --rebase, la integració és un rebase dels teus commits locals sobre el que acabes de portar:
Comparació dels tres modes, ara completa:
| Mode | Si no has treballat en local | Si has treballat i hi ha divergència | Historial |
|---|---|---|---|
--ff-only |
Avança la branca | Falla i t'avisa | Intacte |
--no-rebase (merge) |
Avança la branca | Crea un commit de fusió | Amb nusos |
--rebase |
Avança la branca | Reaplica els teus commits al damunt | Lineal |
Per què agrada tant: quan dues persones toquen coses diferents del mateix projecte i sincronitzen sovint, pull amb merge genera un reguitzell de commits «Merge branch 'main' of git.exemple.cat…» que no expliquen absolutament res. pull --rebase els elimina.
I per què cal fer-lo servir amb cap: reescriu els teus commits locals. Això és inofensiu si no els has publicat (que és el cas típic: acabes de fer-los i encara no hi ha hagut push), i és exactament l'escenari de l'apartat 9 si sí que els has publicat.
Per deixar-ho configurat:
# Que 'git pull' sempre rebasi, a tots els repositoris
git config --global pull.rebase true
# Només per a una branca concreta
git config branch.main.rebase true
# Recomanable si actives l'anterior: no aplanar les fusions que TU vas fer expressament
git config --global pull.rebase mergespull.rebase merges (equivalent a --rebase-merges) mereix una nota: per defecte, un rebase descarta els commits de fusió de la cadena que reaplica i ho aplana tot en una línia. Si tenies fusions intencionades a la teva feina local, merges les conserva reconstruint-les.
Si deixes pull.ff only com a configuració global (la nostra recomanació de 01-06 i la més segura per aprendre), sempre pots demanar el rebase puntualment amb git pull --rebase. L'explícit guanya.
rebase.autoStash i altres opcions còmodes
rebase.autoStash i altres opcions còmodesgit rebase exigeix un directori de treball net: si tens canvis sense confirmar, es nega a començar.
La solució manual és git stash, rebasar i git stash pop (lliçó 05-04). L'automàtica:
Amb això, Git aparta els canvis sol, rebasa i els retorna en acabar. Si en retornar-los hi ha conflicte, t'avisa i el desament temporal es queda estalvi a la pila. És una comoditat excel·lent, i el seu equivalent per a pull és rebase.autoStash combinat amb pull.rebase, o git pull --rebase --autostash.
Altres opcions que convé conèixer:
| Opció / configuració | Què fa |
|---|---|
git rebase -i |
Rebase interactiu: el tema complet de la lliçó 05-02 |
--rebase-merges |
Conserva els commits de fusió en lloc d'aplanar-los |
--keep-empty |
Conserva els commits que queden buits després de reaplicar-se |
--no-verify |
No executa els hooks (mòdul 6) a cada commit reaplicat |
-X ours / -X theirs |
Resolució automàtica de conflictes per una banda (recorda la inversió!) |
--exec "<ordre>" |
Executa una ordre després de cada commit reaplicat (lliçó 05-02) |
git config rebase.updateRefs true |
Actualitza també les altres branques que apuntaven a commits del tram reaplicat |
git rebase --show-current-patch |
Ensenya el commit que està fallant ara mateix |
rebase.updateRefs resol un problema molest: si tens branques apilades (b surt d'a, c surt de b) i rebases c, les referències intermèdies es quedaven apuntant als commits vells. Amb aquesta opció activada, Git les arrossega.
- La xarxa de seguretat
Un rebase pot sortir malament: pots fer --skip d'un commit que no tocava, resoldre un conflicte al revés, o adonar-te als deu minuts que la base era una altra.
Bona notícia: els commits originals continuen allà. Com vam veure a l'apartat 2, el rebase no esborra objectes, només deixa de referenciar-los, i Git guarda un registre de per on ha passat cada referència. Aquest registre és el reflog, i amb ell es recupera l'estat anterior a un rebase gairebé sempre.
Aquest curs dedica la lliçó 09-04: Recuperant Confirmacions Perdudes a aquest tema, inclòs el procediment exacte per desfer un rebase desastrós. Aquí n'hi ha prou que sàpigues dues coses:
- Que la xarxa existeix: un rebase mal fet és recuperable durant setmanes.
- Que no és excusa per saltar-se la regla d'or, perquè el reflog és local. Recupera el teu repositori; no arregla els dels teus companys que ja es van descarregar els commits que vas reescriure.
I el reflex barat que sí que pots adoptar avui: abans d'un rebase que t'imposi respecte, deixa una marca.
git branch copia-abans-del-rebase
git rebase main
# Si surt malament: git reset --hard copia-abans-del-rebase
# Si surt bé: git branch -d copia-abans-del-rebaseUna branca són 41 bytes. La tranquil·litat, bastant més.
Errors Habituals i Consells
Error 1: creure que el rebase «mou» els commits. Els substitueix per uns de nous amb hashos diferents. Tot el perill del rebase deriva d'aquest fet, i qui l'interioritza deixa de tenir sorpreses.
Error 2: rebasar una branca compartida. Si en Bruno i la Carla treballen tots dos a funcionalitat/exportacio-csv, cap dels dos no la rebasa. S'integra amb merge. La comoditat d'un historial lineal no compensa una tarda de duplicats.
Error 3: fer servir --ours durant un rebase pensant que és la teva versió. És la de la base. Durant un rebase, tu ets theirs. Davant del dubte, git show :2:fitxer i git show :3:fitxer.
Error 4: fer servir git rebase --skip per «treure'm el conflicte de sobre». Descarta el commit sencer. Si contenia feina real, desapareix de la branca resultant.
Error 5: fer git commit enmig d'un rebase. Qui confirma és git rebase --continue. Tu només resols i fas git add.
Error 6: git push --force en lloc de --force-with-lease. El primer trepitja el que hi hagi al servidor sense mirar; el segon es nega si algú ha publicat alguna cosa que tu no tens. La diferència es va explicar a 04-05 i aquí és on de debò importa.
Error 7: rebasar sense fetch previ. git rebase main fa servir el teu main local, que pot portar dos dies desactualitzat. L'ordre correcte és git fetch origin, actualitzar main, i llavors rebasar (o rebasar directament sobre origin/main).
Consell 1: rebasa aviat i sovint. Una branca que es posa al dia cada dia té conflictes petits; una que ho fa al cap de dues setmanes té un conflicte per commit i tots grossos.
Consell 2: mira sempre git log --oneline main..HEAD abans de rebasar. Aquesta llista és exactament el que es reescriurà. Si et sorprèn la seva llargada o el seu contingut, la base que anaves a fer servir no era la que et pensaves.
Consell 3: activa rebase.autoStash i merge.conflictStyle zdiff3. Dues línies de configuració que eliminen dues de les friccions més habituals.
Consell 4: si el rebase es posa lleig, avorta. git rebase --abort és gratis i sempre funciona. Tornar-ho a provar amb el cap fred, amb els commits més petits o amb --onto ben triat sol ser molt més ràpid que insistir.
Exercicis
Exercici 1: rebase bàsic i prova de la immutabilitat
Crea un repositori de pràctiques amb una branca main i una branca funcionalitat/alguna-cosa que en surti. Afegeix dos commits a cadascuna. Després:
- Anota els hashos dels dos commits de la branca de funcionalitat.
- Rebasa-la sobre
main. - Demostra amb ordres que els hashos han canviat, que la data d'autoria s'ha conservat i que la de confirmació no.
- Demostra que els commits originals continuen existint a la base de dades d'objectes.
Exercici 2: --onto per desembolicar una branca
Reprodueix el problema de la Carla: crea branca-a sobre main amb dos commits, i branca-b sobre branca-a amb dos més. Després, fent servir git rebase --onto, aconsegueix que branca-b pengi de main contenint només els seus dos commits propis. Verifica el resultat amb git log --graph --all --oneline.
Exercici 3: conflicte en rebase i la inversió de bandes
Provoca un conflicte durant un rebase (les dues branques modificant la mateixa línia d'un fitxer). Amb el rebase aturat:
- Mostra les tres versions del fitxer des de l'índex (etapes
:1:,:2:i:3:) i identifica quina correspon al teu commit. - Resol quedant-te amb la teva versió fent servir l'opció correcta de
git checkout. - Acaba el rebase.
- Repeteix l'experiment des de zero però avortant amb
--abort, i comprova que la branca torna a tenir els seus hashos originals.
Solucions
Solució 1:
mkdir /tmp/ex-rebase && cd /tmp/ex-rebase
git init -b main
echo "linia base" > f.txt && git add . && git commit -m "Base"
git switch -c funcionalitat/alguna-cosa
echo "a" >> f.txt && git commit -am "Commit A"
echo "b" >> f.txt && git commit -am "Commit B"
git switch main
echo "una altra cosa" > g.txt && git add . && git commit -m "Commit a main 1"
echo "mes" >> g.txt && git commit -am "Commit a main 2"# 2 i 3. Rebase i comprovació de dates
git log --pretty='%h | autor: %ad | confirmat: %cd | %s' --date=iso main..HEAD
git rebase main
git log --pretty='%h | autor: %ad | confirmat: %cd | %s' --date=iso main..HEADEl hash és diferent, la data d'autoria idèntica i la de confirmació és la del moment del rebase.
Existeix com a objecte encara que cap branca no l'assoleixi. És el que fa possible la recuperació de la lliçó 09-04.
Solució 2:
mkdir /tmp/ex-onto && cd /tmp/ex-onto
git init -b main
echo "base" > f.txt && git add . && git commit -m "Base"
git switch -c branca-a
echo "a1" >> f.txt && git commit -am "A1"
echo "a2" >> f.txt && git commit -am "A2"
git switch -c branca-b
echo "b1" > b.txt && git add . && git commit -m "B1"
echo "b2" >> b.txt && git commit -am "B2"* 5e2b8c4 (HEAD -> branca-b) B2 * a93f16d B1 | * 6f4c2e9 (branca-a) A2 | * 3b7d5a8 A1 |/ * 1c8e4f2 (main) Base
branca-b penja de main amb els seus dos commits i sense rastre d'A1 ni d'A2.
Solució 3:
mkdir /tmp/ex-conflicte-rebase && cd /tmp/ex-conflicte-rebase
git init -b main
printf 'primera\nsegona\ntercera\n' > f.txt && git add . && git commit -m "Base"
git switch -c la-meva-branca
sed -i 's/segona/segona MEVA/' f.txt && git commit -am "Canvi de la meva branca"
git switch main
sed -i 's/segona/segona DE MAIN/' f.txt && git commit -am "Canvi de main"
git switch la-meva-branca
git rebase mainCONFLICT (content): Merge conflict in f.txt error: could not apply 4d9a7c2... Canvi de la meva branca
# 1. Les tres versions des de l'índex
git show :1:f.txt # base comuna
git show :2:f.txt # "ours" = main, la nova base
git show :3:f.txt # "theirs" = EL MEU commitL'etapa :3: és la meva: durant un rebase, el meu commit és theirs.
# 2 i 3. Quedar-me amb el meu i continuar
git checkout --theirs f.txt
git add f.txt
git rebase --continue
git log --oneline# 4. El mateix escenari, avortant
git reset --hard b7e3f19 # (repeteix el muntatge des de zero si ho prefereixes)
git switch la-meva-branca
git log --oneline -1 # anota el hash
git rebase main # conflicte
git rebase --abort
git log --oneline -1 # mateix hash que abans--abort restaura la branca exactament com era: mateixos commits, mateixos hashos, mateix directori de treball.
Conclusió
git rebase deixa de fer por tan bon punt s'entén què fa de debò. L'essencial d'aquesta lliçó:
- El rebase no mou commits: crea commits nous. Canvia el pare, després canvia el hash, després és un altre objecte. Els originals queden orfes però continuen a la base de dades.
git rebase <base>reaplica sobre<base>tot el que hi ha a la teva branca i no hi és;git rebase --onto <nova-base> <des-de> <fins-a>et deixa triar amb precisió quin tram moure i cap on.- Rebase davant de merge no és una qüestió tècnica sinó narrativa: el merge explica el que va passar, el rebase explica una versió ordenada. Rebase per al que encara és teu; merge per ajuntar històries públiques; davant del dubte, merge.
- Els conflictes d'un rebase es resolen igual que els d'una fusió (lliçó 03-05), però se'n surt amb
--continue,--skip(que descarta el commit) o--abort(que és sempre segur), i les bandesours/theirsestan invertides: la base ésours, el teu commit éstheirs. - La regla d'or: no reescriguis historial publicat que altres puguin tenir. Si ho fas, els teus companys acaben amb tot duplicat i l'única sortida és coordinar-se.
git pull --rebaseaplica aquesta mateixa idea en sincronitzar i elimina els commits de fusió inútils;rebase.autoStashtreu la fricció de tenir canvis sense confirmar.- El reflog és la xarxa de seguretat per al teu repositori (lliçó 09-04), no per al dels altres.
El que ve
Fins ara hem fet servir el rebase per a una sola cosa: canviar la base. Però el mateix mecanisme —descompondre una branca en una llista de commits i tornar-los a aplicar— permet molt més si et deixen editar aquesta llista abans que s'executi: canviar l'ordre, unir dos commits en un, partir-ne un en dos, corregir un missatge o eliminar un commit del tot.
Això és exactament el que fa git rebase -i, i és el que en Bruno necessita per convertir els seus commits apany, wip 2 i wip 3 en alguna cosa que es pugui ensenyar sense vergonya. Ho veiem a la lliçó 05-02: Rebase Interactiu.
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ó
