git stash esegue lo shelve temporaneamente (o accantona) le modifiche apportate alla copia di lavoro per consentirti di lavorare su qualcos'altro e di tornare poi indietro e riapplicarle in un secondo momento. L'accantonamento è utile se devi cambiare rapidamente contesto e lavorare su qualcos'altro, ma sei a metà di una modifica del codice e non sei ancora pronto per il commit.

Accantonamento del lavoro

Il comando git stash prende le modifiche non sottoposte a commit (sia preparate che non preparate per il commit), le salva per un uso futuro e poi le ripristina dalla copia di lavoro. Ad esempio:

$ git status
On branch main
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

$ git stash
Saved working directory and index state WIP on main: 5002d47 our new homepage
HEAD is now at 5002d47 our new homepage

$ git status
On branch main
nothing to commit, working tree clean

A questo punto puoi apportare modifiche, creare nuovi commit, passare da un branch all'altro ed eseguire qualsiasi altra operazione Git; quindi, puoi tornare indietro e riapplicare l'accantonamento quando preferisci.

Nota che l'accantonamento si trova in locale nel repository Git; gli accantonamenti non vengono trasferiti sul server quando esegui il push.

Riapplicazione delle modifiche accantonate

Puoi riapplicare le precedenti modifiche accantonate utilizzando git stash pop:

$ git status
On branch main
nothing to commit, working tree clean
$ git stash pop
On branch main
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

Dropped refs/stash@{0} (32b3aa1d185dfe6d57b3c3cc3b32cbf3e380cc6a)

Il prelievo dell'accantonamento rimuove le modifiche da quest'ultimo e le riapplica alla copia di lavoro.

In alternativa, puoi riapplicare le modifiche alla copia di lavoro e conservarle nell'accantonamento con il comando git stash apply:

$ git stash apply
On branch main
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

Questa procedura è utile se desideri applicare le stesse modifiche accantonate a più branch.

Ora che conosci le basi dell'accantonamento, occorre fare una precisazione su git stash: per impostazione predefinita, Git non accantonerà le modifiche apportate ai file non monitorati o ignorati.

Accantonamento dei file non monitorati o ignorati

Per impostazione predefinita, l'esecuzione di git stash accantonerà:

  • modifiche aggiunte all'indice (modifiche preparate per il commit)
  • modifiche apportate ai file attualmente monitorati da Git (modifiche non preparate per il commit)

Ma non accantonerà:

  • nuovi file nella copia di lavoro che non sono stati ancora preparati per il commit
  • file ignorati

Quindi, se aggiungiamo un terzo file al nostro esempio precedente, ma non lo prepariamo per il commit (ad es. non eseguiamo git add), git stash non lo accantonerà.

$ script.js

$ git status
On branch main
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

Untracked files:

    script.js

$ git stash
Saved working directory and index state WIP on main: 5002d47 our new homepage
HEAD is now at 5002d47 our new homepage

$ git status
On branch main
Untracked files:

    script.js

L'aggiunta dell'opzione -u (o --include-untracked) dice a git stash di accantonare anche i file non monitorati:

$ git status
On branch main
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

Untracked files:

    script.js

$ git stash -u
Saved working directory and index state WIP on main: 5002d47 our new homepage
HEAD is now at 5002d47 our new homepage

$ git status
On branch main
nothing to commit, working tree clean

Puoi includere anche le modifiche ai file ignorati inviando l'opzione -a (o --all) quando esegui git stash.

Opzioni di git stash

Gestione di più accantonamenti

Non sei limitato a un solo accantonamento. Puoi eseguire git stash più volte per creare più accantonamenti e quindi usare git stash list per visualizzarli. Per impostazione predefinita, gli accantonamenti sono identificati semplicemente come "WIP" (lavoro in corso) in cima al branch e al commit da cui hai creato l'accantonamento. Dopo un po', può essere difficile ricordare i contenuti di ciascun accantonamento:

$ git stash list
stash@{0}: WIP on main: 5002d47 our new homepage
stash@{1}: WIP on main: 5002d47 our new homepage
stash@{2}: WIP on main: 5002d47 our new homepage

Per fornire un po' più di contesto, è buona norma aggiungere una descrizione agli accantonamenti, usando git stash save "message":

$ git stash save "add style to our site"
Saved working directory and index state On main: add style to our site
HEAD is now at 5002d47 our new homepage

$ git stash list
stash@{0}: On main: add style to our site
stash@{1}: WIP on main: 5002d47 our new homepage
stash@{2}: WIP on main: 5002d47 our new homepage

Per impostazione predefinita, git stash pop riapplicherà l'accantonamento creato più di recente: stash@ {0}

Puoi scegliere quale accantonamento riapplicare inviando il relativo identificatore come ultimo argomento, ad esempio:

$ git stash pop stash@{2}

Visualizzazione delle differenze tra gli accantonamenti

Puoi visualizzare un riepilogo di un accantonamento con git stash show:

$ git stash show
 index.html | 1 +
 style.css | 3 +++
 2 files changed, 4 insertions(+)

Oppure, puoi inviare l'opzione -p (o --patch) per visualizzare le differenze complete di un accantonamento:

$ git stash show -p
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..d92368b
--- /dev/null
+++ b/style.css
@@ -0,0 +1,3 @@
+* {
+  text-decoration: blink;
+}
diff --git a/index.html b/index.html
index 9daeafb..ebdcbd2 100644
--- a/index.html
+++ b/index.html
@@ -1 +1,2 @@
+<link rel="stylesheet" href="style.css"/>

Accantonamenti parziali

