Git-Checkout une balise distante lorsque deux télécommandes ont le même nom de balise

j'avais espéré que cela marcherait:

git checkout remote/tag_name

mais ce n'est pas le cas. Cela fait:

git checkout tags/tag_name

mais je fais quelque chose de bizarre où j'ai beaucoup de télécommandes, et je m'inquiète de ce qui se passe si deux télécommandes ont la même étiquette. Est-il un moyen de spécifier la distance lors de la vérification de la balise?

43

4 réponses

résumé: ce que vous voulez atteindre est possible, mais d'abord vous devez inventer à distance des balises.

Vous faites cela avec une série de refspecs, un pour chaque distance. Le reste concerne ce qu'ils sont, comment ils fonctionnent, et ainsi de suite.


votre question demande à propos de la vérification d'une "étiquette à distance", mais Git ne ont étiquettes à distance, et ce:

mais je fais quelque chose de bizarre où j'ai beaucoup de télécommandes, et je m'inquiète de ce qui se passe si deux télécommandes ont la même étiquette. Est-il un moyen de spécifier la distance lors de la vérification de la balise?

révèle (je pense) la source de votre confusion.

revenons un instant en arrière et parlons simplement de ce que Git a dans un sens général, qui sont des"références". Pour aider à cimenter l'idée, des formulaires spécifiques des références de votre succursale locale noms ( master , devel , feature , et ainsi de suite), "à distance" de noms comme origin/master et stuff_from_bobs_computer/master , et les noms de balise. Des choses comme "stash" de Git utilisent aussi des références, et même HEAD est une référence, bien qu'elle soit très spéciale, et habituellement une référence "symbolique". Le point ici est que Git a beaucoup de formes de références, et elles fonctionnent toutes de la même manière à la fin: un nom de référence résout, à la fin, à l'une de ces grandes valeurs SHA-1, 676699a0e0cdfd97521f3524c763222f1c30a094 ou peu importe.

la plupart des références-les exceptions sont des choses comme HEAD , ORIG_HEAD , MERGE_HEAD , et quelques autres le long de ces lignes-sont en fait orthographiés avec des noms qui commencent par refs/ . Ceux-ci sont conservés dans une sorte de répertoire - ou la structure de chemise-comme, 1 avec les sous-répertoires: refs/tags/ contient vos étiquettes, 2 refs/heads/ contient tous vos branches, et refs/remotes/ contient toutes vos branches distantes.

les branches distantes sont ensuite subdivisées par le nom de la télécommande: refs/remotes/origin/ contient toutes les origin branches distantes, tandis que refs/remotes/stuff_from_bobs_computer/ contient toutes les stuff_from_bobs_computer branches distantes. Si vous avez beaucoup de télécommandes, vous avez beaucoup de sous-répertoires à l'intérieur de refs/remotes/ .

je viens de mentionner que vos étiquettes sont toutes dans refs/tags/ . Quel est le les étiquettes des télécommandes, toutes les étiquettes des différents télécommandes? Encore une fois, git n'a pas de "remote tags". Git possède des "branches éloignées", mais celles-ci sont en fait toutes locales. Ils sont stockés dans votre , sous la rubrique refs/remotes/ .

quand votre Git contacte un "remote" - habituellement par git fetch remote , mais aussi pour push (et l'étape initiale clone , d'ailleurs), votre Git demande distant Git 3 à la question: "Quelles branches avez-vous? Quelles sont leurs valeurs SHA-1?"C'est, en fait, comment fetch fonctionne: comme une description simplifiée, le processus de fetching consiste à demander au gitan distant "hey, whaddaya got?"et il vous donne un ensemble de noms et SHA-1s. Votre Git vérifie ensuite si elle a les mêmes SHA-1. Si c'est le cas, la conversation est terminée; si ce n'est pas le cas, votre Git dit alors: "ok, j'ai besoin de tout ce qu'il y a dans les commits pour ces SHA-1s", ce qui s'avère être en fait un autre groupe de SHA-1, et votre Git et le leur en parler pour comprendre quels fichiers et tels que vous avez besoin ainsi, tous identifiés par SHA-1s. Votre Git apporte ces objets, et empile les nouveaux SHA-1 dans votre refs/remotes/ , sous le nom de la télécommande et ensuite sous leurs noms de branche locale.

