Git --distributed-even-if-your-workflow-isnt
Chapters ▾ 1st Edition

6.3 Strumenti di Git - Accantonare

Accantonare

Spesso, mentre stai lavorando ad una parte del tuo progetto, le cose possono essere in uno stato confusionario e tu vuoi passare a un'altra ramificazione per lavorare per un po' a qualcosa di diverso. Il problema è che non vuoi committare qualcosa fatta a metà per poi tornarci in un secondo momento. La risposta a questo problema è il comando git stash.

Questo comando prende tutte le modifiche della tua cartella di lavoro — cioè tutti i file tracciati che hai modificato e le modifiche assemblate — e le accantona in una pila di modifiche incomplete che puoi riapplicare in qualsiasi momento.

Accantona il tuo lavoro

Per dimostrare come funziona, vai nella cartella del tuo progetto e modifica un paio di file e assemblane alcuni. Se esegui git status, puoi vederne lo stato "sporco":

$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#      modified:   index.html
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#
#      modified:   lib/simplegit.rb
#

Ora vuoi passare a un'altra diramazione, ma non vuoi ancora committare il tuo lavoro perché non è ancora pronto; accantona quindi le tue modifiche. Per aggiungere un nuovo livello alla pila devi eseguire git stash:

$ git stash
Saved working directory and index state \
  "WIP on master: 049d078 added the index file"
HEAD is now at 049d078 added the index file
(To restore them type "git stash apply")

La tua cartella di lavoro ora è pulita:

$ git status
# On branch master
nothing to commit, working directory clean

A questo punto puoi passare facilmente a un'altra diramazione e lavorare ad altro; le tue modifiche sono salvate nella tua pila di accantonamento. Per vedere cosa c'è nella tua pila usa git stash list:

$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051 Revert "added file_size"
stash@{2}: WIP on master: 21d80a5 added number to log

In questo caso hai due accantonamenti precedenti, e hai così accesso a tre lavori differenti accantonati. Puoi riapplicare quello che hai appena accantonato usando il comando mostrato nell'output d'aiuto del comando che hai usato quanto hai accantonato le tue modifiche: git stash apply. Se vuoi applicare uno degli accantonamenti precedenti, puoi specificarlo usandone il nome così: git stash apply stash@{2}. Se non specifiche un accantonamento Git suppone che ti riferisca all'ultimo e prova ad applicarlo:

$ git stash apply
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#
#      modified:   index.html
#      modified:   lib/simplegit.rb
#

Puoi vedere che Git ha rimodificato i file che aveva rimosso quando hai salvato l'accantonamento. In questo caso avevi una cartella di lavoro pulita quando provasti ad applicare l'accantonamento e stai provando a riappricarlo alla stessa ramificazione da cui l'avevi salvato; ma non è necessario avere una cartella pulita per applicare un accantonamento né che sia la stessa ramificazione. Puoi salvare un accantonamento da una ramificazione, passare a un'altra e applicare le modifiche accantonate. Nella tua cartella puoi anche avere modifiche non ancora committate quando applichi un accantonamento: Git ti darà un conflitto d'incorporamento nel caso che qualcosa non si possa applicare senza problemi.

Le modifiche vengono riapplicate ai tuoi file, ma quello che avevi assemblato ora non lo sono. Per farlo devi eseguire il comando git stash apply con il parametro --index perché provi a riapplicare le modifiche assemblate. Se lo avessi fatto ti saresti trovato nella stessa posizione originale:

$ git stash apply --index
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#      modified:   index.html
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#
#      modified:   lib/simplegit.rb
#

L'opzione apply prova solo ad applicare le modifiche assemblate: continua per averle nel tuo accantonamento. Per cancellare l'accantonamento devi eseguire git stash drop con il nome dell'accantonamento da rimuovere:

$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051 Revert "added file_size"
stash@{2}: WIP on master: 21d80a5 added number to log
$ git stash drop stash@{0}
Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)

Puoi eseguire anche git stash pop per applicare l'accantonamento e cancellare immediatamente dopo.

Annullare una modifica accantonata

In alcuni scenari potresti voler applicare delle modifiche accantonate, fare dell'altro lavoro e successivamente voler rimuovere le modifiche che venivano dall'accantonamento. Git non ha un comando stash unapply, ma è possibile ottenere lo stesso risultato richiamando la modifica associata all'accantonamento e applicarla al contrario:

$ git stash show -p stash@{0} | git apply -R

Di nuovo, se non specifichi un accantonamento, Git assume che sia l'ultimo:

$ git stash show -p | git apply -R

Puoi voler creare un alias per avere un comando stash-unapply nel tuo Git. Per esempio:

$ git config --global alias.stash-unapply '!git stash show -p | git apply -R'
$ git stash apply
$ #... work work work
$ git stash-unapply

Creare una diramazione da un accantonamento

Se accantoni del lavoro e lo lasci lì mentre continui a lavorare per un po' nella diramazione da cui hai creato l'accantonamento, potresti avere dei problemi a riapplicarlo. Se l'applicazione prova a modificare un file che avevi modificato successivamente, otterrai un conflitto d'incorporazione e dovrai risolverlo. Se vuoi un modo facile per ritestare le modifiche accantonate, puoi eseguire il comando git stash branch, che crea una diramazione, scarica la commit dov'eri quando hai accantonato il tuo lavoro, lo riapplica e, se ci riesce senza problemi, cancella l'accantonamento:

$ git stash branch testchanges
Switched to a new branch "testchanges"
# On branch testchanges
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#      modified:   index.html
#
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#
#      modified:   lib/simplegit.rb
#
Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359)

Questa è una bella accorciatoioa per recuperare il lavoro accantonato e lavorarci in una nuova diramazione.