Ha passat el que havia de passar. El Bruno va publicar ahir a main un commit que canvia com es desen les tasques a localStorage, i aquest matí l'Ana ha descobert que als usuaris que ja tenien tasques desades se'ls buida la llista en obrir l'aplicació. El commit és a git.exemple.cat des de fa catorze hores, l'Ana i la Carla el tenen descarregat, i a sobre hi ha tres commits més al seu damunt.

Tot el que hem après en aquest mòdul reescriu historial, i la regla d'or ho prohibeix expressament en aquesta situació: aquell commit ja és públic. No es pot rebasar, no es pot eliminar amb un rebase -i, no es pot fer desaparèixer.

Git té una resposta per a això, i és elegant precisament perquè no lluita contra la immutabilitat sinó que l'accepta: no s'esborra el commit, se n'afegeix un altre que aplica el canvi contrari. L'historial creix en lloc d'encongir-se, ningú no ha de forçar res, i queda constància pública que allò es va desfer i per què.

Això és git revert, i és l'única manera segura de desfer alguna cosa que ja està publicada. Amb ella tanquem el mòdul.

Contingut

  1. Què fa git revert
  2. Revertint el commit del Bruno
  3. revert davant de reset davant d'--amend
  4. Revertir diversos commits i rangs
  5. -n: agrupar diverses reversions en un commit
  6. Conflictes en revertir
  7. Revertir un commit de fusió: -m 1 i -m 2
  8. El problema de la branca revertida que ja no torna a entrar
  9. Revertir una reversió
  10. Quan revertir i quan no

  1. Què fa git revert

git revert <sha>

Git calcula el diff que va introduir <sha>, l'inverteix (el que afegia, ho treu; el que treia, ho afegeix) i crea un commit nou amb aquest canvi invers sobre la punta de la teva branca.

gitGraph
   commit id: "8b6d3c2"
   commit id: "c5d9b1e"
   commit id: "4f8a2e6" type: HIGHLIGHT
   commit id: "7d3a8f4"
   commit id: "e91d4a8"
   commit id: "b52c9d1"
   commit id: "3e7f1a8"

El commit 4f8a2e6 (destacat) és el que va trencar les coses. 3e7f1a8 és la seva reversió: desfà el seu efecte sense tocar res del que hi ha al mig. Els sis commits continuen allà, en el mateix ordre, amb els mateixos hashos.

Les tres propietats que el distingeixen de tota la resta del mòdul:

Propietat git revert
Reescriu historial? No: només afegeix
Canvien els hashos existents? No, cap
Cal forçar el push? No
Es pot fer servir sobre feina publicada? : és exactament per a això
Queda constància? , i és un avantatge

Aquest últim punt es malinterpreta sovint. Algú diu "vull que desaparegui, no que quedi a l'historial". Però que quedi constància és el correcte: d'aquí a sis mesos, quan algú miri per què aquella funcionalitat no hi és, l'historial l'hi dirà. Un commit esborrat no explica res; un commit revertit amb un bon missatge ho explica tot.

  1. Revertint el commit del Bruno

Situació de partida:

git switch main
git pull
git log --oneline -6
b52c9d1 (HEAD -> main, origin/main) Afegeix el filtre de pendents
e91d4a8 Extreu la creació de l'element de tasca a la seva funció
7d3a8f4 Retorna el focus al camp de text després d'esborrar una tasca
4f8a2e6 Canvia el format d'emmagatzematge a localStorage
c5d9b1e Documenta la instal·lació al README
8b6d3c2 Afegeix els estils base del llistat

El culpable és 4f8a2e6. Primer, mirar-lo:

git show 4f8a2e6
commit 4f8a2e6...
Author: Bruno Salas <[email protected]>
Date:   Thu Jul 30 16:41:09 2026 +0200

    Canvia el format d'emmagatzematge a localStorage

diff --git a/app.js b/app.js
--- a/app.js
+++ b/app.js
@@ -8,11 +8,11 @@
 function carregaTasques() {
-  const desades = localStorage.getItem('tasques');
-  return desades ? JSON.parse(desades) : [];
+  const desades = localStorage.getItem('gestor.tasques.v2');
+  return desades ? JSON.parse(desades) : [];
 }

 function desaTasques() {
-  localStorage.setItem('tasques', JSON.stringify(tasques));
+  localStorage.setItem('gestor.tasques.v2', JSON.stringify(tasques));
 }

