Salta el contingut
 

Reserva de canvis (stash)

Joan Puigcerver Ibáñez

Llicència: CC BY-NC-SA 4.0

(Reconeixement - NoComercial - CompartirIgual) 🅭

Reserva de canvis (git stash)

La reserva de canvis (stash) és un magatzem de Git que permet guardar temporalment els canvis que encara no es volen confirmar (commit).

Aquesta funció és útil quan cal fer alguna acció de Git que, d'altra manera, faria perdre els canvis del Directori de treball. Per exemple:

  • Canviar de branca: si la branca de destí modifica els mateixos fitxers.
  • Incorporar canvis d'una altra branca: amb merge, rebase o pull.

La reserva de canvis permet guardar aquests canvis temporalment i recuperar-los més avant, quan siga necessari.

Preparació del repositori

S'inicialitza un repositori amb canvis en el fitxer README.md i una branca addicional, altres_canvis, on s'han fet canvis en el mateix fitxer.

setup_stash.sh
#!/bin/bash

# Elimina el repositori si existeix
if [ -d ~/git_stash ]; then
    rm -rf ~/git_stash
fi

mkdir -p ~/git_stash
cd ~/git_stash
git init
git branch -m main
echo "# Reserva de canvis" > README.md
git add README.md
git commit -m "Commit inicial"
git checkout -b altres_canvis
echo "Altres canvis" >> README.md
git commit -a -m "Altres canvis"
git checkout main
git lga
jpuigcerver@fp:~ $ mkdir -p ~/git_stash
jpuigcerver@fp:~ $ cd ~/git_stash
jpuigcerver@fp:~/git_stash $ git init
Initialized empty Git repository in ~/git_stash/.git/
jpuigcerver@fp:~/git_stash (main) $ git branch -m main
jpuigcerver@fp:~/git_stash (main) $ echo "# Reserva de canvis" > README.md
jpuigcerver@fp:~/git_stash (main) $ git add README.md
jpuigcerver@fp:~/git_stash (main) $ git commit -m "Commit inicial"
[main (root-commit) 5cc4104] Commit inicial
 1 file changed, 1 insertion(+)
 create mode 100644 README.md
jpuigcerver@fp:~/git_stash (main) $ git checkout -b altres_canvis
Switched to a new branch 'altres_canvis'
jpuigcerver@fp:~/git_stash (altres_canvis) $ echo "Altres canvis" >> README.md
jpuigcerver@fp:~/git_stash (altres_canvis) $ git commit -a -m "Altres canvis"
[altres_canvis 602f4b0] Altres canvis
 1 file changed, 1 insertion(+)
jpuigcerver@fp:~/git_stash (altres_canvis) $ git checkout main
Switched to branch 'main'
jpuigcerver@fp:~/git_stash (main) $ git lga
* 602f4b0 - (0 seconds ago) Altres canvis - Joan Puigcerver (altres_canvis)
* 5cc4104 - (0 seconds ago) Commit inicial - Joan Puigcerver (HEAD -> main)

Crea el nou repositori en una carpeta independent per evitar problemes amb els exemples i exercicis anteriors.

Per què és útil git stash?

Suposa que treballes en la branca principal main i has fet canvis en el fitxer README.md. Aquests canvis es troben en el Directori de treball i encara no s'han confirmat (commit).

En aquest moment, decideixes canviar a una altra branca. Si aquesta operació modifica la mateixa part dels fitxers on has fet canvis, Git ho impedeix perquè no els perdes.

jpuigcerver@fp:~/git_stash (main) $ echo "Canvi A" >> README.md
jpuigcerver@fp:~/git_stash (main) $ git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")
jpuigcerver@fp:~/git_stash (main) $ git checkout altres_canvis
error: Your local changes to the following files would be overwritten by checkout:
    README.md
Please commit your changes or stash them before you switch branches.
Aborting

El missatge d'error recomana dues opcions:

  • Confirmar els canvis (commit): permet canviar de branca sense problemes, però potser encara no vols confirmar-los.
  • Guardar els canvis de manera temporal amb l'ordre git stash.

Crear una reserva de canvis

L'ordre git stash guarda els canvis del Directori de treball en una reserva i deixa el directori net:

git stash [-m <missatge>]
  • [-m <missatge>]: (opcional) missatge que identifica els canvis guardats.

