web-dev-qa-db-fra.com

Comment puis-je concilier détaché HEAD avec maître / origine?

Je suis nouveau à la complexité des branches de Git. Je travaille toujours sur une seule branche et valide les modifications, puis répète périodiquement la transmission à mon origine distante.

Quelque part, récemment, j'ai réinitialisé certains fichiers pour les sortir de la mise en place des validations. Plus tard, j'ai fait un rebase -i pour supprimer deux ou trois commits locaux récents. Maintenant, je suis dans un état que je ne comprends pas très bien.

Dans mon espace de travail, git log montre exactement ce à quoi je m'attendais - je suis dans le bon train avec les commits que je ne voulais pas, et les nouveaux là-bas, etc.

Mais j'ai juste poussé vers le référentiel distant, et ce qui existe est différent - deux commits que j'ai tués dans la base ont été poussés, et les nouveaux commis localement ne sont pas là.

Je pense que "maître/origine" est détaché de HEAD, mais je ne suis pas tout à fait sûr de ce que cela signifie, comment le visualiser avec les outils de ligne de commande et comment le corriger.

1457
Ben Zotto

Tout d’abord, clarifions ce que HEAD est et ce que cela signifie quand il est détaché.

HEAD est le nom symbolique du commit actuellement extrait. Lorsque HEAD n'est pas détaché (le paramètre “normal”1 situation: vous avez une branche extraite), HEAD pointe en fait sur le “ref” d’une branche et la branche pointe vers le commit. HEAD est donc "attaché" à une branche. Lorsque vous effectuez une nouvelle validation, la branche vers laquelle HEAD pointe est mise à jour pour pointer vers la nouvelle validation. HEAD suit automatiquement puisqu'il pointe juste sur la branche.

  • _git symbolic-ref HEAD_ donne _refs/heads/master_
    La branche nommée "maître" est extraite.
  • _git rev-parse refs/heads/master_ rendement _17a02998078923f2d62811326d130de991d1a95a_
    Ce commit est l’astuce ou la "tête" actuelle de la branche principale.
  • _git rev-parse HEAD_ donne également _17a02998078923f2d62811326d130de991d1a95a_
    C’est ce que signifie être une "référence symbolique". Il pointe vers un objet à travers une autre référence.
    (Les références symboliques étaient à l’origine implémentées sous forme de liens symboliques, mais ont ensuite été remplacées par des fichiers ordinaires avec une interprétation supplémentaire, de sorte qu’ils puissent être utilisés sur des plates-formes n’ayant pas de liens symboliques.)

Nous avons HEAD → _refs/heads/master_ → _17a02998078923f2d62811326d130de991d1a95a_

Lorsque HEAD est détaché, il pointe directement vers un commit, au lieu de pointer indirectement vers un commit à travers une branche. Vous pouvez imaginer un détaché HEAD sur une branche non nommée.

  • _git symbolic-ref HEAD_ échoue avec _fatal: ref HEAD is not a symbolic ref_
  • _git rev-parse HEAD_ donne _17a02998078923f2d62811326d130de991d1a95a_
    Comme il ne s’agit pas d’une référence symbolique, il doit pointer directement vers le commit lui-même.

Nous avons HEAD → _17a02998078923f2d62811326d130de991d1a95a_

La chose importante à retenir avec un HEAD détaché est que si le commit sur lequel il pointe est par ailleurs non référencé (aucune autre référence ne peut l'atteindre), alors il deviendra "suspendu" lorsque vous extrayerez un autre commit. À terme, ces commits pendants seront supprimés via le processus de récupération de place (par défaut, ils sont conservés pendant au moins deux semaines et peuvent être conservés plus longtemps en étant référencés par le préfog de HEAD).

1 C’est parfaitement bien de faire un travail "normal" avec un HEAD détaché, il vous suffit de garder une trace de ce que vous faites pour éviter d’avoir à pêcher l’histoire laissée de côté.


Les étapes intermédiaires d’une rebase interactive sont effectuées avec un HEAD détaché (partiellement pour éviter de polluer le reflog de la branche active). Si vous terminez l'opération de refonte complète, il mettra à jour votre branche d'origine avec le résultat cumulatif de l'opération de refonte et rattachera HEAD à la branche d'origine. J'imagine que vous n'avez jamais complètement terminé le processus de rebase; cela vous laissera avec un HEAD détaché qui pointe vers le commit qui a été le plus récemment traité par l'opération de rebase.

Pour sortir de votre situation, vous devez créer une branche qui pointe vers le commit actuellement indiqué 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 branche temp.

Ensuite, vous devriez comparer le commit actuel (et son historique) avec la branche normale sur laquelle vous espériez 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 du journal: ajoutez _-p_, laissez _--pretty=…_ pour voir le message complet du journal, etc.)