Aquí hi ha la fallada: es va canviar la clau d'emmagatzematge sense migrar les dades existents. Ara, la reversió:

git revert 4f8a2e6

Git obre l'editor amb un missatge proposat:

Revert "Canvia el format d'emmagatzematge a localStorage"

This reverts commit 4f8a2e6c9b1d5e3a7f2c8b6d9e4a1f5c3b7d2e9a.

L'Ana el completa, perquè el missatge per defecte diu què però no per què:

Revert "Canvia el format d'emmagatzematge a localStorage"

This reverts commit 4f8a2e6c9b1d5e3a7f2c8b6d9e4a1f5c3b7d2e9a.

El canvi de clau de localStorage deixa sense tasques qui ja
tenia dades desades amb la clau anterior. Es reverteix per
restablir el servei; el canvi tornarà quan inclogui la
migració de les dades existents.
[main 3e7f1a8] Revert "Canvia el format d'emmagatzematge a localStorage"
 1 file changed, 2 insertions(+), 2 deletions(-)
git log --oneline -3
git show 3e7f1a8 --stat
3e7f1a8 (HEAD -> main) Revert "Canvia el format d'emmagatzematge a localStorage"
b52c9d1 (origin/main) Afegeix el filtre de pendents
e91d4a8 Extreu la creació de l'element de tasca a la seva funció

El diff del nou commit és exactament l'invers de l'original:

@@ -8,11 +8,11 @@
 function carregaTasques() {
-  const desades = localStorage.getItem('gestor.tasques.v2');
+  const desades = localStorage.getItem('tasques');
   return desades ? JSON.parse(desades) : [];
 }

I ara el millor de tot:

git push
To git.exemple.cat:equip/gestor-tasques.git
   b52c9d1..3e7f1a8  main -> main

Un push normal, sense --force, sense --force-with-lease, sense avisar ningú que reescrigui res. La Carla i el Bruno faran git pull, rebran un commit més i tot funcionarà. Aquest és el valor de revert: desfer sense cost social.

Opcions de git revert:

Opció Què fa
-n / --no-commit Aplica el canvi invers a l'índex sense confirmar
-e / --edit Obre l'editor per al missatge (per defecte ja ho fa)
--no-edit Fa servir el missatge automàtic sense obrir l'editor
-m <n> Tria el pare principal en revertir una fusió (apartat 7)
-s / --signoff Afegeix la línia Signed-off-by:
--continue / --skip / --abort / --quit Control del procés davant de conflictes

  1. revert davant de reset davant d'--amend

Les tres "desfan" coses i es confonen constantment. La diferència clau és si toquen l'historial existent, i això determina quina pots fer servir sobre feina publicada.

git revert <sha> git reset <sha> git commit --amend
Què fa Crea un commit nou amb el canvi invers Mou la branca a un altre commit Substitueix l'últim commit
Reescriu historial? No
Canvien hashos existents? No Els posteriors queden orfes L'últim canvia de hash
L'historial creix o encongeix? Creix Encongeix (aparentment) Igual
Sobre feina publicada Sí, és l'indicat No No
Sobre feina local Es pot, però sol haver-hi alguna cosa millor Sí, perfecte Sí, perfecte
Deixa constància? No No
Abast Qualsevol commit de l'historial Només mou la punta Només l'últim commit
Requereix push --force? No
Cas típic Una funcionalitat publicada trenca alguna cosa Desfer commits locals d'avui Corregir el missatge o afegir un oblit a l'últim commit

La manera de decidir, en una pregunta: el que vull desfer ja és al servidor i altres el poden tenir?

  • git revert. No hi ha alternativa segura.
  • Noreset o --amend són més nets, perquè el resultat no arrossega un commit de desfer que a ningú no li importa.

Un exemple que aclareix la diferència de resultat. Historial A → B → C, volem treure B:

