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
git add: les formes d'indicar què preparar-Aenfront de-uenfront de.: la taula que resol el dubte- Preparació per fragments amb
git add -p - Treure de l'àrea de preparació:
git restore --staged - Descartar canvis del directori de treball:
git restore - Moure i reanomenar amb
git mv - Esborrar amb
git rm(i el cas especial--cached) git commit: totes les formes de confirmar--amend: corregir l'última confirmació- El commit atòmic
git add: les formes d'indicar què preparar
git add: les formes d'indicar què preparargit 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:
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:
En un repositori gran o desconegut, git add -n . costa un segon i evita disgustos.
-A enfront de -u enfront de .: la taula que resol el dubte
-A enfront de -u enfront de .: la taula que resol el dubteAquesta é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 . |
Sí | Sí | Sí | Només des del directori actual cap avall |
git add -A (--all) |
Sí | Sí | Sí | Tot el repositori, siguis on siguis |
git add -u (--update) |
No | Sí | Sí | Tot el repositori |
git add <ruta> |
Sí | Sí | Sí | 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-Afan 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 -sCas A — git add . des de l'arrel:
Tot hi entra: nous, modificats i esborrats.
Cas B — git add . des d'un subdirectori:
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:
-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:
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 -uper registrar tota la teva feina sobre fitxers coneguts, sense sorpreses.git add -Aquan de debò ho vols tot i tens un.gitignoreen 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.
- Preparació per fragments amb
git add -p
git add -pArriba 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 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 |
Sí, 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.
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,?]? yTercer 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,?]? nResultat:
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:
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:
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:
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.
-pfunciona també amb altres ordres:git restore -p,git stash -p,git checkout -p. Igit add -iobre un menú interactiu complet, del qual-pnomés és una opció. A la pràctica,-pcobreix el 95 % dels casos.
- Treure de l'àrea de preparació:
git restore --staged
git restore --stagedHas preparat una cosa que no volies. L'operació inversa és:
Exemple. L'Ana ho prepara tot per costum i s'adona que ESBORRANY.md no hi havia d'entrar:
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.
- Descartar canvis del directori de treball:
git restore
git restoreSense --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.
L'Ana experimenta amb un disseny a estils.css, no li agrada gens i vol tornar enrere:
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.jsAquesta ú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.
- Moure i reanomenar amb
git mv
git mvL'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:
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:
De fet, si haguessis fet servir mv tot sol, Git detectaria el reanomenament igualment en preparar tots dos canvis:
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.mdI 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à:
La solució és el pas intermedi:
O fer servir -f, que a les versions recents de Git resol aquest cas concret.
- Esborrar amb
git rm (i el cas especial --cached)
git rm (i el cas especial --cached)Per eliminar un fitxer del projecte:
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:
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:
# 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):
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 --cacheddeixa 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 |
git commit: totes les formes de confirmar
git commit: totes les formes de confirmarAmb l'àrea de preparació a punt, toca registrar. La forma que ja coneixes:
I aquestes són les variants que val la pena conèixer.
Sense -m: l'editor
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" # abreujatPrepara 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.logde depuració que vas deixar aapp.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:
--amend: corregir l'última confirmació
--amend: corregir l'última confirmacióL'Ana confirma i, un segon després, veu l'errada al missatge:
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 clicCorregit. 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-editEl 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:
- Crear un commit nou amb el mateix pare que l'anterior i el contingut/missatge corregits.
- Moure la branca actual a aquest commit nou.
- 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
--amendsobre 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).
- 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.logde 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:
- La primera línia, curta (uns 50 caràcters) i descriptiva per si sola.
- En imperatiu o infinitiu, descrivint què fa la confirmació: «Afegeix el comptador de tasques», no «Afegit el comptador» ni «canvis».
- 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, ésgit add -A. - Esperar que
-uinclogui 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 --stagedambgit 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 --cachedesborra el fitxer de l'historial. No l'esborra: continua a totes les confirmacions anteriors. Per a secrets, això no n'hi ha prou. - Fer
--amendsobre 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 -pet 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 --stagedt'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.txtSitua'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è:
git add .git add -Agit add -ugit 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:
- Fes servir
git add -pper preparar només el canvi A. - Demostra amb
git status -sque el fitxer és en estatMM. - Confirma el canvi A amb el missatge «Afegeix l'esborrat de tasques al llistat».
- Confirma el canvi B en una segona confirmació.
- Comprova amb
git log --onelineque hi ha dues confirmacions i ambgit show --statque 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:
.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:
- Quins són els tres problemes d'aquesta confirmació?
- Escriu la seqüència d'ordres que deixa de versionar
.envinode_modules/, els afegeix al.gitignorei conserva tots dos fitxers al disc. - Corregeix el missatge de la confirmació per un de descriptiu, sense crear una confirmació addicional.
- Què hauria canviat a la teva resposta si la confirmació ja s'hagués compartit? I quina mesura addicional caldria prendre pel fet que
.envhagi estat versionat?
Solucions
Solució a l'Exercici 1
Estat de partida (vist des de l'arrel):
1. git add . des de components/:
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:
Prepara tot el repositori, ignorant completament on ets. Les quatre entrades tenen la primera columna ocupada.
3. git add -u:
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 ..:
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 . |
Sí | Sí | No | No |
git add -A |
Sí | Sí | Sí | Sí |
git add -u |
Sí | No | Sí | No |
git add .. |
Sí | Sí | Sí | Sí |
Solució a l'Exercici 2
Els dos canvis a app.js. Capçalera (canvi B):
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:
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,?]? nResponem 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,?]? y2. Comprovar l'estat MM:
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ó:
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ó:
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:
.envestà versionat. Conté credencials; ara són a l'historial i les veurà qualsevol amb accés al repositori.node_modules/està versionat. Són dependències reinstal·lables: inflen el repositori, embruten els diffs i no aporten res.- 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/:
--cached és la clau: els fitxers continuen al disc, només deixen de rastrejar-se.
Comprovació que els fitxers continuen allà:
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:
[main 3b9e7d1] Afegeix l'esborrat de tasques i ajusta els estils del llistat 4 files changed, 29 insertions(+), 6 deletions(-)
.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 (7f3c9a2 → 3b9e7d1) perquè, com sabem, --amend crea una confirmació nova.
Nota crítica: aquest
--amendfunciona netament perquè7f3c9a2era l'única confirmació que contenia.env. Si el fitxer portés vint confirmacions a l'historial,--amendnomé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:
.envva 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, igit show 7f3c9a2:.envles mostra. L'única resposta vàlida és invalidar aquelles credencials i generar-ne unes de noves. Netejar l'historial (ambgit filter-repoo 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 addsincronitza rutes amb l'àrea de preparació, i això inclou fitxers nous, modificats i esborrats.-A,-ui.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 -pprepara 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 --stagedtreu de l'àrea de preparació sense tocar el disc;git restoresobreescriu el disc i destrueix els teus canvis sense xarxa de seguretat. Els seus equivalents antics sóngit reset HEADigit checkout --.git mvigit rmfan l'operació del sistema de fitxers i la preparen d'un sol cop.git rm --cacheddeixa de rastrejar conservant el fitxer, i és la peça que falta quan un.gitignore«no funciona».git commitadmet 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.--amendcorregeix 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
- 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ó
