A la lliçó 03-02 ens vam trobar per primera vegada amb aquest missatge:

error: Your local changes to the following files would be overwritten by checkout:
	app.js
Please commit your changes or stash them before you switch branches.

I vam dir que hi havia una tercera sortida, a més de confirmar o descartar: apartar els canvis. Vam prometre una lliçó sencera. És aquesta.

git stash és el calaix de sastre de Git: agafa tot el que tens a mitges al directori de treball i a l'índex, ho desa en un lloc segur, i et deixa la còpia de treball neta com si acabessis de clonar. Després, quan vulguis, ho recuperes.

L'escenari és sempre el mateix i li passa a tothom: estàs a mitja feina, amb codi que no compila i que no mereix un commit, i arriba una urgència. La Carla ho viurà en aquesta lliçó. I al final veurem el que gairebé ningú mira: que el stash no té res de màgic, que per dins són commits normals en una referència amagada, exactament els mateixos objectes del model de dades de la lliçó 01-04.

Contingut

  1. L'escenari: la Carla i la urgència
  2. git stash: què desa i què no
  3. Fitxers sense seguiment i ignorats: -u i -a
  4. La pila: list, show, apply, pop, drop, clear
  5. apply davant de pop
  6. Referir-se a una entrada concreta
  7. git stash push: missatges, rutes i mode interactiu
  8. --keep-index i --staged
  9. git stash branch: quan el stash ja no encaixa
  10. Com funciona per dins
  11. Riscos: stashes oblidats i falses còpies de seguretat

  1. L'escenari: la Carla i la urgència

La Carla és a funcionalitat/ordre-per-data, a mitja funció. Ha tocat app.js i estils.css, i ha creat un fitxer nou, utilitats.js, que encara no ha afegit al repositori.

git status --short
 M app.js
 M estils.css
?? utilitats.js

Arriba un missatge de l'Ana: el botó d'esborrar no funciona en producció i cal corregir-ho ja. La Carla necessita canviar a main, fer la correcció i tornar. Les seves opcions:

Opció Problema
Confirmar un wip Embruta l'historial (encara que sigui corregible amb 05-02), i el seu codi no compila
Descartar amb git restore Perd dues hores de feina
Copiar els fitxers a /tmp a mà Funciona, però és artesania i s'oblida
git stash Aparta-ho tot, deixa el directori net i el torna després
git stash push -u -m "Ordenació per data a mitges"
Saved working directory and index state On funcionalitat/ordre-per-data: Ordenació per data a mitges
git status
On branch funcionalitat/ordre-per-data
nothing to commit, working tree clean

Directori net. La Carla pot canviar de branca sense que Git protesti, corregir la fallada, publicar-la i tornar:

git switch main
git pull
# ... corregeix, confirma, publica ...
git switch funcionalitat/ordre-per-data
git stash pop
On branch funcionalitat/ordre-per-data
Changes not staged for commit:
	modified:   app.js
	modified:   estils.css

Untracked files:
	utilitats.js

Dropped refs/stash@{0} (a7f3c92e8b1d5c4a7f2e9b6d3c8a1f5e7b4d2c9a)

Tot torna a estar com estava. Això és git stash en el seu ús més bàsic. Ara, els detalls que marquen la diferència.

  1. git stash: què desa i què no

git stash sense arguments és un àlies de git stash push. Per defecte desa:

  • Els canvis en fitxers amb seguiment que estan modificats al directori de treball.
  • Els canvis en fitxers amb seguiment que estan preparats a l'índex.

I no desa:

  • Els fitxers sense seguiment (els que apareixen com a ?? a git status).
  • Els fitxers ignorats pel .gitignore.

Aquest és el primer parany, i és greu. Si la Carla hagués fet git stash sense -u:

git stash
git status --short
?? utilitats.js

app.js i estils.css estarien apartats, però utilitats.js continuaria allà, sense seguiment i sense apartar. I com que no l'aparta, tampoc no el torna: si la Carla l'esborrés per error pensant que és al stash, el perdria.

