A la lliçó anterior vam recórrer el cicle complet —editar, preparar, confirmar— i vam veure com git status ens guia a cada pas. Però vam fer servir git add i git commit en la seva forma més elemental: preparar fitxers sencers i confirmar amb un missatge curt.

A la pràctica això es queda curt de seguida. Una tarda de feina real no produeix canvis endreçats: produeix un fitxer amb dos arreglos que no tenen res a veure l'un amb l'altre, un fitxer de configuració que mai no s'hauria d'haver versionat, un altre que cal reanomenar i un missatge de confirmació amb una errada. Git té una resposta precisa per a cadascuna d'aquestes situacions, i dominar-les és la diferència entre un historial que es pot llegir i un que només es pot patir.

Aquesta lliçó és la més densa del mòdul. Veurem git add a fons —inclosa la diferència entre -A, -u i ., que confon gairebé tothom—, el mode interactiu per fragments, com desfer una preparació, com moure i esborrar fitxers dins de Git, i totes les variants útils de git commit, inclosa --amend. Tancarem amb el concepte que dona sentit a tot: el commit atòmic.

Contingut

  1. git add: les formes d'indicar què preparar
  2. -A enfront de -u enfront de .: la taula que resol el dubte
  3. Preparació per fragments amb git add -p
  4. Treure de l'àrea de preparació: git restore --staged
  5. Descartar canvis del directori de treball: git restore
  6. Moure i reanomenar amb git mv
  7. Esborrar amb git rm (i el cas especial --cached)
  8. git commit: totes les formes de confirmar
  9. --amend: corregir l'última confirmació
  10. El commit atòmic

  1. git add: les formes d'indicar què preparar

git add copia el contingut actual d'un o més fitxers a l'àrea de preparació. Accepta moltes formes de dir-li sobre què ha d'actuar:

# Un fitxer concret
git add app.js

# Diversos fitxers
git add app.js estils.css index.html

# Un directori sencer, recursivament
git add src/

# Tot el que hi ha sota el directori actual
git add .

# Tot el repositori, siguis on siguis dins seu
git add -A

# Un patró (cometes obligatòries, vegeu més avall)
git add "*.css"

Un detall important sobre els patrons

Quan escrius git add *.css sense cometes, qui expandeix l'asterisc és el teu intèrpret d'ordres, no Git. El resultat és que només s'expandeixen els fitxers del directori actual, i si no n'hi ha cap l'ordre falla.

Amb cometes, el patró arriba intacte a Git, que l'aplica de manera recursiva sobre tot el repositori:

git add "*.css"        # tots els .css del repositori, a qualsevol profunditat
git add *.css          # només els .css de la carpeta actual (els expandeix el shell)

És una diferència subtil amb conseqüències reals. La recomanació: fes servir cometes quan vulguis el comportament de Git i sigues conscient de quin estàs demanant.

git add també prepara esborrats i fitxers nous

Un malentès freqüent és pensar que git add serveix només per «afegir». En realitat la seva funció és sincronitzar l'àrea de preparació amb el directori de treball per a les rutes indicades. Això inclou:

  • Fitxers nous → se'n comença el seguiment.
  • Fitxers modificats → s'actualitza la seva versió preparada.
  • Fitxers esborrats del disc → es prepara l'esborrat.

Aquest últim cas sorprèn:

rm antic.js
git add antic.js       # prepara l'ESBORRAT d'antic.js
git status -s
# → D  antic.js

Sí: git add sobre un fitxer que ja no existeix registra la seva desaparició. Té sentit si penses en add com a «pren nota de l'estat actual d'aquesta ruta».

Comprovar sense executar

Dues opcions molt útils abans d'un add ampli:

# Mostra què faria, sense fer-ho
git add --dry-run .
git add -n .
add 'app.js'
add 'estils.css'
add 'src/utilitats.js'
# Mostra el detall del que va afegint
git add --verbose .

En un repositori gran o desconegut, git add -n . costa un segon i evita disgustos.

  1. -A enfront de -u enfront de .: la taula que resol el dubte

Aquesta és, amb diferència, la confusió més estesa sobre git add. Les tres formes s'assemblen i fan coses diferents. La diferència s'explica amb dos eixos: quins tipus de canvi inclouen i sobre quina part del repositori actuen.

Forma Fitxers nous Fitxers modificats Fitxers esborrats Abast
git add . Només des del directori actual cap avall
git add -A (--all) Tot el repositori, siguis on siguis
git add -u (--update) No Tot el repositori
git add <ruta> Només aquesta ruta

Dues conclusions es llegeixen directament de la taula:

  • -u és l'únic que no inclou fitxers nous. Actua exclusivament sobre fitxers que Git ja rastreja. És l'opció correcta quan vols registrar tota la teva feina sobre fitxers coneguts sense arriscar-te a afegir un fitxer nou per accident.
  • . i -A fan el mateix llevat de l'abast. La diferència només es nota si no ets a l'arrel del repositori.

Demostració pràctica

Preparem l'escenari al repositori de l'Ana:

cd ~/projectes/gestor-tasques
mkdir -p src
echo "// utilitats" > src/utilitats.js       # nou, dins de src/
echo "/* nou */" >> estils.css               # modificat, a l'arrel
rm README.md                                 # esborrat, a l'arrel
echo "# temporal" > ESBORRANY.md             # nou, a l'arrel
git status -s
 M estils.css
 D README.md
?? ESBORRANY.md
?? src/

Cas A — git add . des de l'arrel:

