Questo articolo Γ¨ una libera traduzione di questo post.
Git ha uno stato chiamato βdetached HEADβ che allβinizio risulta di difficile comprensione.
Lβunica notizia che viene subito fornita ai programmatori Γ¨ di non committare su una detached HEAD. In questo articolo cerchiamo di spiegare meglio il funzionamento di questo dettaglio di git.
Lβoggetto HEAD
Partiamo con la descrizione di HEAD: questa Γ¨ tipicamente un puntatore allβultimo commit di un branch.
Per visualizzare dove punta lβHEAD attuale basta eseguire un
cat .git/HEAD
>> ref: refs/heads/master
che indica che lβHEAD attuale punta allβultimo commit del master. Ora, quando cambio branch tramite un
git checkout branchname
HEAD punterΓ quindi allβultimo commit di βbranchnameβ, e questo significa che il comando eseguito in precedenza fornirΓ i seguenti risultati:
cat .git/HEAD
>> ref: refs/heads/branchname
Questo Γ¨ il comportamento standard.
Detached-HEAD
Ora, assumiamo di voler visualizzare lo stato del codice ad un nodo del grafo che non Γ¨ lβultimo commit di un branch locale, questo porta al fatto che HEAD non Γ¨ piΓΉ un puntatore allβultimo commit di un branch locale, ma Γ¨ un puntatore allo SHA-1 di un commit specifico.
Questo stato Γ¨ detto di detached HEAD, in quanto ogni commit che viene eseguito in questo stato non appartiene a nessun branch (oppure possiamo considerarlo come appartenente ad un branch anonimo).
Se cosΓ¬ non fosse, un commit eseguito dopo un checkout ad un nodo del grafo che non Γ¨ lβultimo, eliminerebbe tutti i nodi successivi.
Per esempio i seguenti comandi portano tutti ad uno stato di detached HEAD:
git checkout master^ # parent of master
git checkout HEAD~2 # grandparent of current HEAD
git checkout origin/master # a non-local branch
git checkout tagname # since you cant commit to a tag!
Recuperare da uno stato di detached HEAD
Qualora incontrassi lβerrore tipico di effettuare alcuni commit in uno stato di detached HEAD (e quindi su un branch anonimo), posso recuperare facilmente con il seguente comando:
git checkout -b newbranch
che crea un nuovo branch chiamato βnewbranchβ spostando tutti i commit in questo branch. Il branch anonimo ora diventa un branch classico.
Come recuperare dei commit perduti
Assumiamo ora di non accorgersi di stare committando in un branch anonimo e di cambiare branch:
git checkout someoldbranch
Ora HEAD Γ¨ un riferimento simbolico a βsomeoldbranchβ e il suo valore precedente (lo SHA-1 dellβultimo commit eseguito in detached HEAD) Γ¨ stato sovrascritto.
Per come sono descritte le cose sembra che i commit ora siano definitivamente persi, ma per fortuna così non è.
I commit infatti esistono ancora da qualche parte, bisogna solo andargli a recuperare.
Git possiede un comando chiamato βreflog
β che permette di recuperare tutto quello che Γ¨ stato fatto sulla repository negli ultimi 30 giorni, soprattutto lo SHA-1 del commit perduto.
git reflog show HEAD@{now} -10
dcd215b... HEAD@{5 minutes ago}: commit (amend): 0-terminology: the malloc analogy added, plus
5ce8bfe... HEAD@{11 minutes ago}: commit: 0-terminology: the malloc analogy added, plus
3d93420... HEAD@{11 minutes ago}: rebase -i (pick): updating HEAD
7fdae94... HEAD@{11 minutes ago}: checkout: moving from master to 7fdae94815d6c676742c9984132b7b9e71a57f98
3d93420... HEAD@{13 minutes ago}: rebase -i (squash): updating HEAD
c55900c... HEAD@{13 minutes ago}: rebase -i (pick): updating HEAD
7fdae94... HEAD@{13 minutes ago}: checkout: moving from master to 7fdae94815d6c676742c9984132b7b9e71a57f98
e9955c8... HEAD@{14 minutes ago}: commit: s
97ab644... HEAD@{20 minutes ago}: commit: autogen
c55900c... HEAD@{23 minutes ago}: commit (amend): 0-terminology: the malloc analogy added, plus
Una volta trovato lo SHA-1 che mi serviva, andiamo a recuperarlo e inseriamolo in un nuovo branch:
git branch thank_God_its_safe 7fdae94
git checkout thank_God_its_safe