Si votre nouvelle branche temp semble bonne, vous pouvez mettre à jour (par exemple) master pour y pointer:

_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'historique rétabli:

_git Push Origin master
_

Vous devrez peut-être ajouter _--force_ à la fin de cette commande en Push si la branche distante ne peut pas être "renvoyée rapidement" vers le nouveau commit (c’est-à-dire que vous avez supprimé, réécrit un commit existant, ou autrement, un bit de l'histoire).

Si vous êtes au milieu d’une opération de rebasement, vous devriez probablement le nettoyer. Vous pouvez vérifier si une base était en cours en recherchant le répertoire _.git/rebase-merge/_. Vous pouvez nettoyer manuellement la base en cours en supprimant simplement ce répertoire (par exemple, si vous ne vous souvenez plus de l'objectif et du contexte de l'opération de base active). Habituellement, vous utiliseriez _git rebase --abort_, mais cela réinitialisera de manière supplémentaire ce que vous voudrez probablement éviter (cela ramènera HEAD dans la branche d'origine et le réinitialisera à la livraison initiale, ce qui annulera certaines modifications. du travail que nous avons fait ci-dessus).

2400
Chris Johnsen

Faites juste ceci:

git checkout master

Ou, si vous souhaitez conserver des modifications, procédez comme suit:

git checkout -b temp
git checkout -B master temp
593
Daniel Alexiuc

J'ai rencontré ce problème et quand j'ai lu dans la réponse votée en haut:

HEAD est le nom symbolique du commit actuellement extrait.

J'ai pensé: ah-ha! Si HEAD est le nom symbolique de la validation actuelle, je peux le réconcilier avec master en le rebasonnant contre master:

git rebase HEAD master

Cette commande:

  1. vérifie master
  2. identifie les commits parents de HEAD vers le point HEAD divergé de master
  3. joue ces commits sur master

Le résultat final est que tous les commits qui étaient dans HEAD mais pas master sont alors aussi dans master. master reste extrait.


En ce qui concerne la télécommande:

quelques commits que j'avais tués dans la base ont été poussés, et les nouveaux commis localement ne sont pas là.

L'historique distant ne peut plus être transféré rapidement à l'aide de votre historique local. Vous aurez besoin de forcer Push (git Push -f) pour écraser l'historique à distance. Si vous avez des collaborateurs, il est généralement logique de coordonner cela avec eux pour que tout le monde soit sur la même page.

Après avoir poussé master vers la télécommande Origin, votre branche de suivi à distance Origin/master sera mise à jour pour indiquer le même commit que master.

119
Dmitry Minkovsky

Regardez ici pour l'explication de base de la tête détachée:

http://git-scm.com/docs/git-checkout

Ligne de commande pour le visualiser:

git branch

ou

git branch -a

vous obtiendrez une sortie comme ci-dessous:

* (no branch)
master
branch1

Le * (no branch) indique que vous êtes en tête détachée.

Vous auriez pu arriver à cet état en faisant un git checkout somecommit etc., qui vous aurait averti de ce qui suit:

Vous êtes dans l'état 'détaché HEAD'. Vous pouvez regarder autour de vous, apporter des modifications expérimentales et les valider. Vous pouvez également supprimer les commits que vous avez effectués dans cet état sans affecter les branches en effectuant une autre vérification.

Si vous souhaitez créer une nouvelle branche pour conserver les commits que vous créez, vous pouvez le faire (maintenant ou plus tard) en utilisant à nouveau -b avec la commande checkout. Exemple:

git checkout -b new_branch_name

Maintenant, pour les amener sur le maître:

Faites un git reflog ou même simplement git log et notez vos commits. Maintenant, git checkout master et git merge les validations.

git merge HEAD@{1}

Modifier:

Pour ajouter, utilisez git rebase -i non seulement pour supprimer/supprimer les commits dont vous n’avez pas besoin, mais également pour les éditer. Il vous suffit de mentionner "edit" dans la liste de validation pour pouvoir modifier votre commit, puis émettre un git rebase --continue pour continuer. Cela aurait fait en sorte que vous ne veniez jamais dans une tête détachée.

80
manojlds

Obtenez votre commettre détaché sur sa propre branche

Il suffit de lancer git checkout -b mynewbranch.

Puis exécutez git log et vous verrez que la validation est maintenant HEAD sur cette nouvelle branche.

31
Rose Perrone

si vous venez de maitriser la branche et que vous voulez revenir à "développer" ou à une fonctionnalité, procédez comme suit:

git checkout Origin/develop

Remarque: vérification Origin/develop.

Vous êtes dans l'état HEAD détaché. Vous pouvez regarder autour de vous, faire des modifications expérimentales et les valider, et vous pouvez supprimer tous les commits que vous avez effectués dans cet état sans affecter les branches en effectuant une autre vérification ...

ensuite

git checkout -b develop

Ça marche :)

21
amdev

Si vous voulez pousser votre détachement actuel HEAD (vérifiez git log avant), essayez:

git Push Origin HEAD:master

envoyer votre HEAD détaché dans la branche principale d’Origin. Si votre diffusion est rejetée, essayez d’abord git pull Origin master pour obtenir les modifications depuis Origin. Si vous ne vous souciez pas des modifications apportées par Origin et que celles-ci sont rejetées, car vous avez modifié intentionnellement votre base et que vous souhaitez remplacer Origin/master par votre branche actuellement isolée, vous pouvez le forcer (-f). Au cas où vous perdriez un accès aux validations précédentes, vous pouvez toujours exécuter git reflog pour voir l'historique de toutes les branches.


Pour revenir sur une branche principale tout en conservant les modifications, essayez les commandes suivantes:

git rebase HEAD master
git checkout master

Voir: Git: "Pas actuellement sur une branche." Existe-t-il un moyen facile de revenir sur une branche tout en conservant les modifications?

17
kenorb

Je viens de rencontrer ce problème aujourd'hui et je suis sûr de l'avoir résolu en procédant comme suit:

git branch temp
git checkout master
git merge temp

J'étais sur mon ordinateur de travail quand j'ai compris comment faire cela, et maintenant je rencontre le même problème sur mon ordinateur personnel. Il faudra donc attendre lundi, quand je serai de retour à l'ordinateur de travail, pour voir exactement comment je l'ai fait.

10
adamwineguy

Ce qui suit a fonctionné pour moi (en utilisant uniquement le maître de branche):

git Push Origin HEAD:master
git checkout master        
git pull

Le premier pousse le détaché HEAD vers l'Origine distant.

Le second passe au maître de branche.

Le troisième récupère le HEAD qui devient attaché au maître de branche.

Des problèmes peuvent survenir lors de la première commande si le Push est rejeté. Mais cela ne serait plus un problème de tête détachée, mais plutôt le fait que le détaché HEAD ne soit pas au courant de certains changements distants.

9
Daniel Porumbel

Si vous êtes complètement sûr que HEAD est le bon état:

git branch -f master HEAD
git checkout master

Vous ne pouvez probablement pas pousser vers Origin, car votre maître s’est écarté d’Origin. Si vous êtes certain que personne n'utilise le référentiel, vous pouvez forcer:

git Push -f

Le plus utile si vous êtes sur une branche de fonctionnalité que personne d'autre n'utilise.

8
geon

J'ai trouvé cette question lors de la recherche de You are in 'detached HEAD' state.

Après avoir analysé ce que j'avais fait pour arriver ici, par rapport à ce que j'avais fait dans le passé, j'ai découvert que j'avais commis une erreur.

Mon flux normal est:

git checkout master
git fetch
git checkout my-cool-branch
git pull

Cette fois j'ai fait:

git checkout master
git fetch
git checkout Origin/my-cool-branch
# You are in 'detached HEAD' state.

Le problème est que j'ai accidentellement fait:

git checkout Origin/my-cool-branch

Plutôt que:

git checkout my-cool-branch

Le correctif (dans ma situation) consistait simplement à exécuter la commande ci-dessus, puis à poursuivre le flux:

git checkout my-cool-branch
git pull
7
user664833

Tout ce que vous avez à faire est 'git checkout [nom de la branche]' où [nom de la branche] est le nom de la branche d'origine à partir de laquelle vous êtes entré dans un état de tête détachée. Le (détaché de asdfasdf) va disparaître.

Ainsi, par exemple, dans la branche "dev", vous extrayez le commit asdfasd14314 ->

'git checkout asdfasd14314'

vous êtes maintenant dans un état de tête détachée

'git branch' listera quelque chose comme ->

* (detached from asdfasdf)
  dev
  prod
  stage

mais pour sortir de l'état de tête détachée et revenir à dev ->

'git checkout dev'

puis 'git branch' listera ->

* dev
  prod
  stage

mais c’est bien sûr si vous n’avez pas l’intention de garder les changements de l’état de la tête détachée, mais je me trouve souvent en train de le faire.

6
Adam Freeman

Comme l'a souligné Chris, j'ai eu la situation suivante

git symbolic-ref HEAD échoue avec fatal: ref HEAD is not a symbolic ref

Cependant, git rev-parse refs/heads/master indiquait un bon commit d'où je pouvais récupérer (dans mon cas, le dernier commit et vous pouvez le voir en utilisant git show [SHA]

J'ai fait beaucoup de choses en désordre après cela, mais ce qui semble avoir été corrigé est juste,

git symbolic-ref HEAD refs/heads/master

Et la tête est re attachée!

6
Varun Garg

J'ai eu ce problème aujourd'hui, où j'avais mis à jour un sous-module, mais pas sur une branche. J'avais déjà commis, donc dissimulation, caisse, dépose ne fonctionnait pas. J'ai fini par choisir le pion de la tête détachée. Donc tout de suite après que j'ai commis (quand la poussée a échoué), j'ai fait:

git checkout master
git cherry-pick 99fe23ab

Ma pensée est allé: je suis sur une tête détachée, mais je veux être sur le maître. En supposant que mon état détaché ne soit pas très différent de maître, si je pouvais appliquer mon engagement à maître, je serais réglé. C'est exactement ce que fait cherry-pick.

4
prewett

Au lieu de faire git checkout Origin/master

il suffit de faire git checkout master

alors git branch confirmera votre succursale.

4
Goran Ch.

J'ai eu le même problème et je l'ai résolu en suivant les étapes suivantes.

Si vous devez conserver vos modifications

  1. Vous devez d’abord exécuter la commande git checkout master pour vous ramener à la branche principale.
  2. Si vous devez conserver vos modifications, exécutez simplement git checkout -b changes et git checkout -B master changes

Si vous n'avez pas besoin de vos changements

  1. Pour supprimer tous les fichiers non suivis de votre branche, exécutez git clean -df.

  2. Ensuite, vous devez effacer toutes les modifications non mises en scène dans votre référentiel. Pour ce faire, vous devez exécuter git checkout --

  3. Enfin, vous devez remettre votre branche dans la branche principale en utilisant la commande git checkout master.

3

Pour moi, cela a été aussi simple que de supprimer à nouveau la branche locale, car je n'avais pas d'engagement local que je veuille pousser:

Alors j'ai fait:

git branch -d branchname

Et puis vérifiant à nouveau la branche:

git checkout branchname
3
Klaus

Si vous avez fait des commits en plus du maître et que vous voulez juste "fusionner en arrière" master ici (c’est-à-dire que vous voulez que master pointe sur HEAD), le une ligne serait:

git checkout -B master HEAD
  1. Cela crée une nouvelle branche nommée master, même si elle existe déjà (ce qui revient à déplacer master et c'est ce que nous voulons).
  2. La branche nouvellement créée est définie pour pointer sur HEAD, où vous vous trouvez.
  3. La nouvelle branche est extraite, vous êtes donc sur master après.

J'ai trouvé cela particulièrement utile dans le cas des sous-dépôts, qui se trouvent également assez souvent dans un état isolé.

3
quazgar

Lorsque je me trouve personnellement dans une situation où il s'avère que j'ai apporté des modifications alors que je ne suis pas dans master (c.-à-d. HEAD est détaché juste au-dessus de master et il n'y a pas de commits dans entre) peut aider:

git stash # HEAD has same content as master, but we are still not in master
git checkout master  # switch to master, okay because no changes and master
git stash apply  # apply changes we had between HEAD and master in the first place
1
Ben Usman

En termes simples, l'état HEAD détaché signifie que vous n'êtes pas extrait de HEAD (ou de la pointe) d'une branche .

Comprendre avec un exemple

Une branche dans la plupart des cas est une séquence de commits multiples comme:

Commit 1: master -> branch_HEAD (123be6a76168aca712aea16076e971c23835f8ca)

Commit 2: master -> 123be6a76168aca712aea16076e971c23835f8ca -> branch_HEAD (100644a76168aca712aea16076e971c23835f8ca)

Comme vous pouvez le voir ci-dessus en cas de séquence de validations, votre branche pointe sur votre dernier validateur. Donc, dans ce cas, si vous passez à la caisse 123be6a76168aca712aea16076e971c23835f8ca alors vous seriez dans l’état de la tête détachée puisque HEAD de votre branche pointe sur 100644a76168aca712aea16076e971c23835f8ca à HEAD d'aucune branche. Par conséquent, vous êtes dans l'état détaché HEAD.

Explication théorique

Dans ce blog il est clairement indiqué qu'un référentiel Git est une arborescence de validations, chaque validation pointant vers son ancêtre avec chaque pointeur de validation est mise à jour et ces pointeurs vers chaque branche sont stockés dans le fichier .git/refs sous-répertoires. Les balises sont stockées dans .git/refs/tags et les branches sont stockées dans .git/refs/head. Si vous examinez l'un des fichiers, vous constaterez que chaque balise correspond à un fichier unique, avec un hachage de validation de 40 caractères et, comme expliqué ci-dessus par @Chris Johnsen et @Yaroslav Nikitenko, vous pouvez consulter ces références.

1
Keshav

Cela a fonctionné pour moi parfaitement:

1 .git stash pour sauvegarder vos modifications locales

Si vous souhaitez ignorer les modifications
git clean -df
git checkout -- .
git clean supprime tous les fichiers non suivis (avertissement: s'il ne supprime pas les fichiers ignorés mentionnés directement dans .gitignore, il peut supprimer les fichiers ignorés résidant dans des dossiers) et git checkout efface toutes les modifications non mises en scène.

2 .git checkout master pour basculer vers la branche principale (en supposant que vous souhaitiez utiliser le maître)
3 .git pull extraire le dernier commit de la branche master
4 .git status afin de tout vérifier

On branch master
Your branch is up-to-date with 'Origin/master'.
0
cepix

Dans mon cas, j'ai exécuté git status et j'ai constaté que j'avais quelques fichiers non suivis dans mon répertoire de travail.

Pour que le rebase fonctionne, il me suffisait de les nettoyer (car je n'en avais pas besoin).

0
falsarella

Je suis tombé dans un état vraiment stupide, je doute que quelqu'un d'autre trouve cela utile ... mais juste au cas où

git ls-remote Origin
0d2ab882d0dd5a6db93d7ed77a5a0d7b258a5e1b        HEAD
6f96ad0f97ee832ee16007d865aac9af847c1ef6        refs/heads/HEAD
0d2ab882d0dd5a6db93d7ed77a5a0d7b258a5e1b        refs/heads/master

que j'ai finalement fixé avec

git Push Origin :HEAD
0
KCD

Si vous utilisez EGit dans Eclipse: supposez que votre maître est votre branche de développement principale

  • vous commet des modifications dans une branche, normalement une nouvelle
  • puis tirez de la télécommande
  • puis cliquez avec le bouton droit sur le nœud du projet, choisissez l'équipe, puis choisissez Afficher l'historique
  • puis faites un clic droit sur le maître, choisissez
  • si Eclipse vous le dit, il y a deux maîtres: un local, un distant, choisissez la télécommande.

Après cela, vous devriez pouvoir vous reconnecter au maître d’origine.

0
Junchen Liu