Innanzitutto, chiariamo cos'è HEAD e cosa significa quando è staccato.
HEAD è il nome simbolico per il commit attualmente estratto. Quando HEAD non è staccato (la situazione "normale" 1 : hai un ramo estratto), HEAD in realtà punta al "ref" di un ramo e il ramo punta al commit. HEAD è quindi "attaccato" a un ramo. Quando si effettua un nuovo commit, il ramo a cui punta HEAD viene aggiornato per puntare al nuovo commit. HEAD segue automaticamente poiché punta semplicemente al ramo.
git symbolic-ref HEAD
rese refs/heads/master
La filiale denominata "master" viene estratta.
git rev-parse refs/heads/master
rendimento 17a02998078923f2d62811326d130de991d1a95a
Quel commit è l'attuale suggerimento o "capo" del ramo principale.
git rev-parse HEAD
produce anche 17a02998078923f2d62811326d130de991d1a95a
Questo è ciò che significa essere un "riferimento simbolico". Indica un oggetto attraverso qualche altro riferimento.
(I riferimenti simbolici sono stati originariamente implementati come collegamenti simbolici, ma in seguito sono stati modificati in file semplici con interpretazione aggiuntiva in modo da poter essere utilizzati su piattaforme che non dispongono di collegamenti simbolici.)
Abbiamo HEAD
→ refs/heads/master
→17a02998078923f2d62811326d130de991d1a95a
Quando HEAD è staccato, punta direttamente a un commit, anziché indirettamente verso uno attraverso un ramo. Puoi pensare a una HEAD distaccata come a un ramo senza nome.
git symbolic-ref HEAD
fallisce con fatal: ref HEAD is not a symbolic ref
git rev-parse HEAD
rese 17a02998078923f2d62811326d130de991d1a95a
Poiché non è un riferimento simbolico, deve puntare direttamente al commit stesso.
Abbiamo HEAD
→17a02998078923f2d62811326d130de991d1a95a
La cosa importante da ricordare con un HEAD distaccato è che se il commit a cui punta è altrimenti non referenziato (nessun altro riferimento può raggiungerlo), allora diventerà "penzolante" quando si verifica un altro commit. Alla fine, tali commit sospesi saranno potati attraverso il processo di raccolta dei rifiuti (per impostazione predefinita, vengono conservati per almeno 2 settimane e possono essere conservati più a lungo facendo riferimento al reflog di HEAD).
1
Va benissimo fare un lavoro "normale" con un HEAD distaccato, devi solo tenere traccia di ciò che stai facendo per evitare di dover eliminare la cronologia abbandonata dal reflog.
I passaggi intermedi di un rebase interattivo vengono eseguiti con una HEAD distaccata (parzialmente per evitare l'inquinamento del reflog del ramo attivo). Al termine dell'operazione di rebase completa, aggiornerà il ramo originale con il risultato cumulativo dell'operazione di rebase e ricollegherà HEAD al ramo originale. La mia ipotesi è che non hai mai completato completamente il processo di rebase; questo ti lascerà con un HEAD distaccato che punta al commit che è stato elaborato più di recente dall'operazione rebase.
Per ripristinare la situazione, è necessario creare un ramo che punti al commit attualmente indicato dal proprio HEAD distaccato:
git branch temp
git checkout temp
(questi due comandi possono essere abbreviati come git checkout -b temp
)
Ciò ricollegherà HEAD al nuovo temp
ramo.
Successivamente, dovresti confrontare l'attuale commit (e la sua cronologia) con il ramo normale su cui ti aspettavi di lavorare:
git log --graph --decorate --pretty=oneline --abbrev-commit master origin/master temp
git diff master temp
git diff origin/master temp
(Probabilmente vorrai sperimentare le opzioni di registro: aggiungi -p
, lascia perdere --pretty=…
per vedere l'intero messaggio di registro, ecc.)
Se il tuo nuovo temp
ramo ha un bell'aspetto, potresti voler aggiornare (ad es.) master
Per puntarlo:
git branch -f master temp
git checkout master
(questi due comandi possono essere abbreviati come git checkout -B master temp
)
È quindi possibile eliminare il ramo temporaneo:
git branch -d temp
Infine, probabilmente vorrai spingere la storia ristabilita:
git push origin master
Potrebbe essere necessario aggiungere --force
alla fine di questo comando per inviare se il ramo remoto non può essere "inoltrato rapidamente" al nuovo commit (ovvero è stato eliminato, riscritto un commit esistente o riscritto in qualche modo un po 'di cronologia).
Se ti trovassi nel mezzo di un'operazione di rebase, probabilmente dovresti ripulirla. È possibile verificare se era in corso un rebase cercando la directory .git/rebase-merge/
. Puoi ripulire manualmente il rebase in corso semplicemente cancellando quella directory (ad esempio se non ricordi più lo scopo e il contesto dell'operazione di rebase attiva). Di solito useresti git rebase --abort
, ma questo comporta un ulteriore ripristino che probabilmente vorrai evitare (riporta HEAD al ramo originale e lo ripristina al commit originale, che annullerà parte del lavoro che abbiamo fatto sopra).