si vous demandez des étiquettes avec votre fetch , votre Git fait un peu plus. 4 au lieu de simplement demander leur avis sur leurs branches, votre Git demande aussi la leur sur leurs étiquettes. Encore une fois, leur Git vous donne juste une liste de noms et SHA-1s. Votre Git apporte alors tous les objets sous-jacents nécessaires, et puis-voici la clé de l'ensemble du problème-il écrit leurs noms d'étiquette dans votre refs/tags/ .

ainsi, ce qui se passe quand vous allez à distance origin et demandez-lui des étiquettes, et il dit "j'ai refs/tags/pinky et refs/tags/brain , est que cela crée , pour vous, des tags locaux pinky et brain , également nommés refs/tags/pinky et refs/tags/brain dans votre espace nom de référence.

maintenant, vous allez sur L'ordinateur de Bob (la télécommande nommée stuff_from_bobs_computer ci-dessus) et lui demander des étiquettes. Il est dans la neurologie, plutôt que Warner Brothers et soeur, et ses étiquettes sont refs/tags/spinal_cord et refs/tags/brain , et le second n'est probablement pas lié à celui sur origin . Uh oh!

Exactement ce qui se passe ici devient un peu compliqué", 15191310920" 5 mais en bref, c'est juste une mauvaise situation et vous devriez probablement éviter si possible. Il y en a deux faciles (bien...) des moyens pour l'éviter. Un, avec un inconvénient évident, est: ne pas obtenir leurs étiquettes. Alors vous n'aurez pas de conflits d'étiquettes. L'autre est: gardez toutes leurs étiquettes séparées les unes des autres (et peut-être aussi des vôtres). Il s'avère que la seconde n'est pas si difficile. Vous avez juste "inventer" à distance balises.

voyons un peu comment Git implémente" les branches distantes", et comment fetch --tags fonctionne. Ils utilisent tous les deux le même mécanisme de base, ce que git appelle "refspecs".

dans sa forme la plus simple , un refspec ressemble à deux noms de ref avec deux points entre eux: refs/heads/master:refs/heads/master , par exemple. En fait, vous pouvez même laisser de côté le refs/heads/ et Git le mettra pour vous, 6 et parfois, vous pouvez laisser le côlon et la répétition de nom. C'est le genre de chose que vous utilisez avec git push : git push origin branch signifie pousser à origin , en utilisant votre refs/heads/branch , et l'appeler refs/heads/branch quand il arrive sur "leur" gitan aussi.

pour fetch , bien que, faisant des branches éloignées, vous obtenez un refspec qui ressemble à ceci:

