Comment est défini l'origine / HEAD?

144

J'ai une branche mise en place pour suivre une référence à l'origine. git checkout <branchname>bascule sur cette branche, et un git statusme montrera à quelle distance en avant ou en arrière ma branche se trouve par rapport à l'origine, mais je suis surpris que cela origin/HEADpointe toujours origin/master, et nonorigin/<branchname>

Ma question est donc la suivante: dans quelles circonstances l'origine / la tête est-elle déplacée?

ÉDITER:

J'apprécie les réponses sur la façon de déplacer l'origine / HEAD, mais je m'intéresse à ce qui la déplace "organiquement", en dehors de moi en lui disant explicitement de le faire.

Par exemple, lorsque je change de branche, git fait pointer HEAD sur la branche que je vérifie, donc je suis surpris que origin / HEAD ne bouge pas de la même manière.

Ecoffey
la source
Notez que cette question concerne les références symboliques locales sur les télécommandes, comme refs/origin/HEAD. Il ne s'agit pas de la façon dont la propre référence symbolique d'un référentiel HEADest définie.
clacke

Réponses:

173

Notez d'abord que votre question montre un peu de malentendu. origin / HEAD représente la branche par défaut sur le distant , c'est-à-dire le HEAD qui se trouve dans ce référentiel distant que vous appelez origin. Lorsque vous changez de succursale dans votre dépôt, cela ne change rien. Il en va de même pour les succursales éloignées; vous pourriez avoir masteret origin/masterdans votre référentiel, où origin/masterreprésente une copie locale de la masterbranche dans le référentiel distant.

HEAD d'origine ne changera que si vous ou quelqu'un d'autre le modifiez réellement dans le référentiel distant , ce qui ne devrait pratiquement jamais se produire - vous voulez que la branche par défaut d'un dépôt public reste constante, sur la branche stable (probablement master). origin / HEAD est une référence locale représentant une copie locale de HEAD dans le référentiel distant. (Son nom complet est refs / télécommandes / origine / HEAD.)

Je pense que ce qui précède répond à ce que vous vouliez réellement savoir, mais pour aller de l'avant et répondre à la question que vous avez explicitement posée ... origin / HEAD est défini automatiquement lorsque vous clonez un référentiel, et c'est à peu près tout. Bizarrement, ce n'est pas défini par des commandes telles que git remote update- je pense que la seule façon dont cela changera est si vous le modifiez manuellement. (Par changement, je veux dire pointer vers une branche différente; évidemment, le commit indique des changements si cette branche change, ce qui peut se produire lors de la récupération / extraction / mise à jour à distance.)


Edit : Le problème discuté ci-dessous a été corrigé dans Git 1.8.4.3 ; voir cette mise à jour .


Il y a cependant une petite mise en garde. HEAD est une référence symbolique, pointant vers une branche au lieu de directement vers un commit, mais les protocoles de transfert à distance git ne rapportent que les validations pour les refs. Donc Git connaît le SHA1 du commit pointé par HEAD et toutes les autres références; il doit ensuite déduire la valeur de HEAD en trouvant une branche qui pointe vers le même commit. Cela signifie que si deux branches pointent là, c'est ambigu. (Je crois qu'il choisit le maître si possible, puis revient au premier par ordre alphabétique.) Vous verrez cela signalé dans la sortie de git remote show origin:

$ git remote show origin
* remote origin
  Fetch URL: ...
  Push  URL: ...
  HEAD branch (remote HEAD is ambiguous, may be one of the following):
    foo
    master

Curieusement, bien que la notion de HEAD imprimée de cette façon changera si les choses changent sur la télécommande (par exemple si foo est supprimé), elle ne se met pas à jour refs/remotes/origin/HEAD. Cela peut conduire à des situations vraiment étranges. Dites que dans l'exemple ci-dessus, origin / HEAD pointait en fait vers foo, et la branche foo de l'origine a ensuite été supprimée. Nous pouvons alors faire ceci:

$ git remote show origin
...
HEAD branch: master
$ git symbolic-ref refs/remotes/origin/HEAD
refs/remotes/origin/foo
$ git remote update --prune origin
Fetching origin
 x [deleted]         (none)     -> origin/foo
   (refs/remotes/origin/HEAD has become dangling)