Els canvis s'emmagatzemen temporalment en una pila, de manera que:

  • Els canvis nous es guarden en la primera posició de la pila, amb l'índex 0: stash@{0}. Així, els canvis més recents es troben en la part superior de la pila i són més fàcils d'accedir, ja que la majoria d'ordres stash treballen per defecte amb stash@{0}.

    Reserva de canvis amb una única entrada Reserva de canvis amb una única entrada

    Figura 1. Reserva de canvis amb una única entrada.

  • L'índex dels canvis que ja hi havia en la pila s'incrementa en 1.

    Reserva de canvis amb entrades existents Reserva de canvis amb entrades existents

    Figura 2. Reserva de canvis amb entrades existents.

Exemple: Crear una reserva de canvis

Es guarden els canvis amb git stash i es canvia de branca.

jpuigcerver@fp:~/git_stash (main) $ git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")
jpuigcerver@fp:~/git_stash (main) $ git diff
diff --git a/README.md b/README.md
index d6704e3..e23e265 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,2 @@
 # Reserva de canvis
+Canvi A
jpuigcerver@fp:~/git_stash (main) $ git stash -m "Canvi A"
Saved working directory and index state On main: Canvi A
jpuigcerver@fp:~/git_stash (main) $ git status
On branch main
nothing to commit, working tree clean
jpuigcerver@fp:~/git_stash (main) $ git checkout altres_canvis
Switched to branch 'altres_canvis'
jpuigcerver@fp:~/git_stash (altres_canvis) $ git checkout main
Switched to branch 'main'

S'observa que els canvis ja no es troben en el Directori de treball i que ara es pot canviar de branca sense problemes.

Exemple: Crear diverses reserves de canvis

Es creen dues reserves més, amb els canvis B i C.

  1. Canvi B:

    jpuigcerver@fp:~/git_stash (main) $ echo "Canvi B" >> README.md
    jpuigcerver@fp:~/git_stash (main) $ git stash -m "Canvi B"
    Saved working directory and index state On main: Canvi B
    jpuigcerver@fp:~/git_stash (main) $ git status
    On branch main
    nothing to commit, working tree clean
    jpuigcerver@fp:~/git_stash (main) $ git stash list
    stash@{0}: On main: Canvi B
    stash@{1}: On main: Canvi A
    
  2. Canvi C:

    jpuigcerver@fp:~/git_stash (main) $ echo "Canvi C" >> README.md
    jpuigcerver@fp:~/git_stash (main) $ git stash -m "Canvi C"
    Saved working directory and index state On main: Canvi C
    jpuigcerver@fp:~/git_stash (main) $ git status
    On branch main
    nothing to commit, working tree clean
    jpuigcerver@fp:~/git_stash (main) $ git stash list
    stash@{0}: On main: Canvi C
    stash@{1}: On main: Canvi B
    stash@{2}: On main: Canvi A
    

S'observa que l'índex dels canvis anteriors s'incrementa amb cada reserva nova.

Mostrar les reserves de canvis

Per a mostrar les reserves existents, s'utilitza l'ordre:

git stash list

Aquesta ordre mostra una llista amb les reserves existents, identificades per l'índex i pel missatge associat.

Exemple: Mostrar les reserves de canvis

Es mostren les reserves creades en els exemples anteriors.

jpuigcerver@fp:~/git_stash (main) $ git stash list
stash@{0}: On main: Canvi C
stash@{1}: On main: Canvi B
stash@{2}: On main: Canvi A

S'observa que la reserva més recent, Canvi C, ocupa la posició stash@{0}.

Mostrar els canvis d'una reserva

Els canvis guardats en una reserva es poden consultar amb l'acció show, que mostra els fitxers modificats:

git stash show [-p] [<index>]
  • [-p]: (opcional) mostra els canvis (diff) en lloc de només els fitxers modificats.
  • [<index>]: (opcional) índex de la reserva que es vol consultar. Per defecte, stash@{0}.
Exemple: Mostrar els canvis d'una reserva

Es mostren els canvis de cadascuna de les reserves.

jpuigcerver@fp:~/git_stash (main) $ git stash list
stash@{0}: On main: Canvi C
stash@{1}: On main: Canvi B
stash@{2}: On main: Canvi A
jpuigcerver@fp:~/git_stash (main) $ git stash show -p
diff --git a/README.md b/README.md
index d6704e3..208685c 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,2 @@
 # Reserva de canvis