La lògica de Git té sentit —un fitxer sense seguiment mai no ha format part del repositori, així que Git és conservador i no el toca— però la conseqüència pràctica sorprèn sempre la primera vegada.

Recordant les tres zones de la lliçó 01-03, el repartiment és aquest:

Zona S'aparta amb git stash?
Directori de treball, fitxers amb seguiment modificats
Índex (àrea de preparació)
Fitxers sense seguiment Només amb -u
Fitxers ignorats Només amb -a
Repositori (commits) Mai: el stash no toca commits

I un matís que importa en recuperar: per defecte, el stash no conserva què estava preparat i què no. En fer pop o apply, tot torna com a "modificat sense preparar". Si necessites conservar aquesta distinció, existeix --index:

git stash pop --index

Amb --index, el que era a l'índex torna a l'índex. Pot fallar si l'estat actual no ho permet, cas en què Git ho aplica tot sense preparar i t'avisa.

  1. Fitxers sense seguiment i ignorats: -u i -a

Opció Nom llarg Què afegeix al que es desa per defecte
-u --include-untracked Els fitxers sense seguiment
-a --all Els sense seguiment i els ignorats
# L'habitual quan has creat fitxers nous
git stash -u

# Només si saps molt bé què fas
git stash -a

Sobre -a: els fitxers ignorats solen ser node_modules/, dist/, .env, fitxers de compilació... Apartar-los significa esborrar-los del directori de treball i ficar-los al stash. Amb node_modules/ això són desenes de milers de fitxers i una operació lentíssima. I amb .env, les teves credencials locals passen a estar dins d'objectes de Git, que és just el que no vols. Fes servir -a de manera excepcional i conscient.

En canvi, -u és tan sovint el que vols que molta gent el configura com a comportament per defecte mitjançant un àlies:

git config --global alias.desa 'stash push -u'

Els àlies es veuen a fons a la lliçó 06-04, però aquest val la pena des d'ara mateix.

  1. La pila: list, show, apply, pop, drop, clear

El stash no és un calaix únic: és una pila. Pots apartar diverses vegades, i cada entrada nova es col·loca a sobre.

git stash list
stash@{0}: On funcionalitat/ordre-per-data: Ordenació per data a mitges
stash@{1}: WIP on main: 7d3a8f4 Retorna el focus al camp de text després d'esborrar
stash@{2}: On funcionalitat/etiquetes-color: prova del selector
  • stash@{0} és sempre la més recent.
  • Els números es renumeren cada vegada que afegeixes o treus una entrada. El stash@{1} d'avui no és el mateix que el de demà. És un motiu excel·lent per posar missatges descriptius.
  • El text WIP on main: 7d3a8f4 ... és el missatge automàtic quan no n'hi poses cap: la branca i el commit sobre el qual es va desar.

Per veure el contingut d'una entrada:

# Resum de fitxers
git stash show stash@{0}
 app.js     | 12 ++++++++----
 estils.css |  5 +++++
 2 files changed, 13 insertions(+), 4 deletions(-)