git add .
git status -s
A  ESBORRANY.md
D  README.md
M  estils.css
A  src/utilitats.js

Tot hi entra: nous, modificats i esborrats.

Cas B — git add . des d'un subdirectori:

git reset            # desfem la preparació anterior
cd src
git add .
git status -s
 M estils.css
 D README.md
?? ESBORRANY.md
A  src/utilitats.js

Aquí hi ha el parany. Només s'ha preparat src/utilitats.js, perquè . significa «el directori actual». Els canvis de l'arrel segueixen sense preparar. Algú que confirmés ara, convençut d'haver-ho afegit tot, deixaria fora tres canvis.

Cas C — git add -A des del mateix subdirectori:

git reset
git add -A            # continuem dins de src/
git status -s
A  ESBORRANY.md
D  README.md
M  estils.css
A  src/utilitats.js

-A ignora on ets i actua sobre tot el repositori. És el comportament que la majoria de la gent creu que demana quan escriu git add ..

Cas D — git add -u:

git reset
git add -u
git status -s
D  README.md
M  estils.css
?? ESBORRANY.md
?? src/

Només s'han preparat els canvis de fitxers ja rastrejats: la modificació d'estils.css i l'esborrat de README.md. Els dos fitxers nous continuen sense seguiment. Aquest és el comportament que vols quan treballes enmig d'una carpeta plena de fitxers generats i no et fies d'un add massiu.

Nota històrica. Abans de Git 2.0, git add . no preparava els esborrats, i aquella asimetria causava moltíssims problemes. Des de Git 2.0 el comportament és el descrit a la taula. Si llegeixes documentació antiga que diu el contrari, està desactualitzada.

Quin fer servir

  • git add <ruta> quan saps exactament què vols. És l'opció per defecte de qui cuida el seu historial.
  • git add -u per registrar tota la teva feina sobre fitxers coneguts, sense sorpreses.
  • git add -A quan de debò ho vols tot i tens un .gitignore en què confies.
  • git add . només si ets a l'arrel i saps que ets a l'arrel.

I en qualsevol cas: git status abans de confirmar.

  1. Preparació per fragments amb git add -p

Arriba el dimarts següent. L'Ana vol implementar el marcatge de tasques com a completades, però mentre hi és veu un color lleig a estils.css i el corregeix de passada. En acabar, app.js conté dos canvis sense relació: la funcionalitat nova i una correcció menor d'un missatge de consola que també va arreglar al vol.

Preparar app.js sencer barrejaria les dues coses en la mateixa confirmació. La solució és el mode per fragments:

git add -p app.js
git add --patch app.js     # forma llarga

Git recorre el fitxer mostrant els canvis en trossos (hunks) i pregunta què fer amb cadascun:

diff --git a/app.js b/app.js
index 7b2e8f1..3c9d4a2 100644
--- a/app.js
+++ b/app.js
@@ -12,4 +12,5 @@ function afegeixTasca(text) {
   tasques.push({ id: Date.now(), text: text, feta: false });
   pintaLlista();
+  actualitzaComptador();
 }

(1/3) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]?

Les respostes possibles són moltes, però a la pràctica se'n fan servir cinc:

Tecla Acció
y , prepara aquest fragment
n No, deixa'l sense preparar
s Divideix (split) el fragment en altres de més petits
e Edita el fragment a mà, línia a línia
q Surt del mode interactiu
a Prepara aquest fragment i tots els que queden del fitxer
d No preparis ni aquest ni cap dels que queden del fitxer
? Mostra l'ajuda amb totes les opcions

Vegem la sessió de l'Ana completa. Primer fragment: la crida al comptador, que forma part de la funcionalitat nova.

(1/3) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? y

Segon fragment: la funció de marcatge.