Ainsi, même si Remote Show sait que HEAD est maître, il ne met rien à jour. La branche foo périmée est correctement élaguée et HEAD devient pendante (pointant vers une branche inexistante), et elle ne la met toujours pas à jour pour pointer vers master. Si vous voulez résoudre ce problème, utilisez git remote set-head origin -a, qui détermine automatiquement HEAD d'origine comme ci-dessus, puis définit en fait l'origine / HEAD pour qu'il pointe vers la branche distante appropriée.

Cascabel
la source
@jefromi Réponse géniale! Juste une remarque: vous écrivez que HEAD est une référence symbolique, pointant vers une branche plutôt que directement vers un commit [...] , mais il peut être intéressant de mentionner "l'état HEAD détaché", pour être complet.
jub0bs
2
@Jubobs Merci! Si ma réponse doit être mise à jour, n'hésitez pas à la modifier simplement - cela permettra certainement aux gens de gagner du temps pour lire un bref résumé de la façon dont les choses fonctionnent réellement, plutôt que d'avoir à trier ce qui était vrai il y a deux ans et ce qui est vrai maintenant .
Cascabel
J'ai
7
git remote set-head origin -aa fait le travail pour moi
Shujito
75

C'est votre paramètre en tant que propriétaire de votre repo local. Changez-le comme ceci:

git remote set-head origin some_branch

Et origin / HEAD pointera vers votre branche au lieu de master. Cela s'appliquerait alors uniquement à votre repo et non aux autres. Par défaut, il pointera vers master, à moins que quelque chose d'autre n'ait été configuré sur le référentiel distant.

La saisie manuelle du décodeur à distance fournit de bonnes informations à ce sujet.

Edit: pour souligner: sans que vous le lui disiez, le seul moyen de "bouger" serait un cas comme le changement de nom de la branche maître , ce que je ne pense pas être considéré comme "organique". Donc, je dirais qu'organiquement ça ne bouge pas.

eis
la source
1
L'accent de modification n'est pas tout à fait correct ici. Cela peut également changer si vous clonez à partir d'une copie locale qui n'est pas sur la branche principale.
mphair
Je ne considère pas qu'un clone "bouge", mais je suppose que nous pouvons être en désaccord là-dessus :)
eis le
24

Qu'est-ce qui fait bouger l'origine / TETE "organiquement"?

  • git clone le place une fois à l'endroit où HEAD est à l'origine
    • il sert de branche par défaut à vérifier après le clonage avec git clone

Que représente HEAD sur l'origine?

  • sur des référentiels nus (souvent des référentiels «sur des serveurs»), il sert de marqueur pour la branche par défaut, car l' git cloneutilise de telle manière
  • sur des référentiels non nus (locaux ou distants), il reflète l'extraction actuelle du référentiel

Qu'est-ce qui définit l'origine / HEAD?

  • git clone le récupère et le définit
  • il serait logique de le mettre à git fetchjour comme toute autre référence, mais ce n'est pas le cas
  • git remote set-head origin -a le récupère et le définit
    • utile pour mettre à jour la connaissance locale de ce que remote considère comme la «branche par défaut»

Trivia

  • origin/HEAD peut également être réglé sur n'importe quelle autre valeur sans contacter la télécommande: git remote set-head origin <branch>
    • Je ne vois aucun cas d'utilisation pour cela, sauf pour les tests
  • malheureusement, rien ne peut régler HEAD sur la télécommande
  • les anciennes versions de git ne savaient pas sur quelle branche HEAD pointe sur la télécommande, seulement quel hachage de commit il a finalement: donc il a juste choisi un nom de branche pointant vers le même hachage
Robert Siemer
la source
J'avais perdu la référence origin/HEADet votre solution m'a aidé. Merci!
java_dude
Je ne suis pas d'accord avec la git fetchmise à jour, car elle permet de configurer un raccourci (local). Citant le document: "Avoir une branche par défaut pour une télécommande n'est pas obligatoire, mais permet de spécifier le nom de la télécommande à la place d'une branche spécifique". Il serait étrange qu'une modification à distance mette à jour les raccourcis configurés localement.
Micha Wiedenmann
@MichaWiedenmann Pourquoi serait-ce un raccourci configuré localement? Pour un raccourci configuré localement, origin/HEADc'est un mauvais nom. Et qui git cloneutilise un nom distant par défaut pour une «branche configurée localement» contredit cela également. Sur les référentiels non nus, il n'a même pas de sens d'utiliser le courant de remote HEAD.
Robert Siemer le
10