# El diff complet
git stash show -p stash@{0}
diff --git a/app.js b/app.js
index 8c3d5a1..2e7b9f4 100644
--- a/app.js
+++ b/app.js
@@ -22,7 +22,11 @@ function pintaLlista() {
   const llista = document.getElementById('llista-tasques');
   llista.innerHTML = '';
-  tasques.forEach(function (t) {
+  const ordenades = tasques.slice().sort(function (a, b) {
+    return b.creada - a.creada;
+  });
+  ordenades.forEach(function (t) {
     llista.appendChild(creaElementTasca(t));
   });

Un detall: per defecte, git stash show no inclou els fitxers sense seguiment que haguessis desat amb -u. Per veure'ls:

git stash show -p -u stash@{0}

El repertori complet d'ordres:

Ordre Què fa
git stash / git stash push Aparta els canvis i neteja el directori
git stash list Llista la pila
git stash show [-p] [<entrada>] Ensenya què hi ha en una entrada
git stash apply [<entrada>] Aplica els canvis conservant l'entrada
git stash pop [<entrada>] Aplica els canvis i elimina l'entrada
git stash drop [<entrada>] Elimina l'entrada sense aplicar-la
git stash clear Buida la pila sencera. Sense confirmació
git stash branch <branca> [<entrada>] Crea una branca a partir de l'entrada i l'aplica
git stash create Crea l'objecte de stash sense tocar la pila ni el directori
git stash store <sha> Desa a la pila un objecte creat amb create

git stash clear mereix un avís en negreta: esborra tota la pila de cop i no pregunta. Recuperar-ne alguna cosa després és possible però empipador (cal remenar objectes orfes amb git fsck, tècnica de la lliçó 09-04). Tracta'l com un rm -rf.

  1. apply davant de pop

Totes dues apliquen els canvis desats sobre el teu directori de treball. La diferència és què passa amb l'entrada després:

git stash apply git stash pop
Aplica els canvis
Elimina l'entrada de la pila No Sí, si l'aplicació va tenir èxit
Es pot aplicar en diverses branques No (desapareix després del primer)
Si hi ha conflicte L'entrada es conserva L'entrada es conserva
Risc de duplicar canvis Sí, si oblides fer drop No
Risc de perdre el desament No Baix, però existeix

En què es tradueix això a la pràctica:

Fes servir pop en el cas normal: vas apartar, vas fer una altra cosa, tornes. És una sola ordre i deixa la pila neta.

Fes servir apply quan:

  • Vols aplicar els mateixos canvis en dues branques diferents.
  • No estàs segur que encaixin i prefereixes conservar la còpia fins a comprovar-ho.
  • La branca actual ha canviat molt i sospites que hi haurà conflicte.

El detall que salva vides: si pop provoca un conflicte, l'entrada NO s'elimina. Git aplica el que pot, deixa els marcadors de conflicte i conserva el stash per si de cas. És un comportament deliberat i molt assenyat. Però té una conseqüència molesta: després de resoldre el conflicte, l'entrada continua a la pila i l'has d'esborrar tu:

git stash pop
Auto-merging app.js
CONFLICT (content): Merge conflict in app.js
The stash entry is kept in case you need it again.
# Resoldre el conflicte (mecànica de la lliçó 03-05)
# ... editar app.js, treure els marcadors ...
git add app.js

# I ara sí, eliminar l'entrada a mà
git stash drop
Dropped refs/stash@{0} (a7f3c92e8b1d5c4a7f2e9b6d3c8a1f5e7b4d2c9a)

Apunta't aquest hash que imprimeix drop. És la referència a l'objecte del stash, i amb ell es pot recuperar una entrada esborrada per error (lliçó 09-04). És el mateix consell que vam donar en esborrar branques a 03-06, i pel mateix motiu.

Un avís sobre els conflictes de stash: a diferència d'una fusió o d'un rebase, aquí no hi ha --abort. Si pop conflictua, ets enmig de la resolució i l'única manera de tornar enrere és descartar els canvis del directori de treball (git checkout -- . o git reset --hard, amb la cura que això exigeix) sabent que el stash continua estalvi a la pila.

  1. Referir-se a una entrada concreta

Gairebé totes les ordres accepten una entrada. Si l'omets, es fa servir stash@{0}.

git stash apply stash@{2}
git stash show -p stash@{1}
git stash drop stash@{3}

La notació stash@{N} és la mateixa sintaxi de reflog que vas veure a 02-06 aplicada a la referència refs/stash. I admet formes per temps:

git stash show stash@{2.hours.ago}
git stash apply stash@{yesterday}

En alguns intèrprets d'ordres les claus necessiten cometes o escapament:

git stash apply "stash@{2}"     # segur en bash, zsh i PowerShell

Des de Git 2.11 també s'admet el número a seques, que és més còmode:

git stash apply 2      # equival a stash@{2}

I recorda: els números es renumeren. Si tens tres entrades i esborres la del mig, la que era stash@{2} passa a ser stash@{1}. No desis mai un número apuntat en un paper; desa el missatge.

  1. git stash push: missatges, rutes i mode interactiu

git stash push és la forma moderna i completa. git stash save "missatge" és la forma antiga, encara funciona, però està desaconsellada i no admet rutes.

Missatge descriptiu:

git stash push -m "Ordenació per data: falta el cas de tasques sense data"

És la millora de qualitat de vida més barata que existeix amb aquesta eina. Un git stash list amb tres entrades anomenades WIP on main és inútil; amb tres missatges descriptius, és un pla de treball.

Desar només unes rutes:

git stash push -m "Només els estils" estils.css
git stash push -m "Tot el del directori d'informes" informes/

Els canvis d'aquests fitxers s'aparten; la resta es queda al directori de treball. És molt útil quan has barrejat dues tasques a la mateixa sessió i en vols separar una per treballar amb l'altra tranquil.

Mode interactiu:

git stash push -p

Fragment a fragment, igual que git add -p (lliçó 02-04), Git et pregunta per cada bloc de canvis si el vols apartar:

@@ -22,7 +22,11 @@ function pintaLlista() {
   const llista = document.getElementById('llista-tasques');
   llista.innerHTML = '';
-  tasques.forEach(function (t) {
+  const ordenades = tasques.slice().sort(function (a, b) {
...
(1/3) Stash this hunk [y,n,q,a,d,j,J,g,/,e,?]?

Les tecles són les mateixes de sempre: y sí, n no, q sortir, s dividir el fragment, e editar-lo a mà.

Altres opcions de push:

Opció Què fa
-m <missatge> Missatge descriptiu
-u / -a Incloure sense seguiment / també ignorats
-p Triar fragment a fragment
-k / --keep-index Deixa intacte el que estava preparat (apartat 8)
-S / --staged Aparta només el que està preparat (apartat 8)
-q Silenciós
--pathspec-from-file=<f> Llegeix la llista de rutes d'un fitxer

  1. --keep-index i --staged

Dues opcions semblants de nom i molt diferents d'efecte. La taula ho aclareix tot; suposem que tens app.js preparat i estils.css modificat sense preparar:

Opció Què es desa al stash Què queda al directori de treball
(cap) app.js + estils.css Res: directori net
--keep-index app.js + estils.css app.js preparat (la resta, net)
--staged Només app.js estils.css modificat

--keep-index desa tot però restaura al directori el que era a l'índex. El seu ús clàssic és provar un commit abans de fer-lo:

# Tinc preparat exactament el que vull confirmar
git add app.js
git stash push --keep-index -m "El que no va en aquest commit"

# El directori conté NOMÉS el que confirmaré: puc provar-ho de debò
npm test

# Si passa, confirmo amb la seguretat d'haver provat això i només això
git commit -m "Afegeix l'ordenació per data de creació"

# I recupero la resta
git stash pop

És la manera correcta d'assegurar-se que un commit és autocontingut i no depèn de canvis que s'han quedat fora. Molta gent descobreix així que el seu commit "llest" no compilava per si sol.

Nota: amb --keep-index, el stash conté també el que estava preparat. En fer pop després del commit, aquests canvis ja estan confirmats i poden conflictuar. A la pràctica se sol combinar amb --include-untracked i s'accepta que el pop posterior de vegades requereix un --skip mental: revisa amb git stash show -p abans de recuperar.

--staged (des de Git 2.35) és més simple i més nova: aparta només el que és a l'índex i deixa la resta. És el contrari del cas anterior, i serveix per a "això que ja tenia preparat me l'enduc a una altra branca":

git add utilitats.js
git stash push --staged -m "La utilitat va en una altra branca"
git switch funcionalitat/utilitats
git stash pop

  1. git stash branch: quan el stash ja no encaixa

Problema clàssic: vas desar un stash fa tres dies, la branca ha avançat molt des de llavors i ara git stash pop dona un conflicte rere l'altre.

La causa és que el stash es va desar sobre un commit concret i ara s'està aplicant sobre un altre de completament diferent. La solució és aplicar-lo on encaixava:

git stash branch funcionalitat/rescat stash@{1}

Aquesta ordre fa quatre coses alhora:

  1. Crea una branca nova al commit sobre el qual es va desar el stash.
  2. Es canvia a ella.
  3. Aplica el stash (que encaixa perfectament, perquè el context és l'original).
  4. Elimina l'entrada de la pila, ja que s'ha aplicat amb èxit.
Switched to a new branch 'funcionalitat/rescat'
On branch funcionalitat/rescat
Changes not staged for commit:
	modified:   app.js

Dropped refs/stash@{1} (5c8e2d1f9a3b7e4c6d1f8a2b5e9c3d7f4a1b8e6c)

Des d'aquí pots confirmar tranquil·lament i després integrar la branca amb merge o rebase, resolent els conflictes una sola vegada i amb context, en lloc de barallar-te amb un pop a cegues.

És la millor sortida quan un stash "no entra". I també la millor manera de convertir un stash que s'ha tornat important en feina de debò.

  1. Com funciona per dins

Aquí és on el stash deixa de semblar màgia. Reprenem el model de dades de la lliçó 01-04.

Quan fas git stash, Git crea commits normals:

  • Un commit amb l'estat de l'índex.
  • Opcionalment, un commit amb els fitxers sense seguiment (si has fet servir -u).
  • Un commit de fusió el primer pare del qual és el HEAD actual, el segon el commit de l'índex, i el tercer (si existeix) el dels sense seguiment. Aquest és el commit del stash.

I desa la referència a aquest commit a refs/stash. Comprovem-ho:

git stash push -u -m "Prova d'anatomia"
cat .git/refs/stash
a7f3c92e8b1d5c4a7f2e9b6d3c8a1f5e7b4d2c9a
git cat-file -t a7f3c92
commit

Un commit corrent. Vegem-ne l'interior amb les mateixes eines de 01-04:

git cat-file -p a7f3c92
tree 3f8b1c7e2d9a5b4f6c1e8a3d7b2f5c9e4a1d6b8f
parent 7d3a8f4c9b1e5d2a8f7c3b6e9d4a1c8f5b2e7d3a
parent 8e2c5f1a9d3b7e4c1f6a8d2b5e9c3f7a4d1b8e6c
parent 1c9e4b7f2a8d5c3e6b1f9a4d7c2e5b8f3a6d1c9e
author Carla Vidal <[email protected]> 1753959200 +0200
committer Carla Vidal <[email protected]> 1753959200 +0200

On funcionalitat/ordre-per-data: Prova d'anatomia

Tres pares:

Pare Què conté
1r (7d3a8f4) El HEAD de quan vas desar: la base
2n (8e2c5f1) L'estat de l'índex
3r (1c9e4b7) Els fitxers sense seguiment (només amb -u)

I el tree del commit mateix és l'estat del directori de treball. Amb aquests quatre arbres, Git pot reconstruir exactament el que tenies i aplicar-ho com una fusió a tres bandes. D'aquí que els conflictes de stash pop siguin conflictes de fusió normals i corrents.

La pila, per la seva banda, és el reflog de refs/stash:

git reflog stash
a7f3c92 stash@{0}: On funcionalitat/ordre-per-data: Prova d'anatomia
5c8e2d1 stash@{1}: WIP on main: 7d3a8f4 Retorna el focus al camp de text
9b4f7e3 stash@{2}: On funcionalitat/etiquetes-color: prova del selector

Això explica de cop tres coses que abans semblaven arbitràries:

  • Per què la sintaxi és stash@{N}: és exactament la sintaxi del reflog.
  • Per què els números es renumeren: són posicions en un registre, no identificadors.
  • Per què es poden recuperar stashes esborrats: l'objecte continua a la base de dades fins que passi el recol·lector d'escombraries.

I una conseqüència pràctica molt útil: com que el stash és un commit, pots fer servir qualsevol ordre de commits amb ell.

git show stash@{0}                          # el commit del stash
git diff stash@{0}^ stash@{0}               # el seu diff contra la base
git diff main stash@{0} -- app.js           # comparar amb una altra branca
git log --oneline stash@{0}^..stash@{0}     # rangs, encara que aquí aporti poc

  1. Riscos: stashes oblidats i falses còpies de seguretat

Risc 1: el stash oblidat. És, de bon tros, el problema més freqüent. Apartes alguna cosa, la urgència s'allarga, passen tres setmanes i la feina continua allà. Quan la trobes, ja no encaixa amb res.

El stash no apareix a git status, no apareix a git log, no surt en cap interfície gràfica per defecte i no s'envia mai al servidor. És invisible.

Mesures:

# Mira la pila de tant en tant
git stash list

# Millor: afegeix-ho al missatge del teu prompt o a un àlies que facis servir a diari
git config --global alias.st '!git status && echo "--- stash ---" && git stash list'

I la mesura real: el stash és per a minuts o hores, no per a dies. Si la feina ha d'esperar més d'una jornada, és una branca. git stash branch està just per a això.

Risc 2: creure que és una còpia de seguretat. No ho és, per tres motius:

  1. És local. No s'envia mai amb git push. El refspec per defecte només cobreix refs/heads/* (lliçó 04-05), i refs/stash en queda fora. Si se't mor el disc, el stash mor amb ell.
  2. No es clona. git clone no porta els stashes de ningú. Ni tan sols un git clone --mirror els replica de manera útil.
  3. És fràgil. git stash clear ho esborra tot sense preguntar. I els commits del stash, en no estar referenciats per cap branca, són candidats al recol·lector d'escombraries quan s'esborren de la pila.

Una còpia de seguretat de debò és un commit en una branca publicada. Si la feina importa, confirma-la —encara que sigui amb un missatge provisional que després arreglaràs amb rebase -i (lliçó 05-02)— i publica-la.

Risc 3: aplicar el stash a la branca equivocada. El stash no està lligat a cap branca: pots fer pop a qualsevol. De vegades és exactament el que vols (moure't de branca amb la feina a coll); de vegades és un accident que omple main de canvis que no li tocaven. git stash list et diu en quina branca es va desar cada entrada; llegeix-ho abans de recuperar.

Risc 4: els fitxers sense seguiment. Ja ho hem vist: sense -u no es desen. L'error concret i perillós és fer git stash seguit de git clean -fd per "deixar-ho tot net": el clean esborra els fitxers sense seguiment que el stash no va desar, i aquests sí que es perden de debò.

Errors Habituals i Consells

Error 1: git stash sense -u quan hi ha fitxers nous. Es queden fora. És la sorpresa número u amb aquesta eina. Mira sempre git status --short abans d'apartar.

Error 2: git stash clear per "endreçar". Esborra tota la pila sense preguntar i sense confirmació. Fes servir git stash drop <entrada> d'una en una, després de mirar cadascuna amb show -p.

Error 3: pensar que pop sempre elimina l'entrada. Si hi ha conflicte, la conserva expressament. Després de resoldre, has de fer git stash drop tu.

Error 4: acumular stashes sense missatge. Cinc entrades anomenades WIP on main són cinc incògnites. git stash push -m "..." sempre.

Error 5: fer servir el stash com a sistema de branques. Si trigaràs més d'una jornada, fes una branca. El stash no sobreviu a la memòria de ningú.

Error 6: fiar-se dels números. stash@{2} canvia de significat tan bon punt afegeixes o treus entrades. Identifica per missatge, no per número.

Error 7: combinar git stash amb git clean -fd sense pensar. El primer no desa el que està sense seguiment; el segon ho esborra. La combinació destrueix fitxers nous.

Consell 1: àlies amb -u incorporat. git config --global alias.desa 'stash push -u -m' i a partir d'aquí git desa "el que sigui".

Consell 2: --keep-index abans de confirmar. Un git stash push --keep-index && npm test et diu si el teu commit és realment autocontingut. Es triga un minut i evita commits trencats.

Consell 3: git stash show -p abans de pop. Especialment si l'entrada té més d'un dia. Saber què arribarà evita ensurts.

Consell 4: git stash branch tan bon punt hi hagi conflicte. No et barallis amb un pop que no encaixa: crea la branca al punt original, aplica netament i fusiona amb calma.

Consell 5: revisa la pila els divendres. Un git stash list setmanal és suficient perquè no se't quedi res tres mesos.

Exercicis

Exercici 1: el parany dels fitxers sense seguiment

En un repositori de proves:

  1. Modifica un fitxer amb seguiment i crea'n un de nou sense afegir.
  2. Fes git stash sense -u i comprova amb git status què ha passat amb cadascun.
  3. Recupera, i repeteix l'operació amb -u.
  4. Demostra amb git stash show -p -u que en el segon cas el fitxer nou sí que és a dins.

Exercici 2: --keep-index per validar un commit

Prepara un escenari on tinguis dos canvis: un de preparat (que compila per si sol) i un altre sense preparar (que trenca el fitxer). Fent servir --keep-index:

  1. Aparta el que no va al commit.
  2. Comprova que el fitxer és vàlid (node --check o similar).
  3. Confirma.
  4. Recupera la resta i observa què passa.

Exercici 3: anatomia d'un stash

Crea un stash amb -u i demostra amb ordres de baix nivell:

  1. Que refs/stash apunta a un commit.
  2. Que aquest commit té tres pares.
  3. Què conté cadascun dels tres.
  4. Que git reflog stash i git stash list mostren la mateixa informació.

Solucions

Solució 1:

mkdir /tmp/practica-stash && cd /tmp/practica-stash
git init -b main
echo "original" > seguit.txt && git add . && git commit -m "Base"

echo "modificat" > seguit.txt
echo "soc nou" > sense-seguir.txt
git status --short
 M seguit.txt
?? sense-seguir.txt
# 2. Sense -u
git stash
git status --short
ls
?? sense-seguir.txt
seguit.txt  sense-seguir.txt

seguit.txt ha tornat a la seva versió original; sense-seguir.txt continua exactament on era. No s'ha desat.

# 3. Recuperar i repetir amb -u
git stash pop
git stash -u
git status --short
ls
(sense sortida)
seguit.txt

Ara sí: el directori està net de debò i el fitxer nou ha desaparegut (és al stash).

# 4. Comprovar que és a dins
git stash show -p -u stash@{0}
diff --git a/seguit.txt b/seguit.txt
--- a/seguit.txt
+++ b/seguit.txt
@@ -1 +1 @@
-original
+modificat
diff --git a/sense-seguir.txt b/sense-seguir.txt
new file mode 100644
--- /dev/null
+++ b/sense-seguir.txt
@@ -0,0 +1 @@
+soc nou
git stash pop     # deixar-ho tot com estava

Solució 2:

mkdir /tmp/practica-keepindex && cd /tmp/practica-keepindex
git init -b main
echo "const a = 1;" > app.js && git add . && git commit -m "Base"

# Canvi bo, preparat
echo "const b = 2;" >> app.js
git add app.js

# Canvi dolent, sense preparar
echo "const c = ;" >> app.js

git status --short
MM app.js

(La doble M significa: modificat a l'índex i modificat a més al directori de treball.)

# 1. Apartar el que no va al commit
git stash push --keep-index -m "El canvi a mitges"
cat app.js
const a = 1;
const b = 2;

El fitxer conté només el que estava preparat.

# 2. Validar
node --check app.js
(sense sortida: correcte)
# 3. Confirmar
git commit -m "Afegeix la constant b"
# 4. Recuperar
git stash pop
cat app.js
const a = 1;
const b = 2;
const c = ;

El pop ha tornat l'estat complet que hi havia abans. Com que el commit ja conté la línia de b, en aquest cas simple no hi ha conflicte; amb canvis que se solapen a les mateixes línies sí que n'hi hauria, i és la raó per la qual convé revisar amb git stash show -p abans.

Solució 3:

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

echo "canvi al directori" > f.txt
echo "preparat" > g.txt && git add g.txt
echo "sense seguir" > h.txt

git stash push -u -m "Anatomia"
# 1. refs/stash apunta a un commit
git rev-parse refs/stash
git cat-file -t refs/stash
a7f3c92e8b1d5c4a7f2e9b6d3c8a1f5e7b4d2c9a
commit
# 2. Tres pares
git cat-file -p refs/stash | grep '^parent'
parent 4b8e1c7f2a9d5e3b6c1f8a4d7b2e5c9f3a6d1b8e
parent 9d2f6a3c8b1e5f7d4a2c9e6b3f8d1a5c7e4b2f9d
parent 6c1a8f4d3e7b2c5a9f1d6b8e3c7a4f2d5b9e1c8a
# També amb rev-parse, un a un
git rev-parse refs/stash^1 refs/stash^2 refs/stash^3
# 3. Què conté cadascun
git show --stat refs/stash^1 | head -3     # la base: el commit HEAD original
git ls-tree refs/stash^2                   # l'índex: inclou g.txt preparat
git ls-tree refs/stash^3                   # els sense seguiment: h.txt
commit 4b8e1c7...
    Base
100644 blob 8c3d5a1...	f.txt
100644 blob 2e7b9f4...	g.txt
100644 blob 5f9c2a8...	h.txt
# 4. La pila és el reflog de refs/stash
git stash list
git reflog stash
stash@{0}: On main: Anatomia
a7f3c92 stash@{0}: On main: Anatomia

La mateixa informació, presentada de dues maneres. git stash list és, literalment, una vista del reflog de refs/stash.

Conclusió

git stash és una eina petita amb més matisos dels que aparenta. L'essencial:

  • Aparta els canvis sense confirmar i deixa el directori net, perquè puguis canviar de branca, atendre una urgència o provar alguna cosa, i recuperar-los després.
  • Per defecte NO desa els fitxers sense seguiment ni els ignorats: per a això hi ha -u (sense seguiment, l'opció que voldràs gairebé sempre) i -a (també els ignorats, que només es fa servir molt conscientment).
  • És una pila: git stash list l'enumera, stash@{0} és la més recent i els números es renumeren, així que cal identificar per missatge. git stash push -m "..." és obligatori a la pràctica.
  • pop aplica i elimina; apply aplica i conserva. Si hi ha conflicte, pop conserva l'entrada i has de fer drop a mà després de resoldre.
  • --keep-index deixa al directori només el que anaves a confirmar (per provar-ho de debò); --staged aparta només el que està preparat. I push -p permet triar fragment a fragment, i push <ruta> apartar només uns fitxers.
  • git stash branch <branca> crea una branca al commit original del stash i l'aplica allà: la millor sortida quan un stash ja no encaixa.
  • Per dins no hi ha màgia: són commits normals a refs/stash, amb el HEAD original, l'índex i els sense seguiment com a pares, i la pila és el reflog d'aquesta referència. D'aquí venen la sintaxi stash@{N} i la renumeració.
  • No és una còpia de seguretat: és local, no s'envia, no es clona i clear l'esborra sense preguntar. Per a minuts i hores, no per a dies.

El que ve

Fins aquí, tot el mòdul ha anat de modificar l'historial: reaplicar-lo, reorganitzar-lo, copiar-lo, apartar-lo. Ara farem el contrari: fixar-hi un punt per sempre.

gestor-tasques està a punt de tenir la seva primera versió estable. L'equip necessita poder dir "això és la 1.0.0" i que d'aquí a dos anys, quan arribi una incidència d'un client que continua amb aquella versió, algú pugui situar-se exactament en aquell codi sense dependre de recordar un hash de quaranta caràcters.

Per a això hi ha les etiquetes, el quart tipus d'objecte de la base de dades de Git que vam conèixer a la lliçó 01-04 i del qual amb prou feines hem parlat. Ho veiem a la lliçó 05-05: Etiquetant Confirmacions.

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