Full Stack

Salvare i cambiamenti senza fare una commit in git

Autore

Manuel Ricci

Lo stash in Git è un’area dove conservare temporaneamente uno snapshot dei cambiamenti senza la necessità di commit. Si tratta di un’area separata dalla working directory, staging area e repository; ed è il secondo tipo di salvataggio in Git oltre la commit.

Questa è la definizione di massima di stash, ma esattamente a cosa serve e, soprattutto come funziona? Procediamo un po’ per volta.

A cosa serve stash?

Le motivazioni dietro l’uso di stash possono essere molteplici.

Supponiamo di trovarci in una branch diversa da master/main. Abbiamo fatto delle modifiche e abbiamo la necessità di spostarci di branch, ma non siamo pronti a salvare suddette modifiche con commit.

La risposta ovvia sarebbe “eh vabbé cambia branch”, ma cosa accadrebbe se mi dovessi spostare senza commit in un’altra branch? Le modifiche fatte, non salvate, verrebbero con noi durante lo spostamento.

Non suona così male.. E se nella branch nella quale ci siamo spostati dobbiamo fare una modifica ad un file che abbiamo modificato nell’altra branch? E se queste modifiche dovessero modificare ANCHE ciò che abbiamo sviluppato nell’altra branch? Come potete vedere le cose si stanno complicando un pochino…

Se tutto questo giro dell’oca fosse troppo complesso da immaginare, prova a pensarla così. Stai facendo delle modifiche nella branch principale e ti viene chiesto di risolvere al volissimo un bug. Fai la commit a metà sulla branch principale? Direi proprio di no. Quindi devi archiviare da qualche parte quanto fatto fino a quel momento e tirarlo fuori successivamente.

Se ora è più chiaro, possiamo far entrare in scena stash.

Stash infatti ci permette di conversare momentaneamente uno snapshot senza le necessità di procedere con una commit.

Il comando git stash

Per poter salvare i cambiamenti non ancora pronti per la commit il comando da utilizzare è abbastanza semplice:

1$ git stash

Eseguendo il comando verranno salvati nell’area temporanea tutti i file modificati (di cui git teneva una cronologia). Tutto qui? Non proprio…

Git stash è un comando ricco di comandi (un comando con dei comandi), che non sono da confondere con le flag, qui stiamo parlando di veri e propri comandi senza trattini davanti. Questi comandi permettono di eseguire varie azioni, nello specifico abbiamo a disposizione:

  • push (che ha sostituito save)
  • list
  • show
  • pop
  • apply
  • branch
  • clear
  • drop

Una menzione speciale va fatta anche per altri due comandi: create e store; i quali sono pensati e quindi destinati all’uso negli script. Non approfondiremo ulteriormente questi due comandi in questa guida, consiglio come sempre di prendere visione della documentazione ufficiale.

Cosa fanno quindi questi comandi? Vediamoli uno per uno.

Salvare le modifiche in stash

Per poter salvare le modifiche in stash, il comando di default, che volendo possiamo anche omettere se non abbiamo particolari esigenze, è push.

1$ git stash push

Dopo aver eseguito il comando, i file modificati di cui Git stava tenendo traccia verranno salvati nello stash e la directory di lavoro verrà riportata alla HEAD (lo stato in cui si trova nella repository).

Come già menzionato in precedenza push sostituisce il vecchio comando save, il quale permetteva di fare le medesime cose, con alcune differenze.

Le flag del comando git stash push

Push ha delle flag interessanti per poterne modificare l’output, nello specifico: --patch (o -p): permette di salvare pezzi di codice, un po’ come quando abbiamo visto git add --staged (o -S): salva nello stash solo ciò che c’è in staging area --include-untracked (o -u): include nel salvataggio i file untracked (ignorati di default) --all (o -a): inserisce nello stash tutti i fil. Anche quelli ignorati e untracked, per poi eliminarli automaticamente con git clean --message (o -m): permette di includere un messaggio per riconoscere l’elemento nello stash.

Comando per vedere gli elementi nello stash

1$ git stash list

list è il comando che invece ci permette di vedere quali sono gli elementi dello stash. Ogni elemento ha un nome.

stash@{0} è l’ultimo elemento inserito, mentre stash@{1} è il penultimo e così via. Se te lo stai domandando: sì, stash è una pila in termini di strutture dati.

Se durante il push non viene fornito un messaggio l’elemento sarà simile a questo:

1stash@{2}: WIP on test-branch: 793e4d4 First commit

Dove: stash@{2} è il nome dell'elemento nello stash WIP on test-branch ci dice in quale branch il comando è stato eseguito (WIP sta per Work In Progress) 793e4d4 First commit sono l’hash e il messaggio della commit alla quale poi lo stash ha fatto ritornare la working directory

Mentre nel caso un messaggio venisse fornito:

1stash@{1}: On test-branch: Test message for stash entry

Dove: stash@{1} è il nome dell'elemento nello stash On test-branch ci dice in quale branch il comando è stato eseguito Test message for stash entry è il messaggio definito con l’uso della flag -m

Comando per vedere le differenze tra ciò che c’è nello stash

1$ git stash show