Avertissement : il s'agit d'une mise à jour de la réponse de Jefromi , que j'écris pour gagner du temps aux curieux.

J'ai essayé en vain de reproduire (dans Git 2.0.1) le remote HEAD is ambiguousmessage que Jefromi mentionne dans sa réponse; j'ai donc creusé un peu (en clonant https://github.com/git/git et en recherchant le journal). C'était autrefois ça

Determining HEAD is ambiguous since it is done by comparing SHA1s.

In the case of multiple matches we return refs/heads/master if it
matches, else we return the first match we encounter. builtin-remote
needs all matches returned to it, so add a flag for it to request such.

(Commit 4229f1fa325870d6b24fe2a4c7d2ed5f14c6f771, daté du 27 février 2009, trouvé avec git log --reverse --grep="HEAD is ambiguous")

Cependant, l'ambiguïté en question a depuis été levée :

One long-standing flaw in the pack transfer protocol used by "git
clone" was that there was no way to tell the other end which branch
"HEAD" points at, and the receiving end needed to guess.  A new
capability has been defined in the pack protocol to convey this
information so that cloning from a repository with more than one
branches pointing at the same commit where the HEAD is at now
reliably sets the initial branch in the resulting repository.

(Commit 9196a2f8bd46d36a285bdfa03b4540ed3f01f671, daté du 8 novembre 2013, trouvé avec git log --grep="ambiguous" --grep="HEAD" --all-match)

Edit (merci à torek ):

$ git name-rev --name-only 9196a2f8bd46d36a285bdfa03b4540ed3f01f671
tags/v1.8.4.3~3

Cela signifie que, si vous utilisez Git v1.8.4.3 ou une version ultérieure , vous ne devriez pas rencontrer de problème HEAD à distance ambiguë.

jub0bs
la source
1
Basé sur les balises de la source git, ce correctif s'applique à git version 1.8.4.3 et ultérieure.
torek
@RobertSiemer Je ne suis pas sûr, mais je pense que oui, oui.
jub0bs
8

N'oubliez pas qu'il y a deux dépôts git indépendants dont nous parlons. Votre dépôt local avec votre code et la télécommande s'exécutant ailleurs.

Vous avez raison, lorsque vous changez de branche, HEAD pointe vers votre branche actuelle. Tout cela se passe sur votre dépôt git local. Pas le dépôt distant, qui pourrait appartenir à un autre développeur, ou un emplacement sur un serveur de votre bureau, ou github, ou un autre répertoire sur le système de fichiers, ou etc ...

Votre ordinateur (dépôt local) n'a pas à changer le pointeur HEAD sur le dépôt git distant. Il pourrait appartenir à un développeur différent par exemple.

Une dernière chose, ce que votre ordinateur appelle origin / XXX est la compréhension de votre ordinateur de l'état de la télécommande au moment de la dernière récupération.

Alors, qu'est-ce qui mettrait à jour "organiquement" l'origine / HEAD? Ce serait une activité sur le repo git distant. Pas votre repo local.

Les gens ont mentionné

git ref symbolique HEAD refs / head / my_other_branch

Normalement, cela est utilisé lorsqu'il existe un référentiel git central partagé sur un serveur pour une utilisation par l'équipe de développement. Ce serait une commande exécutée sur l'ordinateur distant. Vous verriez cela comme une activité sur le repo git distant.

Pablo Maurin
la source
1
Désolé, si je suis un peu répétitif. Je veux juste souligner le fait que git est un système de contrôle de version distribué, et en tant que tel, les deux dépôts sont indépendants.
Pablo Maurin
3

Exécutez les commandes suivantes à partir de git CLI:

# move to the wanted commit
git reset --hard <commit-hash> 

# update remote
git push --force origin <branch-name> 
Yakir GIladi Edry
la source
1
Génial, c'est ce qui m'a aidé!
Shay Zambrovski