Fin qui tutto molto basic, ma siamo ancora ben lontani dall’aver imparato ad usare Git in maniera efficace. Finora non abbiamo fatto altro che salvare delle modifiche nel database di versionamento e nulla più, ma se volessimo vedere lo stato dei file in quella specifica commit che vediamo visualizzando i log? Beh in questa lezione vedremo proprio questo.
Il comando git checkout
L’abbiamo intravisto durante la creazione degli alias, ma esattamente cosa vuol dire checkout in Git? Quando lo sentii per la prima volta non mi fu immediatamente chiaro, per me il checkout è quando te ne vai da un albergo, ma in Git checkout significa "passare", in inglese switch.
Questo perché il comando git checkout ci permette di passare (switchare) tra versioni diverse di un’entità, nello specifico: file, commit e branch.
Quando cercate su Google git checkout potete osservare che la maggior parte delle volte si fa riferimento al comando per cambiare le branch, ma non serve solo a quello. In questa lezione noi lo useremo per poter tornare indietro nella cronologia (non in via definitiva) e vedere lo stato della working directory in un determinato punto nel tempo.
Non entrerò ulteriormente nel dettaglio del comando perché tanto lo vedremo meglio più avanti durante lo spiegone sulle branch. Se però vuoi saperne di più già da ora, c’è sempre la documentazione ufficiale.
Recuperare l’hash di una vecchia commit
Ormai dovreste saperlo fare ad occhi chiusi, recuperare l’identificativo di una commit.
Se non avete seguito la lezione su come impostare un alias su Git, potete scrivere questo comando a terminale
1$ git log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
Per chi invece ha sapientemente seguito le lezioni precedenti potrà scrivere solo:
1$ git hist
Vi avevo detto che sarebbe stato utile usare un alias per il comando di git log.
L’esito sarà più o meno simile a questo:
1* 24da56a 2023-06-13 | Aggiunti vari file (HEAD -> master) [Manuel Ricci]
2* fbcfe0b 2023-06-07 | Questo è il mio oggetto e deve arrivare più o meno fino a qui [Manuel Ricci]
3* bdc5f46 2023-06-07 | Aggiunta reference a lorem.txt [Manuel Ricci]
4* 99bb4c6 2023-06-07 | Aggiunto lo spagnolo [Manuel Ricci]
5* fa09924 2023-06-07 | Terza commit [Manuel Ricci]
6* 14b02a5 2023-06-07 | Seconda commit [Manuel Ricci]
7* 937db31 2023-06-07 | Prima commit [Manuel Ricci]
L’hash è quel codice subito dopo l’asterisco, ma questo già lo sapevate. Vi ricordo che usando %h
otteniamo un hash breve, se avessimo usato %H
allora avremmo avuto l’hash completo di 40 caratteri.
Scegliete un hash a vostro piacimento, io vado con il 99bb4c6
.
Visualizzare una vecchia commit
Hash alla mano possiamo eseguire il comando
1$ git checkout 99bb4c6
Ovviamente il vostro hash sarà diverso quindi sostituitelo al mio.
Ricordatevi che nella lezione sugli alias ne abbiamo impostato uno anche per checkout. Semmai doveste dimenticarvi quali alias avete impostato potete sempre usare il comando
1$ git config --global -l
Tra le varie configurazioni troverete anche i vari alias e nel caso di checkout avevamo scelto git co. Quindi volendo potete anche eseguire
1$ git co 99bb4c6
In seguito all’esecuzione del comando, Git ci mostrerà qualcosa di simile a quanto segue:
1Note: switching to '99bb4c6'.
2
3You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by switching back to a branch.
4
5If you want to create a new branch to retain commits you create, you may do so (now or later) by using -c with the switch command. Example:
6
7 git switch -c <new-branch-name>
8
9Or undo this operation with:
10
11 git switch -
12
13Turn off this advice by setting config variable advice.detachedHead to false
14
15HEAD is now at 99bb4c6 Aggiunto lo spagnolo
Per quanto penso sia chiarissimo l’output, cerchiamo di capire meglio quello che ci sta dicendo. Al di là delle cose ovvie come il fatto che lo switch sia avvenuto correttamente, ci sta parlando di una certa detached HEAD. Ma cos’è? Ma soprattutto vi ricordate cos’è la HEAD?
Cos’è la HEAD in Git? Un piccolo ripasso
Git è una collezione di oggetti e referenze. Gli oggetti sono relazionati tra di loro e le referenze puntano ad oggetti e ad altre referenze.
Gli oggetti principali di Git sono tre e sono: commit, blob e tree; mentre le referenze più importanti sono le branch (di cui ancora non abbiamo parlato in maniera approfondita), le quali non sono altro che delle etichette che appiccichiamo alle commit.
HEAD è una referenza molto importante. Il suo scopo è quello di tenere traccia del punto corrente della repository Git. In altre parole ci dice dove siamo.
Quando eseguiamo il git log, da dove parte Git, dalla HEAD. In una situazione normale, cioè quella dove eravamo fino a poco fa, prima di eseguire il comando di checkout, la HEAD era l’ultima commit effettuata. E ora? Provate ad eseguire un git log
(o git hist
). Da dove parte? Dalla commit sulla quale abbiamo switchato.
199bb4c6 (HEAD) Aggiunto lo spagnolo
2fa09924 Terza commit
314b02a5 Seconda commit
4937db31 Prima commit
Cosa vuol dire essere nello stato di detached HEAD?
Piccola premessa per chi arriva magari da una ricerca su Google ed è nel panico cercando “Come sistemare detached HEAD in Git?”, calma. Va tutto bene e non è successo niente di grave. Più giù trovi la soluzione al tuo problema :)
Sappiamo quindi che la HEAD è la referenza che Git usa per indicare l’attuale posizione nella cronologia delle commit. Nel suo stato "normale" HEAD indica l’ultima commit della branch corrente o in altre parole HEAD fa riferimento al nome della branch (ricordate, le referenze possono puntare ad altre referenze), ad esempio master o main. Questo significa che le HEAD è attached.
Quando però HEAD non fa più riferimento all’ultima commit e non è più referenziata ad una branch, ma ad una commit, lì la HEAD è detached.
La detached branch e i suoi benefici
1You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by switching back to a branch.
C’è davvero bisogno di sottolineare i benefici? Il messaggio di Git è cristallino, nello stato detached possiamo:
- Farci un giro
- Effettuare dei cambiamenti sperimentali e salvarli
- Possiamo scartare tutte le commit fatte in questo stato senza impattare nessuna branch tornando alla branch precedente.
Provate a fare una modifica ai file presenti nella vostra repository e a salvare quanto fatto in una nuova commit.
1014a869 (HEAD) Aggiunto file
299bb4c6 Aggiunto lo spagnolo
3fa09924 Terza commit
414b02a5 Seconda commit
5937db31 Prima commit
Qui le cose si stanno complicando un attimino. Forse è meglio mappare il nostro attuale percorso.
Ci siamo separati dal flusso principale, ma non abbiamo creato una branch. Siamo in una situazione dove queste modifiche che stiamo apportando sono temporanee, a meno che non decidiamo di mantenerle. Ma come? Ci arriviamo subito.
Come sistemare detached HEAD in Git?
Facciamo tirare un sospiro di sollievo al nostro utente di Google che sta cercando di uscire da questo non casino. Già, perché la detached HEAD è uno stato normalissimo di Git e quindi non c’è fix se non c’è niente di rotto.
Ci si presentano quindi diversi scenari quindi che possono presentarsi.
1 Sono in detached HEAD per sbaglio
Capita. Tornare indietro è comunque molto facile.
1$ git checkout master
Oppure, nelle nuove versioni di Git (>2.23.0) possiamo usare anche:
1$ git switch master
Oppure
1$ git switch -
Come c’era scritto nel messaggio comparso subito dopo essere entrati in stato di detached. Leggete con attenzione, mi raccomando.
2 Sono in detached mode volontariamente, ho fatto delle modifiche e voglio scartarle e tornare alla situazione precedente
Vedi scenario 1. (Sono riuscito ad essere breve)
3 Sono in detached mode volontariamente, ho fatto delle modifiche e voglio tenerle
Anche qui la procedura è molto semplice e ci basterà creare una branch per poter salvare definitivamente le modifiche. Per farlo
1$ git switch -c <new-branch-name>
Oppure
1$ git checkout -b <nome-branch>
Oppure
1$ git branch <nome-branch>
2$ git checkout <nome-branch>
Arrivati a questo punto abbiamo quasi analizzato tutto il messaggio che Git ci aveva restituito, manca solo la penultima frase.
1Turn off this advice by setting config variable advice.detachedHead to false
Il quale ci dice che possiamo disattivare completamente questo messaggio impostando a falso la variabile di configurazione advice.detachedHead. Personalmente preferisco tenerla attiva, ma semmai dovesse darvi fastidio basta eseguire il comando:
1$ git config --global advice.detachedHead false
Conclusioni
Dal titolo della lezione si poteva pensare che fosse una lezione breve e invece… Ci vediamo nella prossima dove parleremo di stash.
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!