+Canvi C
jpuigcerver@fp:~/git_stash (main) $ git stash show -p 1
diff --git a/README.md b/README.md
index d6704e3..5a36536 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,2 @@
 # Reserva de canvis
+Canvi B
jpuigcerver@fp:~/git_stash (main) $ git stash show -p 2
diff --git a/README.md b/README.md
index d6704e3..e23e265 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,2 @@
 # Reserva de canvis
+Canvi A

S'observa que cada reserva conté una línia diferent afegida al fitxer README.md.

Recuperar els canvis

Els canvis reservats es poden recuperar amb l'acció apply, que els aplica en el Directori de treball:

git stash apply [<index>]
  • [<index>]: (opcional) índex de la reserva que es vol aplicar. Per defecte, stash@{0}.

Recuperar canvis amb stash apply Recuperar canvis amb stash apply

Figura 3. Recuperar canvis amb stash apply.

Exemple: Recuperar els canvis amb apply

S'apliquen els canvis de la reserva més recent.

jpuigcerver@fp:~/git_stash (main) $ git status
On branch main
nothing to commit, working tree clean
jpuigcerver@fp:~/git_stash (main) $ git stash apply
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")
jpuigcerver@fp:~/git_stash (main) $ git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")
jpuigcerver@fp:~/git_stash (main) $ git diff
diff --git a/README.md b/README.md
index d6704e3..208685c 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,2 @@
 # Reserva de canvis
+Canvi C
jpuigcerver@fp:~/git_stash (main) $ git stash list
stash@{0}: On main: Canvi C
stash@{1}: On main: Canvi B
stash@{2}: On main: Canvi A

L'acció apply manté la reserva en la pila. Si, a més, es vol esborrar la reserva després d'aplicar-la, s'utilitza l'acció pop:

git stash pop [<index>]
  • [<index>]: (opcional) índex de la reserva que es vol aplicar i esborrar. Per defecte, stash@{0}.

Recuperar canvis i esborrar la reserva amb stash pop Recuperar canvis i esborrar la reserva amb stash pop

Figura 4. Recuperar canvis i esborrar la reserva amb stash pop.

Exemple: Recuperar els canvis amb pop

S'apliquen els canvis de la reserva més recent i s'esborra la reserva.

jpuigcerver@fp:~/git_stash (main) $ git restore README.md # (1)!
jpuigcerver@fp:~/git_stash (main) $ git status
On branch main
nothing to commit, working tree clean
jpuigcerver@fp:~/git_stash (main) $ git stash pop
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (5421da4ebcbd229b003f105eeefea1b6cfa13f3e)
jpuigcerver@fp:~/git_stash (main) $ git status
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
    modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")
jpuigcerver@fp:~/git_stash (main) $ git diff
diff --git a/README.md b/README.md
index d6704e3..208685c 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,2 @@
 # Reserva de canvis
+Canvi C
jpuigcerver@fp:~/git_stash (main) $ git stash list
stash@{0}: On main: Canvi B
stash@{1}: On main: Canvi A
  1. Es descarten els canvis del Directori de treball de l'exemple anterior.

Descartar els canvis

Una reserva es pot eliminar de la pila amb l'acció drop:

git stash drop [<index>]
  • [<index>]: (opcional) índex de la reserva que es vol descartar. Per defecte, stash@{0}.
Exemple: Descartar els canvis

Es descarta la reserva més recent.

jpuigcerver@fp:~/git_stash (main) $ git stash list
stash@{0}: On main: Canvi B
stash@{1}: On main: Canvi A
jpuigcerver@fp:~/git_stash (main) $ git stash drop
Dropped refs/stash@{0} (a1be7febfb9e812173942ae63be37375e8d27d13)
jpuigcerver@fp:~/git_stash (main) $ git stash list
stash@{0}: On main: Canvi A

S'observa que la reserva descartada desapareix de la llista i que l'índex de la resta es redueix en 1.

📌 Aquest document pot quedar desactualitzat després d’imprimir-lo. Pots consultar la versió més recent a la pàgina web.
🌿 Abans d’imprimir aquest document, considera si és realment necessari. Redueix el consum de paper i ajuda a protegir el nostre entorn.