show è il comando che ci permette di visualizzare le differenze tra l’ultimo elemento dello stash e la commit di quando l’elemento dello stash è stato creato. Importantissima questa cosa, non la nostra working directory, ma la commit di quando è stato creato.

Di default viene preso l’ultimo elemento inserito nello stash (quello con indice 0), ma volendo è possibile specificare l’elemento in questa maniera:

1$ git stash show stash@{1}

Sempre di default verranno mostrate però delle statistiche di modifica, come queste:

1 index.txt | 1 +
2 1 file changed, 1 insertion(+)

Volendo è possibile passare qualsiasi configurazione disponibile per git diff.

1$ git stash show -p stash@{1}

-p sta per patch e produrrà un output più simile a quello visto in git add.

1diff --git a/index.txt b/index.txt
2index bd122b7..1a6d3d3 100644
3--- a/index.txt
4+++ b/index.txt
5@@ -1,2 +1,3 @@
6 ciao
7 hola
8+hello

Comando per applicare lo stash ed eliminare l’elemento

1$ git stash pop stash@{2}

pop permette di rimuovere un elemento a piacere dallo stash e di applicarlo al working tree corrente.

L’operazione potrebbe non andare sempre per il meglio. Potrebbero infatti crearsi dei conflitti che dovranno essere risolti manualmente. L’elemento dello stash dovrà essere rimosso anch’esso manualmente, cosa che avviene automaticamente qualora tutto andasse a buon fine.

Comando per applicare lo stash

1$ git stash apply stash@{1}

apply è sostanzialmente identico a pop, con l’unica differenza che l’elemento dello stash non viene rimosso automaticamente qualora tutto vada a buon fine.

Comando per creare una nuova branch partendo dall’elemento in stash

Questo è quel classico comando che vi salva quando siete partiti come dei treni a fare le modifiche su una branch, ma dovevate prima crearne una.

Per creare quindi una branch a partire dall’elemento in stash possiamo avvalerci di branch.

1$ git stash branch new-feature stash@{2}

Il comando crea e ci sposta nella nuova branch, applicando ciò che era presente nell’elemento dello stash. Se tutto va a buon fine l’elemento nello stash viene rimosso.

Attenzione che il codice di base alla quale viene poi applicato l’elemento dello stash è quello della commit che c’era quando è stato creato l’elemento nello stash.

Comando per eliminare tutti gli elementi dallo stash

1$ git stash clear

Molto semplice come comando e altrettanto semplice l’output, tutti gli elementi dello stash vengono eliminati.

Comando per eliminare un elemento dallo stash

Mettiamo caso che pop non sia andato a buon fine, avete risolto i conflitti e ora volete rimuovere l’elemento dallo stash, oppure avete usato apply invece di pop e ora volete cancellare l’elemento dallo stash. Il comando che state cercando per ottenere l’eliminazione è drop.

1$ git stash drop stash@{1}

Questo eliminerà l’elemento con indice 1 dello stash.

Questi sono tutti i comandi da conoscere quando si parla di stash. Ovviamente non studiateli a memoria, c’è sempre la documentazione a supporto e, perché no, anche questa guida.

Come funziona esattamente lo stash?

Qui parlo un po’ di come funziona tutta la baracca, il consiglio è quello di continuare a leggere, ma se non sei interessato, la parte pratica è finita.

Come funziona quindi lo stash? Beh partiamo dalla sua codifica nel database di git. Dovete sapere che lo stash è codificato come una commit. Come tale può essere quindi ispezionato con il comando git log.

1$ git log --online --graph stash@{0}

Ogni push crea dalle due alle tre commit, dipende cosa viene inserito in stash.

1*   4ef2831 (refs/stash) WIP on main: 0f773b5 Add message
2|\
3| * d6f4d22 index on main: 0f773b5 Add message
4|/
5* 0f773b5 (HEAD -> main) Add message

Cosa vogliono dire queste informazioni?

4ef2831 = nuova commit che memorizza i file tracciati quando git stash è stato eseguito 0f773b5 = HEAD quando git stash è stato eseguito d6f4d22 = nuova commit che rappresenta l’indice quando è stato eseguito git stash

La terza potenziale commit (che qui non si vede) è quella che rappresentava i file untracked quando è stato eseguito git stash. Viene creata solo se erano presenti file untracked e se è stato incluso --include-untracked o --all durante l’esecuzione di git stash push.

Conclusioni

Con questo abbiamo visto anche come funziona lo stash e come trarne il massimo vantaggio. Nella prossima lezione continuiamo a vedere i comandi che git ci mette a disposizione e parliamo di tag.

Caricamento...

Diventiamo amici di penna? ✒️

Iscriviti alla newsletter per ricevere una mail ogni paio di settimane con le ultime novità, gli ultimi approfondimenti e gli ultimi corsi gratuiti puubblicati. Ogni tanto potrei scrivere qualcosa di commerciale, ma solo se mi autorizzi, altrimenti non ti disturberò oltre.

Se non ti piace la newsletter ti ricordo la community su Discord, dove puoi chiedere aiuto, fare domande e condividere le tue esperienze (ma soprattutto scambiare due meme con me). Ti aspetto!

Ho in previsione di mandarti una newsletter ogni due settimane e una commerciale quando capita.