@@ -25,4 +26,12 @@ function pintaLlista() {
     const li = document.createElement('li');
     li.textContent = tasca.text;
+    li.addEventListener('click', function () {
+      tasca.feta = !tasca.feta;
+      pintaLlista();
+      actualitzaComptador();
+    });
+    if (tasca.feta) {
+      li.classList.add('feta');
+    }
     llista.appendChild(li);

(2/3) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? y

Tercer fragment: la correcció del missatge de consola, que no pertany a aquesta confirmació.

@@ -48,4 +57,4 @@ document.querySelector('#nova-tasca').addEventListener('submit', function (esdev
   if (camp.value.trim() !== '') {
     afegeixTasca(camp.value.trim());
-    console.log('tasca afegia');
+    console.log('Tasca afegida correctament');
     camp.value = '';

(3/3) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? n

Resultat:

git status -s
MM app.js
 M estils.css

app.js surt com a MM: una part preparada (la funcionalitat) i una altra pendent (el missatge). Ara l'Ana pot confirmar només la primera:

git commit -m "Marca les tasques com a completades en fer clic"
[main 9d1e4b7] Marca les tasques com a completades en fer clic
 1 file changed, 9 insertions(+)

I després, en una confirmació a part, la resta:

git add app.js estils.css
git commit -m "Corregeix el missatge de consola i el color del text secundari"

Dues confirmacions netes on hi havia un poti-poti.

s: dividir un fragment

Si dos canvis independents cauen dins del mateix fragment perquè són a prop, s el parteix:

(1/2) Stage this hunk [y,n,q,a,d,s,e,?]? s
Split into 2 hunks.

Git només pot dividir quan hi ha almenys una línia de context sense canvis entre els dos blocs. Si els canvis són literalment contigus, s no estarà disponible i caldrà fer servir e.

e: editar el fragment a mà

És l'opció més potent i la que fa més por. Git obre el fragment al teu editor (el que vas configurar a Configuració Inicial) amb instruccions al final:

# To remove '-' lines, make them ' ' lines (context).
# To remove '+' lines, delete them.

Les dues regles que necessites:

  • Per no preparar una línia afegida (+), esborra-la del fragment.
  • Per no preparar una línia eliminada (-), canvia-li el - per un espai.

No esborris mai una línia que comenci per - ni afegeixis línies noves: el pedaç deixaria d'aplicar-se i Git protestaria (encara que et donarà l'oportunitat de reintentar-ho).

Ordres relacionades. -p funciona també amb altres ordres: git restore -p, git stash -p, git checkout -p. I git add -i obre un menú interactiu complet, del qual -p només és una opció. A la pràctica, -p cobreix el 95 % dels casos.

  1. Treure de l'àrea de preparació: git restore --staged

Has preparat una cosa que no volies. L'operació inversa és:

git restore --staged <fitxer>

Exemple. L'Ana ho prepara tot per costum i s'adona que ESBORRANY.md no hi havia d'entrar:

git add -A
git status -s
A  ESBORRANY.md
M  app.js
M  estils.css
git restore --staged ESBORRANY.md
git status -s
M  app.js
M  estils.css
?? ESBORRANY.md

ESBORRANY.md ha tornat a ser un fitxer sense seguiment. El seu contingut al disc no s'ha tocat: --staged actua només sobre l'àrea de preparació.

La forma antiga: git reset HEAD

Veuràs moltíssima documentació —i molts companys— fent servir això:

git reset HEAD <fitxer>
git reset <fitxer>           # equivalent, HEAD és el valor per defecte
git reset                    # ho treu TOT de l'àrea de preparació

Fa exactament el mateix. La diferència és de disseny: git reset és una ordre amb tres modes i diversos significats, i git checkout també estava sobrecarregada. Per resoldre-ho, Git 2.23 (2019) va introduir dues ordres amb un propòsit clar cadascuna:

Tasca Forma moderna Forma antiga
Treure de l'àrea de preparació git restore --staged <f> git reset HEAD <f>
Descartar canvis del disc git restore <f> git checkout -- <f>
Canviar de branca git switch <branca> git checkout <branca>

Les formes antigues continuen funcionant i les veuràs pertot arreu. Fes servir les modernes a la teva feina i reconeix les antigues quan les llegeixis. L'ús de git reset per moure el HEAD i reescriure l'historial és un altre assumpte, i es tracta a Desfent Canvis.

  1. Descartar canvis del directori de treball: git restore

Sense --staged, git restore fa una cosa força més seriosa: sobreescriu el fitxer del disc amb la versió de referència, descartant els teus canvis.

git restore <fitxer>

L'Ana experimenta amb un disseny a estils.css, no li agrada gens i vol tornar enrere:

git status -s
# →  M estils.css

git restore estils.css

git status -s
# → (buit)

El fitxer ha tornat a com estava. Els canvis s'han perdut de manera irrecuperable: no eren en cap confirmació ni a l'àrea de preparació, així que Git no en té cap còpia. Aquesta és una de les poques ordres de Git que destrueix feina sense xarxa de seguretat.

Avís seriós. Abans d'un git restore, pregunta't si de debò vols llençar aquella feina. Si dubtes, hi ha alternatives que la desen: git stash (lliçó 05-04) o simplement confirmar-la i decidir després.

La combinació importa

Quan un fitxer és en estat MM —preparat i modificat després— cal tenir clar què descarta cada variant:

Ordre Àrea de preparació Directori de treball
git restore --staged f Es restaura des de HEAD No es toca
git restore f No es toca Es restaura des de l'àrea de preparació
git restore --staged --worktree f Es restaura des de HEAD Es restaura des de HEAD
git restore --source=HEAD~2 f S'hi posa la versió de fa 2 commits

Fixa't en la segona fila: git restore f tot sol restaura des de l'àrea de preparació, no des de l'últim commit. Si havies preparat una versió intermèdia, és a aquella a la qual tornes.

Opcions útils:

# Descartar canvis de TOTS els fitxers del repositori (perillós)
git restore :/

# Descartar per fragments, revisant-los un a un
git restore -p estils.css

# Portar al disc una versió antiga d'un fitxer
git restore --source=HEAD~3 app.js

Aquesta última no és un «desfer»: modifica el teu directori de treball amb contingut antic, que després pots preparar i confirmar normalment. És una manera senzilla de recuperar l'estat d'un fitxer concret sense tocar la resta del projecte.

  1. Moure i reanomenar amb git mv

L'Ana ha creat un fitxer notes-versio.md i vol que es digui CANVIS.md. Ho podria fer amb l'explorador de fitxers, però llavors Git veuria un fitxer esborrat i un altre de nou. El correcte és:

git mv notes-versio.md CANVIS.md
git status -s
R  notes-versio.md -> CANVIS.md

El codi R indica un reanomenament, i el canvi ja està preparat: git mv prepara el resultat automàticament.

git mv és una drecera

Aquesta ordre no és màgia. És exactament equivalent a tres ordres:

mv notes-versio.md CANVIS.md
git rm --cached notes-versio.md
git add CANVIS.md

De fet, si haguessis fet servir mv tot sol, Git detectaria el reanomenament igualment en preparar tots dos canvis:

mv notes-versio.md CANVIS.md
git add -A
git status -s
# → R  notes-versio.md -> CANVIS.md

Això connecta amb una cosa fonamental del model de dades que vam veure a 01-04: Git no emmagatzema reanomenaments. Un blob guarda contingut, i el nom viu al tree. El que Git fa és detectar reanomenaments a posteriori, comparant el contingut dels fitxers esborrats i afegits. Si el contingut és idèntic, la detecció és infal·lible; si vas canviar molt el fitxer en moure'l, potser no ho detecti i ho mostri com un esborrat més un fitxer nou.

Usos habituals

# Reanomenar
git mv estils.css estils-principal.css

# Moure a un subdirectori (ha d'existir)
mkdir css
git mv estils.css css/estils.css

# Moure diversos fitxers a un directori
git mv app.js utilitats.js src/

# Forçar la sobreescriptura del destí
git mv -f esborrany.md CANVIS.md

I el cas incòmode: canviar només majúscules i minúscules a macOS o Windows, on el sistema de fitxers no distingeix entre Llegeixme.md i LLEGEIXME.md. Com que en Bruno treballa a macOS, li passarà:

git mv llegeixme.md LLEGEIXME.md
# → fatal: destination exists

La solució és el pas intermedi:

git mv llegeixme.md temporal.md
git mv temporal.md LLEGEIXME.md

O fer servir -f, que a les versions recents de Git resol aquest cas concret.

  1. Esborrar amb git rm (i el cas especial --cached)

Per eliminar un fitxer del projecte:

git rm antic.js
rm 'antic.js'
git status -s
# → D  antic.js

Fa dues coses alhora: esborra el fitxer del disc i prepara l'esborrat. Equival a rm antic.js && git add antic.js.

Si Git detecta que el fitxer té canvis sense confirmar, s'hi nega:

git rm app.js
error: the following file has local modifications:
    app.js
(use --cached to keep the file, or -f to force removal)

És una protecció deliberada: t'avisa que estàs a punt de perdre feina. Si n'estàs segur, -f.

git rm --cached: el cas realment important

Aquesta variant treu el fitxer del control de versions però el deixa al disc. És la resposta a un problema molt freqüent: un fitxer que mai no s'hauria d'haver versionat i que ja està rastrejat.

Recorda de l'apartat 6 de la lliçó anterior: .gitignore només actua sobre fitxers sense seguiment. Si config.local.json ja és a l'historial, afegir-lo al .gitignore no fa res. La seqüència correcta és:

# 1. Deixar de rastrejar-lo, sense esborrar-lo del disc
git rm --cached config.local.json
rm 'config.local.json'
# 2. Assegurar-se que no hi torni
echo "config.local.json" >> .gitignore

# 3. Confirmar les dues coses
git add .gitignore
git commit -m "Deixa de versionar la configuració local de l'entorn"
ls config.local.json
# → config.local.json     ← continua allà, intacte
git status -s
# → (buit: ara està ignorat)

Per a un directori sencer cal -r (recursiu):

git rm -r --cached node_modules/

Un truc molt útil quan has arreglat el .gitignore i vols que s'apliqui a tot allò ja rastrejat:

git rm -r --cached .        # deixa de rastrejar-ho tot (sense esborrar res del disc)
git add .                   # torna a afegir, ara respectant el .gitignore
git status -s               # revisa què ha quedat fora
git commit -m "Aplica el .gitignore als fitxers ja versionats"

Advertència important. git rm --cached deixa de rastrejar un fitxer des d'ara, però no l'esborra de l'historial. Totes les confirmacions anteriors continuen contenint-lo, i qualsevol amb accés al repositori el pot recuperar. Si el fitxer contenia credencials, això no les protegeix: cal rotar-les i, si és imprescindible, reescriure l'historial. Ho tractarem a Bones Pràctiques de Seguretat.

Resum d'esborrat

Ordre Fitxer al disc Rastrejat per Git
rm f (del sistema) Esborrat Continua rastrejat; esborrat sense preparar
git rm f Esborrat Esborrat, preparat
git rm --cached f Es conserva Deixa de rastrejar-se
git rm -f f Esborrat encara que tingui canvis Esborrat, preparat
git rm -r --cached dir/ Es conserva Tot el directori deixa de rastrejar-se

  1. git commit: totes les formes de confirmar

Amb l'àrea de preparació a punt, toca registrar. La forma que ja coneixes:

git commit -m "Marca les tasques com a completades en fer clic"

I aquestes són les variants que val la pena conèixer.

Sense -m: l'editor

git commit

Git obre el teu editor amb una plantilla:

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch main
# Changes to be committed:
#	modified:   app.js
#	modified:   estils.css
#

Escrius a dalt, deses i tanques. Si deses sense escriure res, el commit es cancel·la. Les línies que comencen per # es descarten.

Aquesta forma té dos avantatges sobre -m: et mostra què confirmaràs mentre escrius el missatge (una última oportunitat de detectar un error), i facilita escriure missatges de diverses línies.

Missatges multilínia

Un missatge de confirmació té dues parts: una primera línia de resum i, opcionalment, un cos separat per una línia en blanc.

Des de l'editor és natural. Des de la línia d'ordres, hi ha dues formes:

# Amb diversos -m: cadascun és un paràgraf, separat per línia en blanc
git commit -m "Marca les tasques com a completades en fer clic" \
           -m "L'estat es desa a l'array en memòria; en recarregar la pàgina es perd. La persistència s'abordarà quan existeixi l'emmagatzematge local."
# Amb un salt de línia real (Bash i Zsh)
git commit -m "Marca les tasques com a completades en fer clic

L'estat es desa a l'array en memòria; en recarregar la
pàgina es perd."

El resultat és idèntic. Moltes ordres —git log --oneline, la llista de confirmacions de les plataformes web— mostren només la primera línia, així que aquella línia ha de funcionar tota sola.

-a: preparar i confirmar d'un cop

git commit -a -m "Ajusta el color del text secundari"
git commit -am "Ajusta el color del text secundari"     # abreujat

Prepara automàticament tots els fitxers rastrejats que estiguin modificats o esborrats, i confirma.

El seu risc és que se salta l'àrea de preparació, és a dir, se salta la decisió de què hi entra. Amb -a confirmes tot el que hagis tocat, incloent-hi:

  • El console.log de depuració que vas deixar a app.js.
  • L'ajust de tres línies en un fitxer que no té res a veure amb el que estàs confirmant.
  • El canvi que anaves a revisar millor abans de registrar-lo.

A més, no inclou fitxers sense seguiment, cosa que produeix l'error contrari: et penses que ho has confirmat tot i el fitxer nou es queda fora, el projecte no compila a la màquina del teu company i ningú no entén per què.

git add + git commit git commit -a
Fitxers nous S'inclouen si els afegeixes Mai no s'inclouen
Fitxers modificats Els que triïs Tots
Fitxers esborrats Els que triïs Tots
Control sobre el contingut Total Cap
Risc de commit remenat Baix Alt

Fes-lo servir quan estiguis segur que tot el que has modificat pertany a la mateixa confirmació, i tot i així mira git status abans. Com a pràctica per defecte, no el recomano.

Altres opcions útils

# Veure el diff del que confirmaràs mentre escrius el missatge
git commit -v

# Confirmar encara que no hi hagi res preparat (útil en scripts i per marcar fites)
git commit --allow-empty -m "Dispara el desplegament"

# Fixar una data concreta d'autoria
git commit --date="2026-07-15 10:00:00" -m "..."

# Confirmar en nom d'una altra persona (treball en parella)
git commit --author="Bruno Salas <[email protected]>" -m "..."

# Signar criptogràficament la confirmació
git commit -S -m "..."

git commit -v mereix un comentari a part: afegeix al peu de la plantilla el diff complet del que està preparat. És la millor manera de revisar la teva pròpia feina just abans de registrar-la, i molts desenvolupadors l'activen sempre:

git config --global commit.verbose true

  1. --amend: corregir l'última confirmació

L'Ana confirma i, un segon després, veu l'errada al missatge:

git log --oneline -1
# → 9d1e4b7 Marca les tasques com a completdes en fer clic

Solució:

git commit --amend -m "Marca les tasques com a completades en fer clic"
git log --oneline -1
# → 5c2e8b4 Marca les tasques com a completades en fer clic

Corregit. Fixa't que el hash ha canviat: era 9d1e4b7 i ara és 5c2e8b4.

--amend serveix per a tres coses:

# 1. Canviar només el missatge
git commit --amend -m "Missatge corregit"

# 2. Afegir un fitxer oblidat a l'últim commit
git add fitxer-que-vaig-oblidar.js
git commit --amend --no-edit          # --no-edit conserva el missatge actual

# 3. Corregir l'autoria
git commit --amend --author="Ana Ferrer <[email protected]>" --no-edit

El segon cas és el més útil en el dia a dia: acabes de confirmar i descobreixes que faltava un fitxer. En comptes de crear una confirmació «Afegeix el fitxer que faltava», l'incorpores al commit anterior i l'historial queda net.

--amend reescriu l'historial

Això és el que cal entendre bé, i connecta directament amb El Model de Dades de Git.

--amend no modifica la confirmació existent. No pot: els objectes de Git són immutables, i el seu identificador és el hash del seu contingut. El que fa és:

  1. Crear un commit nou amb el mateix pare que l'anterior i el contingut/missatge corregits.
  2. Moure la branca actual a aquest commit nou.
  3. Deixar el commit antic orfe, sense res que hi apunti.
graph LR
    subgraph "Abans"
        A1["c5d9b1e"] --> B1["2f6a3c8"] --> C1["9d1e4b7<br/>(missatge amb errada)"]
        M1["main"] -.-> C1
    end
    subgraph "Després de --amend"
        A2["c5d9b1e"] --> B2["2f6a3c8"] --> C2["9d1e4b7<br/>(orfe)"]
        B2 --> D2["5c2e8b4<br/>(missatge corregit)"]
        M2["main"] -.-> D2
    end

D'aquí ve la regla d'or:

No facis mai --amend sobre una confirmació que ja has compartit.

Si la confirmació 9d1e4b7 ja era al servidor i en Bruno se l'havia baixat, en reescriure-la el teu historial i el seu divergeixen: ell té un commit que tu ja no tens, i el teu commit nou li resulta desconegut. Arreglar-ho requereix feina i coordinació. Mentre la confirmació estigui només a la teva màquina, --amend és segur i molt recomanable.

La regla general —reescriu lliurement allò local, mai allò compartit— val també per al rebase (mòdul 5) i per a totes les tècniques de neteja d'historial (08-02).

  1. El commit atòmic

Tot l'anterior existeix per a una cosa: poder crear confirmacions atòmiques.

Un commit atòmic és aquell que conté un canvi complet i només un. Dues condicions simultànies:

  • Complet: inclou tot el que cal perquè aquell canvi funcioni. Si afegeixes una funció i el seu estil, tots dos van junts; una confirmació que deixa el projecte trencat no és atòmica.
  • Únic: no inclou res que no pertanyi a aquell canvi. Ni l'arreglada d'ortografia de passada, ni el console.log de depuració.

La regla mínima

Aquí va l'única regla que necessites de moment, i que a més funciona com a test d'atomicitat:

Si en descriure la teva confirmació necessites la paraula «i», probablement haurien de ser dues confirmacions.

Aplicat al cas de l'Ana: «Marca les tasques com a completades i corregeix el missatge de consola i ajusta el color». Tres «i» implícits, tres confirmacions. Per això va fer servir git add -p.

Per què val la pena

Amb commits atòmics Amb commits remenats
git log explica la història real del projecte L'historial és una llista de «canvis diversos»
Desfer un canvi concret és trivial Desfer arrossega coses que no volies
git bisect troba el commit culpable exacte El commit culpable conté deu canvis
Les revisions de codi són ràpides Ningú no vol revisar 40 fitxers barrejats
git blame explica de debò cada línia git blame apunta a «canvis del divendres»

Els beneficis es cobren mesos després, quan alguna cosa falla i cal esbrinar per què. Eines com Git Bisect i Git Blame depenen completament d'aquesta disciplina.

Sobre els missatges

Un commit atòmic necessita un missatge que el descrigui. De moment, tres normes suficients:

  1. La primera línia, curta (uns 50 caràcters) i descriptiva per si sola.
  2. En imperatiu o infinitiu, descrivint què fa la confirmació: «Afegeix el comptador de tasques», no «Afegit el comptador» ni «canvis».
  3. El què a la primera línia, el perquè al cos si cal explicar-ho.

Les convencions completes —Conventional Commits, longituds, cos, peus, referències a incidències— es tracten a Escrivint Bons Missatges de Confirmació. Amb aquestes tres normes ja escriuràs missatges millors que la mitjana.

Errors Habituals i Consells

  • Fer servir git add . des d'un subdirectori creient que ho afegeix tot. Només afegeix des d'allà cap avall. Si vols tot el repositori, és git add -A.
  • Esperar que -u inclogui fitxers nous. No ho fa, per disseny. És el seu avantatge, no el seu defecte.
  • Confiar en git commit -a. Confirma tot el que s'ha modificat sense filtre i deixa fora el que és nou. És la recepta dels commits remenats i dels fitxers oblidats.
  • git restore <fitxer> a la lleugera. Destrueix els teus canvis sense còpia de seguretat. És de les poquíssimes ordres de Git sense xarxa. Si dubtes, git stash.
  • Confondre git restore --staged amb git restore. El primer treu de l'àrea de preparació i no toca el disc; el segon sobreescriu el disc. La diferència és un canvi desfet enfront d'un canvi perdut.
  • Creure que git rm --cached esborra el fitxer de l'historial. No l'esborra: continua a totes les confirmacions anteriors. Per a secrets, això no n'hi ha prou.
  • Fer --amend sobre una confirmació ja compartida. Reescriu l'historial i provoca divergència amb els teus companys. Només sobre allò que encara és a la teva màquina.
  • Confirmar la feina de tres dies en un sol commit. Ningú no el pot revisar, desfer ni entendre. Confirma aviat i sovint.
  • Consell: activa git config --global commit.verbose true. Veure el diff mentre escrius el missatge detecta moltíssims errors abans que entrin a l'historial.
  • Consell: si git add -p et resulta lent al principi, insisteix una setmana. És l'hàbit que més millora la qualitat d'un historial, i acaba sent automàtic.
  • Consell: quan dubtis de què confirmaràs, git diff --staged t'ho ensenya exactament. És el tema de la lliçó següent.

Exercicis

Exercici 1: Dominar -A, -u i .

Munta aquest escenari:

mkdir -p ~/practica/add-test/components && cd ~/practica/add-test
git init
echo "inicial" > arrel.txt
echo "inicial" > components/boto.js
git add -A && git commit -m "Estat inicial"

echo "canvi" >> arrel.txt
echo "canvi" >> components/boto.js
echo "nou" > arrel-nou.txt
echo "nou" > components/menu.js
rm arrel.txt

Situa't dins de components/ i, executant git reset entre proves per tornar al punt de partida, determina què prepara exactament cadascuna d'aquestes ordres. Anota la sortida de git status -s en cada cas i explica'n el perquè:

  1. git add .
  2. git add -A
  3. git add -u
  4. git add ..

Exercici 2: Separar dos canvis d'un mateix fitxer

Al repositori de gestor-tasques (o en un de prova amb un app.js semblant), fes aquests dos canvis alhora a app.js:

  • Canvi A (funcionalitat): afegeix una funció esborraTasca(id) que elimini una tasca de l'array i repinti la llista.
  • Canvi B (manteniment): corregeix un comentari mal escrit a la capçalera del fitxer.

Després:

  1. Fes servir git add -p per preparar només el canvi A.
  2. Demostra amb git status -s que el fitxer és en estat MM.
  3. Confirma el canvi A amb el missatge «Afegeix l'esborrat de tasques al llistat».
  4. Confirma el canvi B en una segona confirmació.
  5. Comprova amb git log --oneline que hi ha dues confirmacions i amb git show --stat que cadascuna toca el que ha de tocar.

Exercici 3: Rescatar un repositori mal preparat

Un company et passa un repositori en aquest estat, just després d'haver confirmat:

git log --oneline -1
# → 7f3c9a2 arreglos diversos

git show --stat HEAD
 .env                  |  3 +
 app.js                | 24 ++++++++---
 estils.css            |  6 ++--
 node_modules/lib/a.js | 99 +++++++++++++++++
 README.md             |  2 +-
 5 files changed, 128 insertions(+), 6 deletions(-)

La confirmació encara no s'ha compartit amb ningú. Resol:

  1. Quins són els tres problemes d'aquesta confirmació?
  2. Escriu la seqüència d'ordres que deixa de versionar .env i node_modules/, els afegeix al .gitignore i conserva tots dos fitxers al disc.
  3. Corregeix el missatge de la confirmació per un de descriptiu, sense crear una confirmació addicional.
  4. Què hauria canviat a la teva resposta si la confirmació ja s'hagués compartit? I quina mesura addicional caldria prendre pel fet que .env hagi estat versionat?

Solucions

Solució a l'Exercici 1

Estat de partida (vist des de l'arrel):

 M components/boto.js
 D arrel.txt
?? components/menu.js
?? arrel-nou.txt

1. git add . des de components/:

cd ~/practica/add-test/components
git add .
git status -s
M  components/boto.js
A  components/menu.js
 D arrel.txt
?? arrel-nou.txt

Prepara tot (nous, modificats i esborrats) però només des del directori actual cap avall. Els canvis de l'arrel queden fora.

2. git add -A:

git reset
git add -A
git status -s
M  components/boto.js
A  components/menu.js
D  arrel.txt
A  arrel-nou.txt

Prepara tot el repositori, ignorant completament on ets. Les quatre entrades tenen la primera columna ocupada.

3. git add -u:

git reset
git add -u
git status -s
M  components/boto.js
D  arrel.txt
?? components/menu.js
?? arrel-nou.txt

Actua sobre tot el repositori, però només sobre fitxers rastrejats: la modificació de boto.js i l'esborrat d'arrel.txt. Els dos fitxers nous continuen sense seguiment.

4. git add ..:

git reset
git add ..
git status -s
M  components/boto.js
A  components/menu.js
D  arrel.txt
A  arrel-nou.txt

Mateix resultat que -A, però per una altra raó: .. és l'arrel del repositori, així que «des d'allà cap avall» és tot. Confirma que la diferència entre . i -A és exclusivament el punt de partida del recorregut.

Resum:

Ordre (des de components/) boto.js menu.js arrel.txt arrel-nou.txt
git add . No No
git add -A
git add -u No No
git add ..

Solució a l'Exercici 2

Els dos canvis a app.js. Capçalera (canvi B):

// gestor-tasques — logica prinicpal       ← abans
// gestor-tasques — lògica principal       ← després

I la funció nova (canvi A):

function esborraTasca(id) {
  const index = tasques.findIndex(function (t) { return t.id === id; });
  if (index !== -1) {
    tasques.splice(index, 1);
    pintaLlista();
    actualitzaComptador();
  }
}

1. Preparar només el canvi A:

git add -p app.js

El primer fragment que mostra Git és el de la capçalera (és més amunt al fitxer):

@@ -1,3 +1,3 @@
-// gestor-tasques — logica prinicpal
+// gestor-tasques — lògica principal
 const tasques = [];

(1/2) Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? n

Responem n: no pertany a aquesta confirmació. El segon és la funció:

@@ -30,6 +30,15 @@ function pintaLlista() {
+function esborraTasca(id) {
+  const index = tasques.findIndex(function (t) { return t.id === id; });
...

(2/2) Stage this hunk [y,n,q,a,d,K,g,/,e,?]? y

2. Comprovar l'estat MM:

git status -s
# → MM app.js

Primera columna M: hi ha una versió preparada (amb la funció nova, sense la correcció del comentari). Segona columna M: el disc en difereix.

Es pot verificar amb més precisió:

git diff --staged --stat
# → app.js | 9 +++++++++
git diff --stat
# → app.js | 2 +-

Nou línies preparades (la funció) i una línia pendent (el comentari). Exactament la separació buscada.

3 i 4. Les dues confirmacions:

git commit -m "Afegeix l'esborrat de tasques al llistat"
# → [main 4e7f2a9] Afegeix l'esborrat de tasques al llistat
# →  1 file changed, 9 insertions(+)

git add app.js
git commit -m "Corregeix una errada al comentari de capçalera"
# → [main 8a1d5c3] Corregeix una errada al comentari de capçalera
# →  1 file changed, 1 insertion(+), 1 deletion(-)

5. Verificació:

git log --oneline -2
8a1d5c3 (HEAD -> main) Corregeix una errada al comentari de capçalera
4e7f2a9 Afegeix l'esborrat de tasques al llistat
git show --stat 4e7f2a9 | tail -2
# →  app.js | 9 +++++++++
# →  1 file changed, 9 insertions(+)

git show --stat 8a1d5c3 | tail -2
# →  app.js | 2 +-
# →  1 file changed, 1 insertion(+), 1 deletion(-)

Dues confirmacions atòmiques sobre el mateix fitxer. Sense git add -p això només s'aconseguiria desfent un canvi a mà, confirmant i tornant-lo a escriure.

Solució a l'Exercici 3

1. Els tres problemes:

  1. .env està versionat. Conté credencials; ara són a l'historial i les veurà qualsevol amb accés al repositori.
  2. node_modules/ està versionat. Són dependències reinstal·lables: inflen el repositori, embruten els diffs i no aporten res.
  3. El missatge «arreglos diversos» no diu res. I, de fons, la confirmació no és atòmica: barreja lògica (app.js), estils (estils.css) i documentació (README.md) amb fitxers que no hi havien de ser.

2. Deixar de versionar .env i node_modules/:

git rm --cached .env
git rm -r --cached node_modules/
rm '.env'
rm 'node_modules/lib/a.js'

--cached és la clau: els fitxers continuen al disc, només deixen de rastrejar-se.

cat >> .gitignore <<'FI'
.env
node_modules/
FI

git add .gitignore
git status -s
A  .gitignore
D  .env
D  node_modules/lib/a.js

Comprovació que els fitxers continuen allà:

ls -la .env && ls node_modules/lib/
# → -rw------- 1 ana ana 87 jul 28 10:12 .env
# → a.js

3. Corregir el missatge sense crear una confirmació nova.

Com que els canvis del punt 2 estan preparats i la confirmació no s'ha compartit, es poden incorporar a la mateixa confirmació amb --amend, arreglant de passada el missatge:

git commit --amend -m "Afegeix l'esborrat de tasques i ajusta els estils del llistat"
[main 3b9e7d1] Afegeix l'esborrat de tasques i ajusta els estils del llistat
 4 files changed, 29 insertions(+), 6 deletions(-)
git show --stat HEAD
 .gitignore   | 2 ++
 app.js       | 24 ++++++++---
 estils.css   | 6 ++--
 README.md    | 2 +-
 4 files changed, 29 insertions(+), 6 deletions(-)

.env i node_modules/ han desaparegut de la confirmació, i el missatge ja descriu alguna cosa. El hash ha canviat (7f3c9a23b9e7d1) perquè, com sabem, --amend crea una confirmació nova.

Nota crítica: aquest --amend funciona netament perquè 7f3c9a2 era l'única confirmació que contenia .env. Si el fitxer portés vint confirmacions a l'historial, --amend només el trauria de l'última i continuaria estant a les dinou anteriors.

Sobre el missatge: continua tenint una «i», així que el veritablement correcte hauria estat separar la confirmació en dues (funcionalitat i estils). Amb git reset i un parell de git add -p es podria fer, però això ja entra al terreny de Mantenint un Historial Net.

4. Si la confirmació ja s'hagués compartit.

Canviarien dues coses:

  • Res de --amend. Reescriure una confirmació que altres ja tenen provoca divergència entre historials. La solució correcta seria una confirmació nova a sobre:
git rm --cached .env
git rm -r --cached node_modules/
git add .gitignore
git commit -m "Deixa de versionar credencials i dependències"

El missatge dolent del commit anterior es queda allà. És el preu d'haver publicat: l'historial compartit és immutable a la pràctica.

  • I, sobretot, cal rotar les credencials. Aquest és el punt que no es pot passar per alt: .env va estar a l'historial i, si aquell historial es va compartir, les claus es consideren compromeses. Treure'l del seguiment no les protegeix; qualsevol que hagi clonat el repositori les té al seu disc, i git show 7f3c9a2:.env les mostra. L'única resposta vàlida és invalidar aquelles credencials i generar-ne unes de noves. Netejar l'historial (amb git filter-repo o similar) és un pas complementari, mai un substitut. Ho veurem a Bones Pràctiques de Seguretat.

Conclusió

Aquesta lliçó t'ha donat el control fi sobre què entra a l'historial i com. Recapitulant:

  • git add sincronitza rutes amb l'àrea de preparació, i això inclou fitxers nous, modificats i esborrats.
  • -A, -u i . es diferencien en dos eixos: -u és l'únic que exclou fitxers nous, i . és l'únic limitat al directori actual. -A és «tot, a tot arreu».
  • git add -p prepara per fragments i és l'eina que permet separar dos canvis barrejats en un mateix fitxer. És l'hàbit que més millora un historial.
  • git restore --staged treu de l'àrea de preparació sense tocar el disc; git restore sobreescriu el disc i destrueix els teus canvis sense xarxa de seguretat. Els seus equivalents antics són git reset HEAD i git checkout --.
  • git mv i git rm fan l'operació del sistema de fitxers i la preparen d'un sol cop. git rm --cached deixa de rastrejar conservant el fitxer, i és la peça que falta quan un .gitignore «no funciona».
  • git commit admet missatge en línia o a l'editor, missatges multilínia i l'opció -a, que és còmoda i arriscada: confirma tot el que s'ha modificat i deixa fora el que és nou.
  • --amend corregeix l'última confirmació creant-ne una de nova i descartant l'anterior. És segur i molt útil mentre aquella confirmació no s'hagi compartit.
  • El commit atòmic —un canvi complet i només un— és l'objectiu de tot l'anterior. La regla mínima: si necessites «i» per descriure'l, separa'l.

L'Ana ja sap triar amb precisió què confirma. Però hi ha una peça que hem fet servir sense explicar: al mode -p, Git li mostrava els canvis en un format amb línies + i -, capçaleres @@ i fragments. Aquest format apareix pertot arreu a Git i saber llegir-lo és imprescindible.

A la lliçó següent, Inspeccionant Canvis amb git diff, aprendrem a veure exactament què ha canviat abans de confirmar: la diferència entre git diff, git diff --staged i git diff HEAD segons quines zones comparen, com llegir el format unified diff línia a línia, com comparar dues confirmacions concretes o un fitxer solt, i les opcions que fan la sortida llegible en els casos difícils.

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