Amb revert:   A → B → C → B'        (B' desfà B; tot continua allà)
Amb reset:    A                      (B i C deixen d'estar referenciats)

Fixa't en el detall important del reset: també s'endú C, perquè mou la branca a A i tot el posterior queda orfe. Per treure només B conservant C cal rebasar, no resetejar.

git reset és molt més del que hi cap aquí. Té tres modes —--soft, --mixed i --hard— que es comporten de manera molt diferent segons a quines de les tres zones de la lliçó 01-03 afectin, i és l'eina central per desfer feina local. Tot això, amb les taules i els procediments complets, és el tema de la lliçó 09-02: Desfent Canvis. Aquí només necessitàvem el contrast conceptual: reset reescriu, revert no.

  1. Revertir diversos commits i rangs

git revert accepta diversos commits i rangs, amb la mateixa notació de la lliçó 02-06:

# Diversos solts
git revert 4f8a2e6 7d3a8f4

# Un rang: reverteix els posteriors a A fins a B (A NO inclòs)
git revert A..B

# Amb A inclòs
git revert A^..B

# Els tres últims commits
git revert HEAD~3..HEAD

Un detall fonamental: Git els reverteix en ordre invers, del més recent al més antic. És el correcte i el que evita conflictes innecessaris: si C es recolza en B, cal desfer C abans que B. Per això git revert HEAD~3..HEAD produeix tres commits de reversió en aquest ordre:

Revert "Commit C"
Revert "Commit B"
Revert "Commit A"
git log --oneline -7
9c4e7b2 (HEAD -> main) Revert "Afegeix el filtre de pendents"
5f1d8a3 Revert "Extreu la creació de l'element de tasca a la seva funció"
2a8c6f9 Revert "Retorna el focus al camp de text després d'esborrar una tasca"
b52c9d1 Afegeix el filtre de pendents
e91d4a8 Extreu la creació de l'element de tasca a la seva funció
7d3a8f4 Retorna el focus al camp de text després d'esborrar una tasca
4f8a2e6 Canvia el format d'emmagatzematge a localStorage

Tres commits desfets, tres commits de reversió, i un historial que explica exactament el que va passar. Si prefereixes que en sigui un de sol, passem a l'apartat següent.

  1. -n: agrupar diverses reversions en un commit

Tres commits de reversió seguits solen ser soroll: el que va passar conceptualment és una decisió ("traiem la funcionalitat del filtre"), no tres. -n (--no-commit) aplica els canvis inversos a l'índex sense confirmar, i tu confirmes al final:

git revert -n HEAD~3..HEAD
git status --short
M  app.js
M  estils.css
M  index.html
git commit -m "Reverteix la funcionalitat de filtre de pendents

Es reverteixen els commits b52c9d1, e91d4a8 i 7d3a8f4 perquè el filtre
deixa el comptador desincronitzat quan hi ha tasques ocultes. Tornarà
quan el comptador es calculi sobre la llista completa."
[main 7e2c9f4] Reverteix la funcionalitat de filtre de pendents
 3 files changed, 18 insertions(+), 42 deletions(-)

Un sol commit, un sol missatge, una sola decisió. És gairebé sempre la millor manera de revertir un conjunt de commits relacionats.

Mentre ets enmig d'un revert -n, el repositori està en un estat especial (hi ha un fitxer .git/REVERT_HEAD). Si te'n penedeixes:

git revert --abort     # desfà tot l'acumulat
git revert --quit      # surt de l'estat però CONSERVA el que s'ha aplicat a l'índex

  1. Conflictes en revertir

Un revert és, per dins, una fusió: Git intenta aplicar un pedaç invers sobre un context que pot haver canviat des de llavors. Si el codi que vols desfer ha estat modificat després, conflictua.

git revert 4f8a2e6
Auto-merging app.js
CONFLICT (content): Merge conflict in app.js
error: could not revert 4f8a2e6... Canvia el format d'emmagatzematge a localStorage
hint: After resolving the conflicts, mark them with
hint: "git add/rm <pathspec>", then run "git revert --continue".
hint: You can instead skip this commit with "git revert --skip".
hint: To abort and get back to the state before "git revert",
hint: run "git revert --abort".

La mecànica de resolució és la de sempre (lliçó 03-05). L'específic:

Ordre Què fa
git revert --continue Confirma la reversió amb la teva resolució i continua amb la següent
git revert --skip Descarta aquella reversió i continua amb la resta del rang
git revert --abort Cancel·la tota l'operació i torna a l'estat inicial
git revert --quit Surt de l'estat de revert conservant el que s'ha aplicat

Com en el cherry-picking (lliçó 05-03), aquí ours i theirs NO estan invertits: ours és la teva branca, theirs és el canvi invers que s'intenta aplicar. La inversió era una peculiaritat del rebase.

I un advertiment que convé interioritzar: quan un revert conflictua, atura't a pensar. El conflicte t'està dient que algú va construir a sobre del que vols desfer. Revertir a la força pot deixar codi que crida una funció que acaba de desaparèixer. Comprova qui depèn d'allò abans de continuar:

git log --oneline 4f8a2e6..HEAD -- app.js     # què s'ha tocat després en aquell fitxer
git log -S "gestor.tasques.v2" --oneline      # qui més fa servir el que estic traient

-S és la cerca «pickaxe» de la lliçó 02-06, i aquí és exactament l'eina adequada.

  1. Revertir un commit de fusió: -m 1 i -m 2

Aquest és el cas que fa fallar tothom la primera vegada.

git revert c2a8f1e
error: commit c2a8f1ea4b7d9c3e5f2a8b6d1c9e4f7a3b5d2c8e is a merge but no -m option was given.
fatal: revert failed

Per què falla. Revertir significa aplicar el diff invers. Però un commit de fusió té dos pares, i per tant dos diffs possibles: un contra el primer pare i un altre contra el segon. Git no pot endevinar quin vols, així que s'hi nega i t'obliga a dir-ho.

gitGraph
   commit id: "8b6d3c2"
   commit id: "c5d9b1e"
   branch funcionalitat/filtre-pendents
   commit id: "a1e5c93"
   commit id: "6f2b9d4"
   checkout main
   commit id: "7d3a8f4"
   merge funcionalitat/filtre-pendents id: "c2a8f1e"
   commit id: "e91d4a8"

Els pares del commit de fusió, en ordre:

git show --format='%h %p %s' -s c2a8f1e
c2a8f1e 7d3a8f4 6f2b9d4 Fusiona funcionalitat/filtre-pendents
Número Pare Què és
1 7d3a8f4 La branca en què eres en fusionar: main
2 6f2b9d4 La branca que vas fusionar: funcionalitat/filtre-pendents

El primer pare és sempre la branca de destinació, perquè el commit de fusió es va crear estant tu en ella. El segon (i següents, en un octopus merge) són les branques portades.

-m 1 significa: "queda't amb la línia del pare 1", és a dir, desfés tot el que va aportar la branca fusionada i conserva main com estava. És el que vols el 99 % de les vegades.

git revert -m 1 c2a8f1e
[main 4d7c1a9] Revert "Fusiona funcionalitat/filtre-pendents"
 3 files changed, 4 insertions(+), 47 deletions(-)

-m 2 faria el contrari: desfer el que va aportar main i conservar la branca. És una operació raríssima i gairebé sempre indica que t'has equivocat d'ordre.

La regla mnemotècnica: -m 1 = "conserva la branca principal, llença el que vaig portar".

Consell pràctic: abans de revertir una fusió, mira'n els pares. git log --format='%h %p %s' -1 <sha> te'ls dona en un segon, i git show <sha>^1 / git show <sha>^2 t'ensenya cadascun. Amb dues ordres evites l'error.

  1. El problema de la branca revertida que ja no torna a entrar

Aquí arriba la conseqüència subtil, la que mossega setmanes després, i la raó per la qual revertir una fusió mereix un apartat propi.

El Bruno va revertir la fusió de funcionalitat/filtre-pendents. Dues setmanes després arregla el problema del comptador i vol tornar a integrar la branca:

git switch main
git merge funcionalitat/filtre-pendents
Already up to date.

"Already up to date", i a main no apareix res del filtre. Desconcertant.

Per què passa. Recorda la fusió a tres bandes de la lliçó 03-03: Git busca l'ancestre comú i compara. Però l'ancestre comú de main i funcionalitat/filtre-pendents és ara la mateixa fusió revertida (c2a8f1e), perquè aquella fusió continua a l'historial i fa que tots els commits de la branca siguin accessibles des de main.

Des del punt de vista del graf, aquells commits ja estan integrats. Que el seu contingut es desfés després amb 4d7c1a9 és irrellevant per al càlcul: Git raona sobre accessibilitat, no sobre contingut.

gitGraph
   commit id: "c5d9b1e"
   branch funcionalitat/filtre-pendents
   commit id: "a1e5c93"
   commit id: "6f2b9d4"
   checkout main
   commit id: "7d3a8f4"
   merge funcionalitat/filtre-pendents id: "c2a8f1e"
   commit id: "4d7c1a9" type: HIGHLIGHT
   commit id: "e91d4a8"

El commit destacat és la reversió. La branca està connectada al graf de main; el contingut, no.

I si el Bruno afegeix commits nous a la branca i fusiona una altra vegada, el resultat és pitjor encara: hi entren només els commits nous, sobre un main al qual li falta la base que aquells esperaven. Codi que crida funcions que no existeixen, i cap conflicte que ho avisi.

Les tres solucions, en ordre de preferència:

A. Revertir la reversió. La més simple i la que es fa servir gairebé sempre:

git revert 4d7c1a9
[main 9e2c6b1] Revert "Revert \"Fusiona funcionalitat/filtre-pendents\""
 3 files changed, 47 insertions(+), 4 deletions(-)

Això retorna el contingut de la branca a main. A partir d'aquí, els commits nous que el Bruno faci a la branca es fusionaran amb normalitat. L'historial queda una mica còmic —un "Revert del Revert"— però és correcte, honest i segur. Si vols, edita el missatge per explicar-ho:

Reintegra el filtre de pendents

Reverteix la reversió 4d7c1a9. El problema del comptador està
corregit a 8f3d2c7, així que la funcionalitat torna a entrar.

B. Refer la branca sobre el main actual. Si la branca era curta, crear una branca nova des de main i portar els commits amb git cherry-pick (lliçó 05-03) dona un historial més net:

git switch -c funcionalitat/filtre-pendents-v2 main
git cherry-pick a1e5c93 6f2b9d4
# ... corregir el problema del comptador ...

Els commits nous tenen hashos nous, no són accessibles des de main, i la fusió posterior funciona amb normalitat.

C. Forçar la fusió ignorant l'ancestre. Existeix, però gairebé mai no és bona idea; la mencionem perquè la reconeguis si la veus:

git merge --no-commit --no-ff funcionalitat/filtre-pendents

La lliçó de fons, i mereix subratllar-se:

Revertir una fusió desfà el contingut, no la topologia. El graf continua dient que aquella branca es va integrar.

Per això, quan una branca sencera s'ha de treure i se sap que tornarà, molts equips prefereixen revertir els commits individuals en lloc de la fusió, o directament no fusionar fins a estar segurs. I per això convé saber això abans de revertir una fusió, no després.

  1. Revertir una reversió

Ja ho hem fet servir a l'apartat anterior, però convé enunciar-ho: una reversió és un commit normal i corrent, així que es pot revertir.

git revert <sha-de-la-reversio>

El resultat és que el canvi original torna. És la manera canònica de dir "em vaig precipitar en desfer això".

I com que és simètric, funciona tantes vegades com vulguis: revertir la reversió de la reversió ho desfà una altra vegada. Cada pas és un commit nou, ningú no força res i l'historial explica la seqüència completa de decisions. Poc elegant, però rigorosament segur.

  1. Quan revertir i quan no

Situació Eina
Un commit publicat trenca alguna cosa git revert
Una fusió publicada s'ha de desfer git revert -m 1
Vull desfer commits locals que no he enviat git reset (lliçó 09-02)
M'he equivocat en el missatge de l'últim commit (sense publicar) git commit --amend (lliçó 02-04)
Vull treure un commit del mig d'una branca sense publicar git rebase -i amb drop (lliçó 05-02)
Vull descartar canvis sense confirmar git restore (lliçó 02-04)
Vull tornar a provar una versió antiga sense canviar res git switch --detach <sha> (lliçó 03-02)

I dues consideracions de criteri que no són tècniques:

Reverteix aviat. Si alguna cosa està trencada a main, revertir primer i diagnosticar després és gairebé sempre el correcte. Un main trencat bloqueja tot l'equip; el commit revertit no es perd i es pot reintroduir arreglat. És una decisió de servei, no d'orgull.

Escriu per què. El missatge automàtic (This reverts commit ...) diu què es va desfer però no per què. Afegir tres línies explicant el motiu i què caldria per tornar-ho a intentar converteix una reversió en informació útil d'aquí a sis mesos. Les convencions de missatges són el tema de la lliçó 08-01, però aquest cas concret val la pena mencionar-lo aquí: la reversió sense explicació és una de les entrades més frustrants que es pot trobar en un historial.

Errors Habituals i Consells

Error 1: fer servir reset en lloc de revert sobre feina publicada. És la via ràpida a l'escenari de l'apartat 9 de la lliçó 05-01: push --force, companys amb historials divergents i una tarda de coordinació. Si és al servidor, revert.

Error 2: intentar revertir una fusió sense -m. Git falla amb un missatge clar. Recorda: -m 1 en el 99 % dels casos.

Error 3: confondre l'ordre dels pares. -m 1 és la branca en què eres (normalment main); -m 2 és la que vas portar. Comprova amb git show --format='%h %p %s' -s <sha> abans de decidir.

Error 4: donar per fet que després de revertir una fusió la branca tornarà a entrar sola. No ho farà: Already up to date. Cal revertir la reversió o refer la branca.

Error 5: acceptar el missatge per defecte sense més. Diu què, no per què. Trenta segons d'explicació estalvien una investigació futura.

Error 6: revertir un commit sobre el qual altres van construir sense comprovar dependències. Pots deixar el codi cridant alguna cosa que ja no existeix. Fes servir git log -S i git log <sha>..HEAD -- <fitxer> abans.

Error 7: revertir a la branca equivocada. git revert actua sobre la branca actual. Comprova amb git status abans de llançar-lo, sobretot si véns de mirar una altra branca.

Consell 1: mira sempre el commit abans de revertir-lo. git show <sha> costa tres segons i et diu si el canvi és autocontingut o si arrossega mig projecte.

Consell 2: per a diversos commits relacionats, fes servir -n i confirma una sola vegada. Una decisió, un commit.

Consell 3: si el revert conflictua, llegeix-ho com un senyal. Algú va construir a sobre. Pot ser que la sortida correcta no sigui revertir sinó corregir cap endavant.

Consell 4: revertir és reversible. Si et precipites, git revert sobre la reversió ho retorna tot. Res del que facis amb aquesta ordre és irrecuperable.

Consell 5: a main, revertir primer i pensar després. Restablir el servei és prioritari; el diagnòstic es pot fer amb calma en una branca.

Exercicis

Exercici 1: revertir un commit publicat

En un repositori amb cinc commits, dels quals el tercer introdueix una fallada:

  1. Reverteix el tercer commit amb un missatge que expliqui el motiu.
  2. Demostra que cap dels hashos anteriors no ha canviat.
  3. Demostra que el contingut del fitxer és el que hi havia abans d'aquell commit, tret del que hi van aportar el quart i el cinquè.
  4. Compara conceptualment amb el que hauria passat fent servir git reset --hard sobre el segon commit (no cal executar-ho a la branca principal: fes-ho en una còpia).

Exercici 2: revertir una fusió i tornar-la a integrar

  1. Crea una branca amb dos commits i fusiona-la a main amb --no-ff.
  2. Afegeix un commit més a main.
  3. Reverteix la fusió amb -m 1 i comprova que el contingut de la branca ha desaparegut.
  4. Intenta fusionar la branca una altra vegada i observa el missatge.
  5. Resol-ho revertint la reversió i comprova que el contingut torna.
  6. Dibuixa (o descriu) el graf resultant.

Exercici 3: agrupar reversions

Amb una branca que tingui quatre commits sobre main, reverteix els tres últims en un únic commit fent servir -n. Verifica amb git show --stat que el commit resultant conté la reversió dels tres, i amb git diff que l'estat del projecte coincideix amb el que hi havia després del primer.

Solucions

Solució 1:

mkdir /tmp/practica-revert && cd /tmp/practica-revert
git init -b main
printf 'linia 1\n' > f.txt && git add . && git commit -m "Un"
printf 'linia 1\nlinia 2\n' > f.txt && git commit -am "Dos"
printf 'linia 1\nlinia 2\nERROR\n' > f.txt && git commit -am "Tres (la fallada)"
printf 'linia 1\nlinia 2\nERROR\nlinia 4\n' > f.txt && git commit -am "Quatre"
printf 'linia 1\nlinia 2\nERROR\nlinia 4\nlinia 5\n' > f.txt && git commit -am "Cinc"

git log --oneline
7c2e9b4 Cinc
3f8a1d6 Quatre
9b5c7e2 Tres (la fallada)
1d4f8a3 Dos
6e2b9c7 Un
# 1. Revertir el tercer
git revert 9b5c7e2 --no-edit
git commit --amend -m "Revert \"Tres (la fallada)\"

This reverts commit 9b5c7e2.

La linia ERROR s'hi va colar per un enganxat accidental i trenca
l'analisi del fitxer. Es reverteix per restablir el comportament."
# 2. Els hashos anteriors no han canviat
git log --oneline
8a1f6c3 Revert "Tres (la fallada)"
7c2e9b4 Cinc
3f8a1d6 Quatre
9b5c7e2 Tres (la fallada)
1d4f8a3 Dos
6e2b9c7 Un

Els cinc commits originals conserven els seus hashos; només se n'ha afegit un a dalt.

# 3. El contingut
cat f.txt
linia 1
linia 2
linia 4
linia 5

Ha desaparegut ERROR i es conserven les línies de Quatre i Cinc.

# 4. Comparació amb reset, en una còpia
git switch -c copia-reset 7c2e9b4
git reset --hard 1d4f8a3
git log --oneline
cat f.txt
1d4f8a3 Dos
6e2b9c7 Un
linia 1
linia 2

El reset s'ha endut per davant Tres, Quatre i Cinc: mou la branca, no desfà un canvi concret. I si aquella branca estigués publicada, el push seria rebutjat i caldria forçar-lo.

Solució 2:

mkdir /tmp/practica-revert-merge && cd /tmp/practica-revert-merge
git init -b main
echo "base" > f.txt && git add . && git commit -m "Base"

git switch -c funcionalitat/filtre
echo "filtre part 1" > filtre.js && git add . && git commit -m "Filtre part 1"
echo "filtre part 2" >> filtre.js && git commit -am "Filtre part 2"

git switch main
git merge --no-ff funcionalitat/filtre -m "Fusiona funcionalitat/filtre"
echo "una altra cosa" > altre.txt && git add . && git commit -m "Altra feina a main"

git log --oneline --graph
* 5c1e8f2 (HEAD -> main) Altra feina a main
*   9d3b7a4 Fusiona funcionalitat/filtre
|\
| * 2f8c6e1 (funcionalitat/filtre) Filtre part 2
| * 7a4d9b3 Filtre part 1
|/
* 1e6f2c8 Base
# 3. Revertir la fusió
git show --format='%h %p %s' -s 9d3b7a4
git revert -m 1 9d3b7a4 --no-edit
ls
9d3b7a4 1e6f2c8 2f8c6e1 Fusiona funcionalitat/filtre
altre.txt  f.txt

filtre.js ha desaparegut: el contingut de la branca està desfet.

# 4. Intentar fusionar una altra vegada
git merge funcionalitat/filtre
Already up to date.

Aquí hi ha el problema: el graf diu que ja està integrada.

# 5. Revertir la reversió
git log --oneline -1
git revert HEAD --no-edit
ls
cat filtre.js
4b8e2c9 Revert "Fusiona funcionalitat/filtre"
altre.txt  f.txt  filtre.js
filtre part 1
filtre part 2

El contingut ha tornat.

# 6. El graf
git log --oneline --graph
* 8f2d6a1 (HEAD -> main) Revert "Revert "Fusiona funcionalitat/filtre""
* 4b8e2c9 Revert "Fusiona funcionalitat/filtre"
* 5c1e8f2 Altra feina a main
*   9d3b7a4 Fusiona funcionalitat/filtre
|\
| * 2f8c6e1 (funcionalitat/filtre) Filtre part 2
| * 7a4d9b3 Filtre part 1
|/
* 1e6f2c8 Base

Vuit commits que expliquen la història completa: es va integrar, es va treure, es va tornar a posar. Res no s'ha reescrit i ningú no ha forçat res.

Solució 3:

mkdir /tmp/practica-revert-n && cd /tmp/practica-revert-n
git init -b main
echo "base" > f.txt && git add . && git commit -m "Base"

echo "a" > a.txt && git add . && git commit -m "Afegeix a"
echo "b" > b.txt && git add . && git commit -m "Afegeix b"
echo "c" > c.txt && git add . && git commit -m "Afegeix c"
echo "d" > d.txt && git add . && git commit -m "Afegeix d"

git log --oneline
9c2f7e4 Afegeix d
5a8d1b6 Afegeix c
3e7b9c2 Afegeix b
8f4c2a9 Afegeix a
1d6e8f3 Base
# Revertir els tres últims en un sol commit
git revert -n HEAD~3..HEAD
git status --short
D  b.txt
D  c.txt
D  d.txt
git commit -m "Reverteix els fitxers b, c i d

Es reverteixen 3e7b9c2, 5a8d1b6 i 9c2f7e4: l'enfocament d'un fitxer
per lletra no era el previst. Tornarà amb l'estructura correcta."

git show --stat HEAD
commit 2b9e6c1...
    Reverteix els fitxers b, c i d

 b.txt | 1 -
 c.txt | 1 -
 d.txt | 1 -
 3 files changed, 3 deletions(-)
# L'estat coincideix amb el que hi havia després d'"Afegeix a"
git diff 8f4c2a9 HEAD
ls
(sense sortida: són idèntics)
a.txt  f.txt

Un sol commit de reversió i un estat idèntic al del primer commit de la sèrie, sense haver tocat cap hash.

Conclusió

git revert és la peça que faltava: la manera de desfer que respecta la regla d'or. L'essencial:

  • Revertir no esborra: afegeix. Git crea un commit nou amb el canvi invers. Cap hash existent no canvia, no cal forçar el push i queda constància pública de la decisió.
  • És l'única manera segura de desfer alguna cosa publicada. reset i --amend reescriuen historial i només valen per a feina que encara és teva; la lliçó 09-02 desenvolupa reset a fons.
  • Admet diversos commits i rangs, que reverteix del més recent al més antic; -n permet acumular diverses reversions i confirmar-les com una sola decisió.
  • Els conflictes es resolen com sempre, amb --continue, --skip i --abort, i aquí ours/theirs no estan invertits. Un conflicte en revertir sol significar que algú va construir a sobre: atura't a mirar.
  • Revertir una fusió exigeix -m per triar el pare principal: -m 1 (la branca en què eres, gairebé sempre main) o -m 2 (la que vas portar, raríssim).
  • Revertir una fusió desfà el contingut, no la topologia. La branca continua sent accessible, així que tornar-la a fusionar diu Already up to date. Es resol revertint la reversió, o refent la branca amb cherry-picking.
  • Tot és reversible: una reversió és un commit normal i es pot revertir al seu torn.
  • I el criteri: a main, reverteix primer i diagnostica després, i explica sempre per què.

Tancament del mòdul 5

Amb aquesta lliçó tanques el bloc d'operacions avançades. Repassant el que ha canviat en la teva manera de treballar:

  • git rebase per reaplicar commits sobre una altra base i obtenir un historial lineal, sabent que crea commits nous.
  • git rebase -i per reordenar, unir, dividir, reanomenar i eliminar commits abans de publicar-los, amb --fixup/--autosquash com a flux diari i --exec com a xarxa de validació.
  • git cherry-pick per trasplantar commits concrets entre branques que no es fusionaran, amb -x per deixar rastre i git cherry per detectar duplicats.
  • git stash per apartar feina a mitges durant minuts o hores, amb -u per al que està sense seguiment i git stash branch quan ja no encaixa.
  • Les etiquetes anotades per marcar versions de manera permanent, amb SemVer, --follow-tags i git describe.
  • git revert per desfer el que és publicat sense reescriure res.

I sobre totes elles, la regla d'or: no reescriguis historial que ja has publicat i altres poden tenir. Rebase i interactiu, abans de publicar. Cherry-picking i revert, sempre segurs. És la línia que separa l'ús expert de l'ús temerari.

El que ve

L'equip ja sap construir l'historial i manipular-lo amb criteri. El que li falta ara és aprendre a treure'n partit.

Perquè l'historial de gestor-tasques és una base de dades enorme d'informació que fins ara amb prou feines hem consultat. Allà està escrit qui va escriure cada línia i per què, en quin commit exacte va deixar de funcionar alguna cosa que abans funcionava, i quines comprovacions hauria de passar cada commit abans d'existir. I hi ha problemes nous: el projecte comença a dependre d'una biblioteca interna que viu en el seu propi repositori, i la Carla necessita treballar en dues branques alhora sense passar-se el dia fent stash.

Al mòdul 6: Eines i Tècniques de Git veurem els hooks per automatitzar comprovacions en cada commit i en cada enviament, git bisect per trobar per cerca binària el commit exacte que va introduir una fallada, git blame per reconstruir la història de cada línia, els àlies i els formats avançats de git log per consultar tot això amb comoditat, els submòduls per compondre projectes a partir de diversos repositoris, i git worktree per tenir diverses còpies de treball del mateix repositori alhora.

Comencem per l'automatització, a la lliçó 06-01: Usant Git Hooks.

Dominant Git: De Principiant a Avançat

Mòdul 1: Introducció a Git

Mòdul 2: Operacions Bàsiques de Git

Mòdul 3: Branques i Fusió

Mòdul 4: Treballant amb Repositoris Remots

Mòdul 5: Operacions Avançades de Git

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

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

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

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

Mòdul 10: Git al Món Real

© Copyright 2026. Tots els drets reservats