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
- Què fa
git revert - Revertint el commit del Bruno
revertdavant deresetdavant d'--amend- Revertir diversos commits i rangs
-n: agrupar diverses reversions en un commit- Conflictes en revertir
- Revertir un commit de fusió:
-m 1i-m 2 - El problema de la branca revertida que ja no torna a entrar
- Revertir una reversió
- Quan revertir i quan no
- Què fa
git revert
git revertGit 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í: és exactament per a això |
| Queda constància? | Sí, 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.
- Revertint el commit del Bruno
Situació de partida:
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:
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 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(-)
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:
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 |
revert davant de reset davant d'--amend
revert davant de reset davant d'--amendLes 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 | Sí | Sí |
| 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? | Sí | No | No |
| Abast | Qualsevol commit de l'historial | Només mou la punta | Només l'últim commit |
Requereix push --force? |
No | Sí | Sí |
| 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?
- Sí →
git revert. No hi ha alternativa segura. - No →
reseto--amendsó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,--mixedi--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:resetreescriu,revertno.
- 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..HEADUn 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:
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.
-n: agrupar diverses reversions en un commit
-n: agrupar diverses reversions en un commitTres 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 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
- 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.
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.
- Revertir un commit de fusió:
-m 1 i -m 2
-m 1 i -m 2Aquest és el cas que fa fallar tothom la primera vegada.
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:
| 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.
[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.
- 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:
"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:
[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:
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.
- 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.
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.
- 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:
- Reverteix el tercer commit amb un missatge que expliqui el motiu.
- Demostra que cap dels hashos anteriors no ha canviat.
- 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è.
- Compara conceptualment amb el que hauria passat fent servir
git reset --hardsobre 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
- Crea una branca amb dos commits i fusiona-la a
mainamb--no-ff. - Afegeix un commit més a
main. - Reverteix la fusió amb
-m 1i comprova que el contingut de la branca ha desaparegut. - Intenta fusionar la branca una altra vegada i observa el missatge.
- Resol-ho revertint la reversió i comprova que el contingut torna.
- 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# 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."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.
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.txtEl 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
filtre.js ha desaparegut: el contingut de la branca està desfet.
Aquí hi ha el problema: el graf diu que ja està integrada.
El contingut ha tornat.
* 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 --onelinegit 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 HEADcommit 2b9e6c1...
Reverteix els fitxers b, c i d
b.txt | 1 -
c.txt | 1 -
d.txt | 1 -
3 files changed, 3 deletions(-)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
pushi queda constància pública de la decisió. - És l'única manera segura de desfer alguna cosa publicada.
reseti--amendreescriuen historial i només valen per a feina que encara és teva; la lliçó 09-02 desenvolupareseta fons. - Admet diversos commits i rangs, que reverteix del més recent al més antic;
-npermet acumular diverses reversions i confirmar-les com una sola decisió. - Els conflictes es resolen com sempre, amb
--continue,--skipi--abort, i aquíours/theirsno estan invertits. Un conflicte en revertir sol significar que algú va construir a sobre: atura't a mirar. - Revertir una fusió exigeix
-mper triar el pare principal:-m 1(la branca en què eres, gairebé sempremain) 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 rebaseper reaplicar commits sobre una altra base i obtenir un historial lineal, sabent que crea commits nous.git rebase -iper reordenar, unir, dividir, reanomenar i eliminar commits abans de publicar-los, amb--fixup/--autosquashcom a flux diari i--execcom a xarxa de validació.git cherry-pickper trasplantar commits concrets entre branques que no es fusionaran, amb-xper deixar rastre igit cherryper detectar duplicats.git stashper apartar feina a mitges durant minuts o hores, amb-uper al que està sense seguiment igit stash branchquan ja no encaixa.- Les etiquetes anotades per marcar versions de manera permanent, amb SemVer,
--follow-tagsigit describe. git revertper 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
- 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ó
