Tout d'abord, clarifions ce qu'est HEAD et ce qu'il signifie lorsqu'il est détaché.
HEAD est le nom symbolique du commit actuellement extrait. Lorsque HEAD n'est pas détaché (situation «normale» 1 : vous avez extrait une branche), HEAD pointe en fait vers le «ref» d'une branche et la branche pointe vers le commit. HEAD est ainsi «attaché» à une succursale. Lorsque vous effectuez un nouveau commit, la branche vers laquelle pointe HEAD est mise à jour pour pointer vers le nouveau commit. HEAD suit automatiquement car il pointe simplement vers la branche.
git symbolic-ref HEAD
rendements refs/heads/master
La branche nommée «master» est extraite.
git rev-parse refs/heads/master
yield 17a02998078923f2d62811326d130de991d1a95a
Ce commit est la pointe ou la «tête» actuelle de la branche principale.
git rev-parse HEAD
donne aussi 17a02998078923f2d62811326d130de991d1a95a
C'est ce que signifie être un «ref symbolique». Il pointe vers un objet via une autre référence.
(Les références symboliques ont été initialement implémentées sous forme de liens symboliques, mais ont ensuite été transformées en fichiers simples avec une interprétation supplémentaire afin de pouvoir être utilisées sur des plates-formes qui n'ont pas de liens symboliques.)
Nous avons HEAD
→ refs/heads/master
→17a02998078923f2d62811326d130de991d1a95a
Lorsque HEAD est détaché, il pointe directement vers une validation, au lieu de pointer indirectement vers celle-ci via une branche. Vous pouvez penser à une TÊTE détachée comme étant sur une branche sans nom.
git symbolic-ref HEAD
échoue avec fatal: ref HEAD is not a symbolic ref
git rev-parse HEAD
renvoie 17a02998078923f2d62811326d130de991d1a95a
Comme il n'est pas une référence symbolique, il doit pointer directement vers le commit lui-même.
Nous avons HEAD
→17a02998078923f2d62811326d130de991d1a95a
La chose importante à retenir avec une HEAD détachée est que si la validation vers laquelle elle pointe n'est pas référencée (aucune autre référence ne peut l'atteindre), elle deviendra alors "suspendue" lorsque vous retirerez une autre validation. Finalement, ces validations pendantes seront élaguées via le processus de collecte des ordures (par défaut, elles sont conservées pendant au moins 2 semaines et peuvent être conservées plus longtemps en étant référencées par le reflog de HEAD).
1
Il est tout à fait correct de faire un travail «normal» avec une TETE détachée, il vous suffit de garder une trace de ce que vous faites pour éviter d'avoir à repasser l'historique perdu du reflog.
Les étapes intermédiaires d'un rebase interactif se font avec une TETE détachée (en partie pour éviter de polluer le reflog de la branche active). Si vous terminez l'opération de rebase complète, il mettra à jour votre branche d'origine avec le résultat cumulé de l'opération de rebase et rattachera HEAD à la branche d'origine. Je suppose que vous n'avez jamais complètement terminé le processus de rebase; cela vous laissera une HEAD détachée pointant vers la validation qui a été traitée le plus récemment par l'opération de rebase.
Pour récupérer de votre situation, vous devez créer une branche qui pointe vers la validation actuellement pointée par votre HEAD détaché:
git branch temp
git checkout temp
(ces deux commandes peuvent être abrégées en git checkout -b temp
)
Cela rattachera votre HEAD à la nouvelle temp
branche.
Ensuite, vous devez comparer le commit actuel (et son historique) avec la branche normale sur laquelle vous vous attendiez à travailler:
git log --graph --decorate --pretty=oneline --abbrev-commit master origin/master temp
git diff master temp
git diff origin/master temp
(Vous voudrez probablement expérimenter avec les options de journal: ajouter -p
, laisser de côté --pretty=…
pour voir le message de journal entier, etc.)
Si votre nouvelle temp
branche semble bonne, vous voudrez peut-être mettre à jour (par exemple) master
pour la signaler:
git branch -f master temp
git checkout master
(ces deux commandes peuvent être abrégées en git checkout -B master temp
)
Vous pouvez ensuite supprimer la branche temporaire:
git branch -d temp
Enfin, vous voudrez probablement pousser l'histoire rétablie:
git push origin master
Vous devrez peut-être ajouter --force
à la fin de cette commande pour pousser si la branche distante ne peut pas être «accélérée» vers le nouveau commit (c'est-à-dire que vous avez abandonné, ou réécrit un commit existant, ou sinon réécrit un peu d'histoire).
Si vous étiez au milieu d'une opération de rebase, vous devriez probablement la nettoyer. Vous pouvez vérifier si un rebase était en cours en recherchant le répertoire .git/rebase-merge/
. Vous pouvez nettoyer manuellement le rebasage en cours en supprimant simplement ce répertoire (par exemple, si vous ne vous souvenez plus du but et du contexte de l'opération de rebase active). Habituellement, vous utiliseriez git rebase --abort
, mais cela fait une réinitialisation supplémentaire que vous voulez probablement éviter (il ramène HEAD à la branche d'origine et le réinitialise au commit d'origine, ce qui annulera une partie du travail que nous avons fait ci-dessus).