Puoi inoltre scegliere di accantonare un singolo file, una raccolta di file o singole modifiche all'interno dei file. Se invii l'opzione -p (o --patch) a git stash, il comando scorrerà ogni "hunk" modificato nella copia di lavoro e ti chiederà se desideri accantonarlo:

$ git stash -p
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..d92368b
--- /dev/null
+++ b/style.css
@@ -0,0 +1,3 @@
+* {
+  text-decoration: blink;
+}
Stash this hunk [y,n,q,a,d,/,e,?]? y
diff --git a/index.html b/index.html
index 9daeafb..ebdcbd2 100644
--- a/index.html
+++ b/index.html
@@ -1 +1,2 @@
+<link rel="stylesheet" href="style.css"/>
Stash this hunk [y,n,q,a,d,/,e,?]? n
git stash -p

Puoi premere ? per un elenco completo dei comandi di hunk. Quelli più utili sono:

Comando Descrizione
/ cerca un hunk in base all'espressione regolare
? assistenza
n non accantonare questo hunk
q esci (tutti gli hunk che sono già stati selezionati verranno accantonati)
s dividi questo hunk in hunk più piccoli
y accantona questo hunk

Non esiste un comando "abort" esplicito, ma se premi CTRL-C(SIGINT) il processo di accantonamento verrà interrotto.

Creazione di un branch dall'accantonamento

Se le modifiche sul branch divergono da quelle nell'accantonamento, potresti incorrere in conflitti quando prelevi o applichi l'accantonamento. In alternativa, puoi usare git stash branch per creare un nuovo branch a cui applicare le modifiche accantonate:

$ git stash branch add-stylesheet stash@{1}
Switched to a new branch 'add-stylesheet'
On branch add-stylesheet
Changes to be committed:

    new file:   style.css

Changes not staged for commit:

    modified:   index.html

Dropped refs/stash@{1} (32b3aa1d185dfe6d57b3c3cc3b32cbf3e380cc6a)

Esegue il checkout di un nuovo branch in base al commit da cui hai creato l'accantonamento, quindi vi inserisce le modifiche accantonate.

Pulizia dell'accantonamento

Se decidi che non ti serve più un determinato accantonamento, puoi eliminarlo con git stash drop:

$ git stash drop stash@{1}
Dropped stash@{1} (17e2697fd8251df6163117cb3d58c1f62a5e7cdb)

Oppure puoi eliminare tutti gli accantonamenti con:

$ git stash clear

Come funziona git stash

Se vuoi solo sapere come usare git stash, puoi fermarti qui, senza leggere oltre. Ma se ti incuriosisce conoscere il funzionamento interno di Git (e di git stash), continua a leggere.

Gli accantonamenti sono in realtà codificati nel repository sotto forma di oggetti di commit. Il riferimento speciale in .git/refs/stash punta all'accantonamento creato più di recente e gli accantonamenti creati in precedenza sono riportati nel log di riferimento del riferimento di stash. Questo è il motivo per cui si fa riferimento agli accantonamenti con stash@{n}: in realtà si fa riferimento all'ennesima voce del log di riferimento relativa al riferimento di stash. Dal momento che un accantonamento è semplicemente un commit, puoi ispezionarlo con git log:

$ git log --oneline --graph stash@{0}
*-.   953ddde WIP on main: 5002d47 our new homepage
|\ \ 
| | * 24b35a1 untracked files on main: 5002d47 our new homepage
| * 7023dd4 index on main: 5002d47 our new homepage
|/ 
* 5002d47 our new homepage

A seconda degli elementi che hai accantonato, una singola operazione git stash crea due o tre nuovi commit. I commit nel diagramma riportato sopra sono:

  • stash@{0}, un nuovo commit per archiviare i file monitorati che erano nella copia di lavoro quando hai eseguito git stash
  • Il primo elemento principale di stash@{0}, il commit preesistente che si trovava su HEAD quando hai eseguito git stash
  • Il secondo elemento principale di stash@{0}, un nuovo commit che rappresentava l'indice quando hai eseguito git stash
  • Il terzo elemento principale di stash@{0}, un nuovo commit che rappresenta i file non monitorati che erano nella copia di lavoro quando hai eseguito git stash. Questo terzo elemento principale è stato creato solo se:
    • la copia di lavoro conteneva effettivamente file non monitorati; e
    • hai specificato l'opzione --include-untracked o --all quando hai richiamato git stash.

In che modo git stash codifica la struttura di lavoro e l'indice sotto forma di commit:

  • Prima dell'accantonamento, la struttura di lavoro può contenere le modifiche ai file monitorati, non monitorati e ignorati. Alcune di queste modifiche possono inoltre essere preparate per il commit nell'indice.

    Prima dell'accantonamento
  • Richiamando git stash, vengono codificate tutte le modifiche ai file monitorati come due nuovi commit nel DAG: uno per le modifiche non preparate per il commit e uno per le modifiche preparate per il commit nell'indice. Il riferimento refs/stash speciale viene aggiornato in modo da puntare a queste ultime.

    Git stash
  • L'uso dell'opzione --include-untracked consente di codificare anche le eventuali modifiche ai file non monitorati come commit aggiuntivo.

    Git stash --include-untracked
  • L'uso dell'opzione --all include le modifiche a tutti i file ignorati e le modifiche ai file non monitorati nello stesso commit.

    Git stash --all

Quando esegui git stash pop, le modifiche provenienti dai commit riportati sopra vengono utilizzate per aggiornare la copia di lavoro e l'indice e il log di riferimento dell'accantonamento viene riordinato in modo da rimuovere il commit prelevato. Nota che i commit prelevati non vengono eliminati immediatamente, ma vengono tenuti da parte per un possibile uso nella futura garbage collection.

Pronto per imparare a utilizzare Git?

Prova questo tutorial interattivo.

Inizia ora