Pourquoi la caisse git avec des références/têtes/branche explicites donne-t-elle la tête détachée?

si je vérifie une branche en utilisant seulement le nom de la branche, HEAD est mis à jour pour pointer cette branche.

$git checkout branch
Switched to branch 'branch'

si je vérifie une succursale en utilisant refs/heads/branch ou heads/branch , HEAD se détache.

$git checkout refs/heads/branch
Note: checking out 'refs/heads/branch'.

You 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 performing another checkout.

$git checkout "refs/heads/branch"
Same result

$git checkout heads/branch
Same result

pourquoi? Si sa version dépend, j'ai git 1.7.9.5 sur Ubuntu 12.04.3.

15
demandé sur darthbith 2014-01-11 00:48:38

2 réponses

la commande checkout distingue deux cas (en fait "beaucoup" , mais commençons par les deux : -)):

  • "je veux" get sur une branche", voilà un nom de branche": par exemple, git checkout branch .
  • "je veux regarder quelques révision particulière, et se 1519550920" off de toute branche, en voici un d'une branche d'un identifiant qui n'est pas un nom de branche": par exemple, git checkout 6240c5c .

(Personnellement, je pense que ceux-ci devraient utiliser des noms de commandes différents, mais ce n'est que moi. D'un autre côté, cela éviterait toutes les bizarreries décrites ci-dessous.)

maintenant, disons que vous voulez le premier. C'est plus facile à écrire en écrivant simplement le nom de la branche, sans la partie refs/heads/ .

si vous voulez cette dernière, vous pouvez spécifier une révision par l'une des méthodes énumérées dans gitrevisions , sauf pour toute méthode résultats dans "sur la branche".

pour quelque raison que ce soit, l'algorithme choisi ici-il est documenté dans la page de manuel , sous <branch> - est-ce: si vous avez écrit un nom que, en ajoutant refs/heads/ à elle, nomme une branche, git checkout vous mettra"sur cette branche". Si vous spécifiez @{-N} ou - , il va chercher la N - e branche plus ancienne en le HEAD refrog (avec - signifiant @{-1} ). Sinon, il choisit la deuxième méthode, en vous donnant la "tête détachée". ceci est vrai même si le nom est celui suggéré dans les dispositions de gitrevisions pour éviter l'ambiguïté, i.e., heads/xyz quand il y a un autre xyz . (Mais: vous pouvez ajouter --detach pour éviter le cas" get on a branch " même si autrement il serait sur la branche.)

cela contredit également les règles de résolution répertoriés dans le gitrevisions document. Pour démontrer ceci (bien qu'il soit difficile de voir), j'ai fait une étiquette et une branche avec le même nom, derp2 :

$ git checkout derp2
warning: refname 'derp2' is ambiguous.
Previous HEAD position was ...
Switched to branch 'derp2'

cela m'a mis sur la branche, plutôt que de me détacher et d'aller à la révision étiquetée.

$ git show derp2
warning: refname 'derp2' is ambiguous.
...

cela m'a montré la version étiquetée, la façon dont gitrevisions dit qu'il devrait.


une note de côté: "obtenir sur une branche" signifie vraiment "mettre une référence symbolique à un nom de branche dans le fichier nommé HEAD dans le répertoire git". La référence symbolique est le texte littéral ref: suivi du nom complet de la branche, par exemple refs/heads/derp2 . Il semble un peu incohérent que git checkout exige le nom sans la partie refs/heads/ afin d'ajouter la partie ref: refs/heads/ , mais c'est idiot pour vous. :-) Il peut y avoir une raison historique à cela: à l'origine, pour être une référence symbolique, le Le fichier HEAD était en fait un lien symbolique vers le fichier branche, qui était toujours un fichier. Ces jours-ci, en partie à cause de Windows et en partie juste à travers l'évolution du code, il a cette chaîne de caractères littérale ref: , et les références peuvent devenir "emballées" et donc pas disponible en tant que fichier séparé de toute façon.

à L'opposé, une " tête détachée "signifie réellement "mettre un SHA-1 brut dans le" fichier HEAD . Autre que d'avoir une valeur numérique dans ce fichier, git continue à se comporter de la même manière que lorsque "sur une branche": ajout d'un nouveau commit fonctionne toujours, avec le nouveau commit parent étant la commettre. Les fusions peuvent toujours être faites aussi bien, avec les parents de la commit de fusion étant le courant et les commits à fusionner. Le fichier HEAD est mis à jour avec chaque nouveau commit. 1 à tout moment vous pouvez créer une nouvelle étiquette de branche ou étiquette pointant vers le commit actuel, pour faire en sorte que la nouvelle chaîne de commits soit préservée contre le futur ramassage des ordures même après avoir éteint la "tête détachée"; ou vous pouvez simplement passer loin et laisser le nouveau commits, le cas échéant, obtenir pris avec la collecte des ordures habituelles. (Notez que le HEAD refrog empêchera cela pendant un certain temps, par défaut 30 jours je pense.)

[ 1 si vous êtes "sur une branche", la même mise à jour automatique se produit, il arrive juste à la branche que HEAD se réfère à . C'est, si vous êtes sur la branche B et vous ajoutez un nouveau commit, HEAD dit toujours ref: refs/heads/B , mais maintenant le commit-ID que vous obtenez avec git rev-parse B est le nouveau commit que vous venez d'ajouter. C'est ainsi que les branches "grandissent": les nouvelles propagations ajoutées alors que "sur la branche" font que la référence à la branche avance automatiquement. De même, dans cet état de "tête détachée", new commits added cause HEAD pour avancer automatiquement.]


pour être complet, voici une liste d'autres choses que git checkout peut faire, que j'aurais pu mettre dans diverses commandes séparées si j'avais eu de tels pouvoirs:

  • découvrez une version spécifique de certains chemins, en écrivant à travers l'index: git checkout revspec -- path ...
  • créer une nouvelle branche: git checkout -b newbranch (plus les options pour git branch )
  • créer une nouvelle branche, si et quand vous faites un commit, est une racine de commettre: git checkout --orphan (cela vous met "sur une branche" qui n'existe pas encore, i.e., écrit ref: refs/heads/branch-name dans HEAD mais ne crée pas la branche branch-name ; c'est aussi comme ça que master est une branche pas encore née dans un nouveau dépôt)
  • créer ou re-créer une opération de fusion ou de conflit de fusion: git checkout -m ...
  • résoudre un conflit de fusion en choisissant l'un ou l'autre "côté" de la fusion: git checkout --ours , git checkout --theirs
  • sélectionner interactivement les patches entre les objets du dépôt et les fichiers de l'arbre de travail, similaire à git add --patch : git checkout --patch
16
répondu torek 2014-01-11 01:25:19

vous n'êtes pas en train de vérifier une branche; vous ne faites que vérifier un commit qui se trouve être le chef d'une branche. Les Branches sont des pointeurs à Sens Unique: avec une branche, vous pouvez déterminer la propagation exacte qui est à la tête de cette branche, mais vous ne pouvez pas prendre une propagation arbitraire et déterminer de quelle(S) branche (s) elle est à la tête. Ainsi, si vous deviez faire un nouveau commit, Git ne saurait pas quelle branche mettre à jour, le cas échéant.

2
répondu chepner 2014-01-10 21:16:45