+refs/heads/*:refs/remotes/origin/*

Le + à l'avant signifie "force", et l' * s font la chose évidente. Votre Git parle aux leurs et obtient une liste d'arbitres. Ceux qui correspondent à refs/heads/* , le vôtre apporte plus (avec leurs objets de dépôt si nécessaire)-mais alors il les colle dans votre repo sous des noms regardant avec refs/remotes/origin/ , et maintenant vous avez toutes les" branches éloignées "de origin . 7

quand vous lancez git fetch --tags , votre git ajoute +refs/tags/*:refs/tags/* à la refspecs it utiliser. 8 qui apporte leurs étiquettes et les met dans vos étiquettes locales. Donc tout ce que vous avez à faire est de donner fetch un refspec qui ressemble à:

+refs/tags/*:refs/rtags/origin/*

et tout à coup vous aurez un tout nouveau nom-espace de" remote tags "sous refs/rtags/ (pour origin seulement, dans ce cas). Il est sûr d'utiliser le drapeau de force + ici puisque vous mettez juste à jour votre copie de leurs étiquettes: s'ils ont force-déplacé (ou supprimé et re-créé) une balise, vous force-déplacement de la copie. Vous pouvez aussi ou même plus besoin --no-tags comportement, que vous pouvez obtenir en spécifiant --no-tags sur la ligne de commande, ou, bien, voir le paragraphe suivant.

le seul élément pratique restant à savoir est que git fetch obtient ses refspecs par défaut, pour n'importe quelle télécommande, à partir du fichier de configuration Git. 9 si vous examinez votre fichier de configuration Git, vous verrez une ligne fetch = sous chaque télécommande, en utilisant la chaîne +refs/heads/*:refs/remotes/remote-name/* . Vous pouvez avoir autant de lignes fetch = que vous voulez par télécommande, donc vous pouvez en ajouter une pour apporter leurs tags, mais mettez-les dans votre nouvel-(re)inventé" tags à distance " nom-espace. Vous pouvez aussi faire de --no-tags la valeur par défaut de cette télécommande en positionnant tagOpt = --no-tags dans cette même section. Voir ce commentaire de l'user200783 pour plus de détails.

comme avec toutes les commandes Git qui résolvent un nom à un SHA-1 brut, vous pouvez puis git checkout par Full ref-name pour passer en mode "tête détachée" sur le SHA-1 correspondant:

git checkout refs/rtag/stuff_from_bobs_computer/spinal_cord

parce que Git n'a pas l'idée de" remote tags "intégré, vous devez épeler la forme longue (voir gitrevisions pour plus de détails).


1 en fait, c'est un vrai répertoire, dans .git/refs . Cependant, il y a aussi un formulaire "emballé" pour les arbitres., ça se termine en .git/packed-refs . La forme emballée est destinée à économiser du temps et de l'effort avec des références qui ne changent pas souvent (ou du tout, comme c'est commun avec les étiquettes). Il y a aussi un effort continu pour réécrire le système de stockage "back end" pour les références, donc à un moment donné, une grande partie de cela peut changer. Ce changement est nécessaire pour les systèmes Windows et Mac. Git croit que les noms de branche et d'étiquette sont sensibles à la casse: que vous pouvez avoir la branche polish pour votre Matériau de cirage, et Polish pour votre saucisse. Les versions emballées sont sensible à la casse, donc cela fonctionne; mais les versions stockées dans les fichiers parfois ne sont pas , donc ça ne fonctionne pas!

2 je fais la différence entre les étiquettes légères et annotées. Les étiquettes annotées sont des objets réels dans le dépôt, tandis que les étiquettes légères sont des étiquettes dans l'espace refs/tags/ . Toutefois, en général, chaque étiquette annotée comporte une étiquette légère correspondante, donc pour cet usage particulier, ils travaillent sur la même chose.

3 c'est presque toujours un autre git repo, bien qu'il y ait maintenant des adaptateurs pour Git à Mercurial, svn, et ainsi de suite. Ils ont leurs propres trucs pour faire semblant d'être des git repos. De plus, cette description ne se veut pas définitive: la séquence réelle des opérations est codée pour l'efficacité du transfert, plutôt que pour le sens-à-l'homme.

4 j'ai glissé sur un peu de bizarrerie spéciale au sujet de plaine fetch et clone ici, c.-à-d., les versions sans --tags . Les versions avec --tags sont faciles à expliquer: elles apportent toutes les étiquettes en utilisant les refspecs que j'ai décrits ici-et, au moins dans Git 2.10 et 2.11, --tags fait aussi des mises à jour forcées, comme si le drapeau de force + était placé. Mais sauf si vous demandez explicitement --no-tags , un simple fetch (et clone) apporte plus de quelques tags. La chose sournoise qu'il fait est de chercher des tags qui correspondent à des objets qui viennent à cause du fetch, et il ajoute ceux-ci (sans forcer les mises à jour) à votre nom-espace (global) tags. Sans --tags , votre Git n'écrasera pas vos propres étiquettes existantes; avec --tags , votre git écrasera vos propres étiquettes existantes, au moins dans Git 2.10, par des expériences réelles réalisée au début de 2017.

5 les versions plus anciennes de Git appliquaient les règles" branche "aux étiquettes pendant push (mais pas nécessairement fetch), permettant une mise à jour d'étiquette si c'était un fast-forward, et exigeant autrement le drapeau de force. La nouvelle version de git push nécessite juste l'étiquette de force. Le fetch refspec from --tags n'a pas le drapeau de la force, mais agit comme s'il le faisait. J'ai non expérimenté avec push avec --tags . Il y a une autre bizarrerie git fetch à propos de --tags vs --no-tags vs refspecs explicites, en rapport avec la façon dont --prune fonctionne. La documentation dit que --prune s'applique à toute ligne de commande explicite refs/tags/ refspecs, mais pas à la implicite --tags refspecs. Je n'ai pas expérimenté pour vérifier cela, soit.

6 pour que votre Git remplisse refs/heads/ ou refs/tags/ pour vous, votre Git doit être en mesure de figure que vous avez voulu dire. Il y a des cas où il le fait, et d'autres où il ne le fait pas. Si votre Git ne parvient pas à le comprendre, vous recevrez un message d'erreur, et vous pourrez réessayer avec ce message rempli-mais dans les scripts, vous devriez toujours le remplir explicitement, pour obtenir un comportement plus prévisible. Si vous utilisez simplement git push pour pousser une branche existante, vous pouvez presque toujours laisser votre Git le hors.

7 en laissant de côté les deux-points et le second nom ne fonctionne pas aussi bien pour git fetch : il indique à votre Git de ne pas mettre à jour vos propres références! Cela semble insensé, mais en fait peut être utile, parce que git fetch toujours écrit le dossier spécial FETCH_HEAD . Vous pouvez extraire les ID d'objet Git (SHA-1s) du fichier spécial et voir ce qui a été récupéré. C'est la plupart du temps un hold-over des versions très anciennes de Git, avant que les branches de suivi à distance aient été inventées.

8 la refspec que git fetch --tags et git push --tags utilise est pré-compilée en interne, dans la version 2.10 de Git, et traitée par un code de cas spécial. La forme pré-compilée n'a pas le drapeau + ; pourtant l'expérimentation montre que les étiquettes récupérées sont mises à jour de force dans Git 2.10/2.11. Je me souviens avoir expérimenté Git 1 Il y a des années.x, et de constater que ces étiquettes --tags - fetched étaient pas force-mis à jour, donc je pense que cela a changé, mais cela peut être juste la mémoire défectueuse. Dans tous les cas, si vous êtes (re)en train d'inventer des tags distants, vous faites très probablement et non vous voulez utiliser un --tags explicite .

9 en fait, c'est ainsi que fonctionnent les miroirs. Par exemple, avec fetch = +*:* vous obtenez un miroir fetch pur. Le processus d'extraction pouvez voir toutes les refs. Vous pouvez les voir vous-même avec git ls-remote . C'est aussi comment --single-branch fonctionne: si vous utilisez --single-branch pendant le clonage, votre fichier de configuration Git ne listera qu'une seule branche dans la ligne fetch. Pour passer d'une seule branche à toutes les branches, il suffit d'éditer la ligne pour contenir l'entrée habituelle glob-pattern.

71
répondu torek 2017-05-23 11:54:17

1 - aller Chercher la balise à partir de la télécommande avec:

git fetch origin --tags 

Ou, à la caisse d'une étiquette à partir d'une autre utilisation à distance:

git fetch your_remote --tags

2 Vérifiez l'étiquette en exécutant

git checkout tags/<tag_name>

plus ici: télécharger une étiquette spécifique avec Git

81
répondu Russell Fair 2017-05-23 12:32:35

dans mon cas, lorsqu'une nouvelle étiquette a été ajoutée au dépôt distant [j'utilise Stash], la nouvelle étiquette n'était pas disponible dans le résultat de git tag -l .

Mais j'ai pu voir la nouvelle étiquette ajoutée en utilisant git ls-remote --tags .

J'ai dû exécuter la commande suivante pour obtenir toutes les dernières balises de mon dépôt local:

git pull --tags L'exécution de git tag -l affiche maintenant les nouvelles étiquettes ajoutées ainsi.

pour pour vérifier une étiquette, utilisez:

git checkout <tag_name>

Note: il est normal d'exécuter l'état git et de trouver un message comme celui-ci:

HEAD detached at tag_name

9
répondu hipsandy 2015-04-20 16:43:09

il y a quelques questions dans mon esprit:

  • pourquoi des télécommandes différentes auraient-elles un code différent (dans le même arbre)?
  • pourquoi le code à distance vous affecte-t-il en vérifiant les étiquettes?

La chose est la suivante:

lorsque vous cochez une balise à l'aide de git checkout tags/fancytag , elle cherchera dans votre dépôt actuel (sur votre machine) la balise appropriée.

si vous voulez pour vérifier une étiquette à partir d'une télécommande spécifique, vous devez fetch it (l'arbre de la télécommande spécifique) d'abord et puis vérifier.

0
répondu Langusten Gustel 2014-03-01 00:52:39