web-dev-qa-db-fra.com

Comment mettre à jour un clone peu profond git?

Contexte

(pour tl; dr, voir #questions ci-dessous)

J'ai plusieurs clones superficiels de référentiel git. J'utilise des clones peu profonds car il est beaucoup plus petit qu'un clone profond. Chacun est cloné faisant environ git clone --single-branch --depth 1 <git-repo-url> <dir-name>.

Cela fonctionne bien, sauf que je ne vois pas comment le mettre à jour.

Lorsque je clone par une balise, la mise à jour n'a pas de sens, car une balise est figée à un moment donné (si je comprends bien). Dans ce cas, si je veux mettre à jour, cela signifie que je veux cloner par une autre balise, donc je viens de rm -rf <dir-name> et cloner à nouveau.

Les choses se compliquent lorsque j'ai cloné le HEAD d'une branche principale, puis que je veux le mettre à jour plus tard.

J'ai essayé git pull --depth 1 mais bien que je ne doive rien envoyer au référentiel distant, il se plaint de ne pas savoir qui je suis.

J'ai essayé git fetch --depth 1, mais bien qu'il semble mettre à jour quelque chose, j'ai vérifié qu'il n'est pas à jour (certains fichiers du référentiel distant ont un contenu différent de ceux de mon clone).

Après https://stackoverflow.com/a/20508591/279335 , j'ai essayé git fetch --depth 1; git reset --hard Origin/master, mais deux choses: d'abord je ne comprends pas pourquoi git reset est nécessaire, deuxièmement, bien que les fichiers semblent être à jour, certains anciens fichiers restent, et git clean -df ne supprime pas ces fichiers.

Des questions

Laissez un clone créé avec git clone --single-branch --depth 1 <git-repo-url> <dir-name>. Comment le mettre à jour pour obtenir le même résultat que rm -rf <dir-name>; git clone --single-branch --depth 1 <git-repo-url> <dir-name>? Ou est rm -rf <dir-name> et cloner à nouveau la seule façon?

Remarque

Ce n'est pas un doublon de Comment mettre à jour un sous-module cloné peu profond sans augmenter la taille du référentiel principal , car la réponse ne répond pas à mes attentes et j'utilise des référentiels simples, pas des sous-modules (que je don sais pas).

25
Hibou57

[légèrement reformulé et formaté] Étant donné un clone créé avec _git clone --single-branch --depth 1 urldirectory_, comment puis-je le mettre à jour pour obtenir le même résultat que _rm -rf directory; git clone --single-branch --depth 1 urldirectory_?

Notez que _--single-branch_ est ledefaultlorsque vous utilisez _--depth 1_. La branche (unique) est celle que vous donnez avec _-b_. Il y a un long côté qui va ici à propos de l'utilisation de _-b_ avec des balises, mais je laisserai cela pour plus tard. Si vousne pasutilisez _-b_, votre Git demande au Git "amont" - le Git àurl- quelle brancheitest sorti et prétend que vous avez utilisé _-b thatbranch_. Cela signifie qu'il est important d'être prudent lorsque vous utilisez _--single-branch_sans_-b_ pour vous assurer que la branche actuelle de ce référentiel en amont est sensible, et bien sûr, lorsque vousdoutilisez _-b_, pour vous assurer que l'argument de branche que vous donnez nomme vraiment une branche, pas une balise.

La réponse simple est essentiellement celle-ci, avec deux légères modifications:

Après https://stackoverflow.com/a/20508591/279335 , j'ai essayé _git fetch --depth 1; git reset --hard Origin/master_, mais deux choses: d'abord je ne comprends pas pourquoi _git reset_ est nécessaire, ensuite , bien que les fichiers semblent être à jour, certains anciens fichiers restent et _git clean -df_ ne supprime pas ces fichiers.

Les deux légères modifications sont les suivantes: assurez-vous d'utiliser _Origin/branchname_ à la place et ajoutez _-x_ (_git clean -d -f -x_ ou _git clean -dfx_) à l'étape _git clean_. Quant àpourquoi, cela devient un peu plus compliqué.

Que se passe-t-il

Sans_--depth 1_, l'étape _git fetch_ appelle l'autre Git et en obtient une liste de noms de branche et les ID de hachage de validation correspondants. Autrement dit, il trouve une liste deallles branches en amont et leurs validations actuelles. Ensuite, parce que vous avez un référentiel _--single-branch_,votreGit jette tout sauf la branche unique, et apporte tout ce dont Git a besoin pour reconnecter ce commit actuel aux commit (s) vous avez déjà dans votre référentiel.

Avec_--depth 1_, votre Git ne prend pas la peine de connecter le nouveau commit à des commit historiques plus anciens. Au lieu de cela, il obtient uniquement un commit et les autres objets Git nécessaires pour terminer ce commit. Il écrit ensuite une entrée "greffe superficielle" supplémentaire pour marquer ce commit comme un nouveau commit pseudo-root.

Clonage et extraction réguliers (non superficiels)

Tout cela est lié à la façon dont Git se comporte lorsque vous utilisez un clone normal (non superficiel, non à une seule branche): _git fetch_ appelle le Git en amont, obtient une liste de tout, puis apportetout ce que vous n'avez pas déjà. C'est pourquoi un clone initial est si lent, et une extraction à mettre à jour est généralement si rapide: une fois que vous obtenez un clone complet, les mises à jour ont rarement beaucoup à apporter: peut-être quelques commits, peut-être quelques centaines, et la plupart de ces commits n'ont pas besoin d'autre chose non plus.

L'historique d'un référentiel est formé à partir des commits. Chaque commit nomme sonparentcommit (ou pour les fusions, les commits parents, au pluriel), dans une chaîne qui remonte du "dernier commit", au commit précédent, à un commit plus ancestral , etc. La chaîne s'arrête finalement lorsqu'elle atteint une validation sans parent, comme la première validation jamais effectuée dans le référentiel. Ce type de commit est unrootcommit.

Autrement dit, nous pouvons dessiner un graphique des commits. Dans un référentiel vraiment simple, le graphique est juste une ligne droite, avec toutes les flèches pointant vers l'arrière:

_o <- o <- o <- o   <-- master
_

Le nom master pointe vers le quatrième et dernier commit, qui pointe vers le troisième, qui pointe vers le second, qui pointe vers le premier.

Chaque commit contient un instantané complet de tous les fichiers qui vont dans ce commit. Les fichiers qui ne sont pas du tout modifiés sontsharedentre ces validations: le quatrième commit "emprunte" simplement la version inchangée du troisième commit, qui "l'emprunte" au second, et ainsi de suite. Par conséquent, chaque commit nomme tous les "objets Git" dont il a besoin, et Git trouve ces objets localement, car il les a déjà, ou utilise le protocole fetch pour les faire passer de l'autre Git en amont. Il existe un format de compression appelé "emballage", et une variante spéciale pour le transfert réseau appelée "paquets légers", qui permet à Git de faire encore mieux/plus sophistiqué, mais le principe est simple: Git a besoin de tous, et seulement, des objets qui vont avec les nouveaux engagements, il reprend. Votre Git décide s'il a ces objets, et sinon, il les obtient de son Git.

Un graphique plus compliqué et plus complet comporte généralement plusieurs points où il se ramifie, certains où il fusionne et plusieurs noms de branche pointant vers différents conseils de branche:

_        o--o   <-- feature/tall
       /
o--o--o---o    <-- master
    \    /
     o--o      <-- bug/short
_

Ici, la branche _bug/short_ est de nouveau fusionnée dans master, tandis que la branche _feature/tall_ est toujours en cours de développement. Lenom_bug/short_ peut (probablement) maintenant être entièrement supprimé: nous n'en avons plus besoin si nous avons fini de faire des commits dessus. La validation à la pointe de master nomsdeuxles validations précédentes, y compris la validation à la pointe de _bug/short_, donc en récupérant master nous allons récupérer les validations _bug/short_.

Notez que le graphique simple et légèrement plus compliqué n'a chacun qu'un commit racine. C'est assez typique: tous les référentiels qui ont des commits ontau moinsun commit racine, puisque le tout premier commit est toujours un commit root; mais la plupart des référentiels ont aussiuniquementun commit racine. Vous pouvez cependant avoir des validations racine différentes, comme avec ce graphique:

_ o--o
     \
o--o--o   <-- master
_

ou celui-ci:

_ o--o     <-- Orphan

o--o      <-- master
_

En fait, celui avec juste celui master a probablement été créé en fusionnant Orphan dans master, puis en supprimant le nom Orphan.

Greffes et remplacements

Git a depuis longtemps un support (peut-être fragile) pourgreffes, qui a été remplacé par un support (bien meilleur, en fait solide) pour le génériqueremplacements. Pour les saisir concrètement, nous devons ajouter à ce qui précède la notion que chaque commit a son propre identifiant unique. Ces ID sont les gros hachages SHA-1 de 40 caractères, _face0ff..._ et ainsi de suite. En fait,everyGit object a un ID unique, bien que pour les besoins du graphe, nous nous soucions uniquement des commits.

Pour dessiner des graphiques, ces gros identifiants de hachage sont trop pénibles à utiliser, nous pouvons donc utiliser des noms à une lettre A à Z à la place. Utilisons à nouveau ce graphique, mais mettons des noms à une lettre:

_        E--H   <-- feature/tall
       /
A--B--D---G    <-- master
    \    /
     C--F      <-- bug/short
_

La validation H renvoie à la validation E (E est leparentde H). Valider G, qui est un commitmerge- ce qui signifie qu'il a au moins deux parents - renvoie à la fois à D et F, et bientôt.

Notez que la branchenames, _feature/tall_, master et _bug/short_, chaque point versun seul commit. Le nom _bug/short_ pointe pour valider F. C'est pourquoi la validation F est sur la branche _bug/short_ ... mais la validation C aussi. La validation C est sur _bug/short_ car elle estaccessibleà partir du nom. Le nom nous amène à F et F nous amène à C, donc C est sur la branche _bug/short_.

Notez cependant que la validation G, la pointe de master, nous oblige à la validation F. Cela signifie que la validation F estégalementsur la branche master. Ceci est un concept clé dans Git: les commits peuvent être surone,many, ou mêmenobranches. Un nom de branche est simplement un moyen de démarrer dans un graphe de validation. Il existe d'autres façons, telles que les noms de balises, _refs/stash_ (qui vous amène à la cachette actuelle: chaque cachette est en fait un couple de validations), et les reflogs (qui sont normalement cachés à la vue car ils sont normalement juste encombrés ).

Cependant, cela nous amène également à des greffes et des remplacements. Une greffe n'est qu'un type de remplacement limité etshallowles référentiels utilisent une forme de greffe limitée.1 Je ne décrirai pas complètement les remplacements ici car ils sont un peu plus compliqués, mais en général, ce que Git fait pour tous ces éléments est d'utiliser le greffon ou le remplacement comme un "au lieu de". Pour le cas spécifique decommits, ce que nous voulons ici est de pouvoir changer - ou au moins,faire semblantpour changer - l'ID parent ou les ID de tout commit ... et pourshallowrepositories, nous voulons pouvoir prétendre que le commit en question anoparents.


1La façon dont les référentiels superficiels utilisent le code de greffe estnottremblante. Pour le cas plus général, j'ai recommandé d'utiliser _git replace_ à la place, car c'était également et estnottremblant. La seule utilisation recommandée pour les greffes est - ou du moins l'était il y a des années - pour les mettre en place juste assez longtemps pour exécuter _git filter-branch_ verscopieune histoire modifiée - greffée -, après que vous devez simplement jeter entièrement l'histoire greffée. Vous pouvez également utiliser _git replace_ à cette fin, mais contrairement aux greffes, vous pouvez utiliser _git replace_ de façon permanente ou semi-permanente,sansnécessitant _git filter-branch_.


Faire un clone peu profond

Pour créer un clone superficiel de profondeur 1 de l'état actuel du référentiel en amont, nous choisirons l'un des trois noms de branche —_feature/tall_, master ou _bug/short_— et le traduirons à un ID de validation. Ensuite, nous écrirons une entrée de greffe spéciale qui dit: "Lorsque vous voyez ce commit, faites comme s'il avaitnoparent, c'est-à-dire qu'il s'agit d'un commit racine."

Disons que nous choisissons master. Le nom master pointe pour valider G, donc pour faire unshallowclone de commit G, nous obtenons commit G depuis le Git en amont comme d'habitude, mais écrivez ensuite une entrée de greffe spéciale qui affirme que la validation G anoparents. Nous avons mis cela dans notre référentiel, et maintenant notre graphique ressemble à ceci:

_G   <-- master, Origin/master
_

Ces ID parents se trouvent toujours dans G; c'est juste que chaque fois que Git utilise ou nous montre l'historique, il "greffe" rien du tout, de sorte que Gsemble êtreun commit racine , à des fins de suivi de l'historique.

Mise à jour d'un clone peu profond que nous avons fait plus tôt

Mais que se passe-t-il si nous avons déjà un clone (profondeur-1 peu profond), et que nous voulonsmettre à jouril? Eh bien, ce n'est pas vraiment un problème. Supposons que nous ayons fait un clone superficiel en amont lorsque master a indiqué de valider B, avant les nouvelles branches et la correction de bogue. Cela signifie que nousactuellementavons ceci:

_B   <-- master, Origin/master
_

Alors que le vrai parent de B est A, nous avons une entrée de greffe de clone peu profond disant "faire semblant B est un commit racine". Maintenant, nous _git fetch --depth 1_, qui recherche le master en amont - la chosewecall _Origin/master_— et voit commit G. Nous récupérons la validation G de l'amont, ainsi que ses objets, mais délibérémentne pasgrab valide D et F. Nous mettons ensuite à jour nos entrées de greffon de clones peu profonds pour dire que "faire semblant G est également un commit racine":

_B   <-- master

G   <-- Origin/master
_

Notre dépôt a maintenanttworoot commits: le nom master pointe (encore) pour valider B, dont nous prétendons (encore) les parents inexistants, et le nom _Origin/master_ pointe vers G, dont nous prétendons que les parents sont inexistants.

C'est pourquoi vous avez besoin de _git reset_

Dans un référentiel normal, vous pouvez utiliser _git pull_, qui est en réalité _git fetch_ suivi de _git merge_. Mais _git merge_ requiert de l'histoire, et nous n'en avons aucune: nous avons truqué Git avec des prétendus root commits, et ils n'ont aucune histoire derrière eux. Nous devons donc utiliser _git reset_ à la place.

Ce que fait _git reset_ est un peu compliqué, car il peut affecter jusqu'à trois choses différentes: unnom de branche, leindexet learbre de travail. Nous avons déjà vu quels sont les noms des branches: ils pointent simplement vers un commit (un, spécifique), que nous appelonstipde la branche. Cela laisse l'index et l'arbre de travail.

L'arbrework-treeest facile à expliquer: c'est là que se trouvent tous vos fichiers. C'est tout: ni plus ni moins. Il est là pour que vous puissiez réellementutiliserGit: Git consiste à stocker chaque commit jamais fait, pour toujours, afin qu'ils puissent tous être récupérés. Mais ils sont dans un format inutile aux simples mortels. Pour êtreutilisé, un fichier - ou plus généralement, la valeur entière d'un fichier de commit - doit être extrait dans son format normal. L'arbre de travail est l'endroit où cela se produit, puis vous pouvez travailler dessus et effectuer de nouveaux commits en l'utilisant également.

Leindexest un peu plus difficile à expliquer. C'est quelque chose de particulier à Git: les autres systèmes de contrôle de version n'en ont pas, ou s'ils ont quelque chose comme ça, ils ne l'exposent pas. Git le fait. L'index de Git est essentiellement où vous conservez lenextcommit à faire, mais cela signifie qu'il commence par contenir lecurrentcommit que vous avez extrait dans le travail- et Git l'utilise pour accélérer Git. Nous en dirons plus à ce sujet dans un instant.

Ce que fait _git reset --hard_, c'est d'affecterles trois: nom de la branche, index et arbre de travail. Ildéplacele nom de la branche afin qu'il pointe vers un commit (probablement différent). Il met ensuite à jour l'index pour correspondre à cette validation et met à jour l'arborescence de travail pour correspondre au nouvel index.

Par conséquent:

_git reset --hard Origin/master
_

dit à Git de rechercher _Origin/master_. Depuis que nous avons exécuté notre _git fetch_, cela indique maintenant de valider G. Git crée ensuitenotremaster - notre branche actuelle (et unique) - pointe également pour valider G, puis met à jour notre index et notre arbre de travail. Notre graphique ressemble maintenant à ceci:

_B   [abandoned - but see below]

G   <-- master, Origin/master
_

Désormais master et _Origin/master_ les deux nomment commit G, et commit G est celui extrait dans l'arborescence de travail.

Pourquoi vous avez besoin de _git clean -dfx_

La réponse ici est un peu compliquée, mais généralement c'est "vous n'avez pas" (besoin de _git clean_).

Lorsque vousdoavez besoin de _git clean_, c'est parce que vous - ou quelque chose que vous avez exécuté - avez ajouté des fichiers à votre arbre de travail dont vous n'avez pas parlé à Git. Ce sont des fichiersnon suiviset/ouignorés. L'utilisation de _git clean -df_ supprimera les fichiersnon suivis(et les répertoires vides); l'ajout de _-x_ supprimera également les fichiers ignorés.

Pour en savoir plus sur la différence entre "non suivi" et "ignoré", voir cette réponse .

Pourquoi vous n'avez pas besoin de _git clean_: l'index

J'ai mentionné ci-dessus que vous n'avez généralement pas besoin d'exécuter _git clean_. C'est à cause de l'index. Comme je l'ai dit plus tôt, l'index de Git est principalement "le prochain commit à faire". Si vous n'ajoutez jamais vos propres fichiers - si vous utilisez simplement _git checkout_ pour vérifier les divers validations existantes que vous avez eues tout le temps, ou que vous avez ajoutées avec _git fetch_; ou si vous utilisez _git reset --hard_ pour déplacer un nom de branche et également basculer l'index et l'arborescence de travail vers un autre commit - alors tout ce qui est dans l'indexen ce momentest làcarune version antérieure de _git checkout_ (ou _git reset_)putit dans l'index, ainsi que dans l'arborescence de travail.

En d'autres termes, l'index a un accès court et rapide pour Git -summaryor manifest décrivant l'arbre de travail actuel. Git l'utilise pour savoir ce qu'il y a maintenant dans l'arbre de travail. Lorsque vous demandez à Git de basculer vers un autre commit, via _git checkout_ ou _git reset --hard_, Git peut comparer rapidement l'index existant au nouveau commit. Tous les fichiers qui ontmodifiés, Git doivent extraire du nouveau commit (et mettre à jour l'index). Tous les fichiers qui sontnouvellement ajoutés, Git doivent également extraire (et mettre à jour l'index). Tous les fichiers qui sontgone- qui sont dans l'index existant, mais pas dans le nouveau commit — Git doitsupprimer... et c'est ce que fait Git. Git met à jour, ajoute et supprime ces fichiers dans l'arborescence de travail, comme indiqué par la comparaison entre l'index actuel et le nouveau commit.

Cela signifie que si vousdoavez besoin de _git clean_, vous devez avoir fait quelque chose en dehors de Git qui a ajouté des fichiers. Ces fichiers ajoutés ne sont pas dans l'index, donc par définition , ils ne sont pas suivis et/ou ignorés. S'ils ne sont simplement pas suivis, _git clean -f_ les supprimera, mais s'ils sont ignorés, seul _git clean -fx_ les supprimera. (Vous voulez _-d_ juste pour supprimer les répertoires qui sont ou deviennent vides pendant le nettoyage.)

Commits abandonnés et collecte des ordures

J'ai mentionné et dessiné dans le graphique peu profond mis à jour que lorsque nous _git fetch --depth 1_ puis _git reset --hard_, nous nous retrouvonsabandonnantle précédent graphique peu profond de profondeur 1 est validé. (Dans le graphique que j'ai dessiné, c'était commit B.) Cependant, dans Git, les commits abandonnés sont rarement vraiment abandonnés, du moins pas tout de suite. Au lieu de cela, certains noms spéciaux comme _ORIG_HEAD_ s'y accrochent pendant un certain temps, et chaqueréférence- les branches et les balises sont des formes de référence - porte avec elle unlogdes "valeurs précédentes".

Vous pouvez afficher chaque reflog avec _git reflog refname_. Par exemple, _git reflog master_ vous montre non seulement quels noms de validation mastermaintenant, mais aussi quels engagements il a nommésdans le passé. Il existe également un reflog pour HEAD lui-même, ce que _git reflog_ affiche par défaut.

Les entrées Reflog expirent finalement. Leur durée exacte varie, mais par défaut, ils sont éligibles à l'expiration après 30 jours dans certains cas et 90 jours dans d'autres. Une fois expirées, ces entrées de reflog ne protègent plus les validations abandonnées (ou, pour les références de balises annotées, l'objet balise annoté - les balises ne sont passupposéesà déplacer, donc ce cas n'est passupposése produire, mais si c'est le cas - si vous forcez Git à déplacer une balise - il est simplement géré de la même manière que toutes les autres références).

Une fois qu'un objet Git — commit, balise annotée, “arbre” ou “blob” (fichier) —estvraimentnon référencé, Git est autorisé à le supprimer pour de vrai.2 Ce n'est qu'à ce stade que les données de référentiel sous-jacentes pour les validations et les fichiers disparaissent. Même alors, cela ne se produit que lorsque quelque chose s'exécute _git gc_. Ainsi, un référentiel superficiel mis à jour avec _git fetch --depth 1_ n'est pastout à faitidentique à un nouveau clone avec _--depth 1_: le référentiel superficiel a probablement des noms persistants pour les validations d'origine, et ne supprimera pas les objets de référentiel supplémentaires jusqu'à ce que ces noms expirent ou soient autrement effacés.


2Outre la vérification des références, les objets obtiennent également un minimumtimeavant leur expiration. La valeur par défaut est de deux semaines. Cela empêche _git gc_ de supprimer les objets temporaires que Git crée, mais n'a pas encore établi de référence. Par exemple, lors d'une nouvelle validation, Git transforme d'abord l'index en une série d'objets tree qui se réfèrent entre eux mais n'ont pas de référence de niveau supérieur. Ensuite, il crée un nouvel objet commit qui fait référence à l'arborescence de niveau supérieur, mais rien ne fait encore référence à la validation. Enfin, il met à jour le nom de la branche actuelle. Jusqu'à la fin de cette dernière étape, les arborescences et le nouveau commit sont inaccessibles!


Considérations spéciales pour _--single-branch_ et/ou clones peu profonds

J'ai noté ci-dessus que le nom que vous donnez à _git clone -b_ peut faire référence à untag. Pour les clones normaux (non superficiels ou non à branche unique), cela fonctionne exactement comme on pourrait s'y attendre: vous obtenez un clone régulier, puis Git fait un _git checkout_ par le nom de la balise. Le résultat est la tête détachée habituelle, dans un clone parfaitement ordinaire.

Cependant, avec des clones peu profonds ou à une seule branche, il y a plusieurs conséquences inhabituelles. Ce sont tous, dans une certaine mesure, le résultat du fait que Git laisse transparaître l'implémentation.

Tout d'abord, si vous utilisez _--single-branch_ , Git modifie la configuration normale fetch dans le nouveau référentiel. La configuration normale de fetch dépend du nom que vous choisissez pour leremote, mais la valeur par défaut est Origin donc je vais juste utiliser Origin ici . Ça lit:

_fetch = +refs/heads/*:refs/remotes/Origin/*
_

Encore une fois, il s'agit de la configuration normale pour un clonenormal(pas une seule branche). Cette configuration indique à _git fetch_quoi récupérer, qui est "toutes les branches". Cependant, lorsque vous utilisez _--single-branch_, vous obtenez à la place une ligne d'extraction qui ne fait référence qu'à la seule branche:

_fetch = +refs/heads/zorg:refs/remotes/Origin/zorg
_

si vous clonez la branche zorg.

Quelle que soit la branche que vous clonez, c'est celle qui va dans la ligne fetch . Chaquefuture_git fetch_ obéira à cette ligne,3 vous ne récupérerez donc aucune autre branche. Si vousdosouhaitez récupérer d'autres branches plus tard, vous devrez modifier cette ligne ou ajouter d'autres lignes.

Deuxièmement, si vous utilisez _--single-branch_ et que vous clonez est une balise , Git mettra une ligne fetch plutôt étrange . Par exemple, avec _git clone --single-branch -b v2.1 ..._ j'obtiens:

_fetch = +refs/tags/v2.1:refs/tags/v2.1
_

Cela signifie que vous obtiendreznobranches, et sauf si quelqu'un a déplacé la balise,4 _git fetch_ ferarien!

Troisièmement, le comportement par défaut des balises est un peu bizarre en raison de la façon dont _git clone_ et _git fetch_ obtiennent des balises. N'oubliez pas que les balises sont simplement une référence à un commit particulier, tout comme les branches et toutes les autres références. Il existe cependant deux différences clés entre les branches et les balises: les branches sontattenduesà déplacer (et les balises ne le sont pas), et les branches obtiennentrenommées(et les balises ne 't).

Rappelez-vous que tout au long de ce qui précède, nous continuons à trouver que les autres (en amont) Git master deviennent notre _Origin/master_, et ainsi de suite. Ceci est un exemple du processus de changement de nom. Nous avons également vu, brièvement, précisémentcommentque renommerfonctionne, à travers la ligne _fetch =_: notre Git prend leur _refs/heads/master_ et change à notre _refs/remotes/Origin/master_. Ce nom est non seulement différent -recherche(_Origin/master_), mais littéralementne peut pasêtre le même que n'importe laquelle de nos succursales. Si nous créons une branche nommée _Origin/master_,5 le "nom complet" de cette branche est en fait _refs/heads/Origin/master_ qui est différent de l'autre nom complet _refs/remotes/Origin/master_. Ce n'est que lorsque Git utilise le nom plus court que nous avons une branche (régulière, locale) nommée _Origin/master_ et une autre branche différente (suivi à distance) nommée _Origin/master_. (C'est un peu comme être à n groupe où tout le monde s'appelle Bruce .)

Les balises ne passent pas par tout cela. La balise _v2.1_ est juste nommée _refs/tags/v2.1_. Cela signifie qu'il n'y a aucun moyen de séparer "leur" tag de "votre" tag. Vous pouvez avoir soit votre tag, soit leur tag. Tant que personnene déplaceune balise, cela n'a pas d'importance: si vousles deuxavez la balise, elle doit pointer vers lamême objet. (Si quelqu'un commence à déplacer des étiquettes, les choses deviennent laides.)

Dans tous les cas, Git implémente la récupération "normale" des balises par une règle simple:6  quand Git a déjà un commit, si certains tagnamesqui se commit, Git copie le tag aussi. Avec des clones ordinaires , le premier clone obtient toutes les balises, puis les opérations _git fetch_ suivantes obtiennent les balisesnew. Un clone peu profond, cependant, par définition omet certains commit (s), à savoir tout ce qui se trouve en dessous de tout point de greffe dans le graphique. Ces commits ne ramasseront pas les balises. Ilsne peuvent pas: pour avoir les balises, vous devez avoir les commits. Git n'est pas autorisé (sauf via les greffes peu profondes) à avoir l'ID d'un commit sans avoir réellement le commit.


3Vous pouvez donner à _git fetch_ une ou plusieurs références sur la ligne de commande, et celles-ci remplaceront la valeur par défaut. Cela s'applique uniquement à une extraction par défaut. Vous pouvez également utiliser plusieurs lignes _fetch =_ dans la configuration, par exemple, pour extraire juste un ensemble spécifique de branches, bien que la manière normale de "restreindre" un clone initialement à une seule branche soit de remettre l'habituel _+refs/heads/*:refs/remotes/Origin/*_ ligne de récupération.

4Puisque les balises ne sont passupposéesse déplacer, nous pourrions simplement dire "cela ne fait rien". S'ils bougent, cependant, le _+_ dans la spécification représente le drapeau de force, de sorte que la balise se déplace.

5Ne fais pas ça. C'est confu.Gitle gèrera très bien - la branche locale est dans l'espace de noms local et la branche de suivi à distance est dans l'espace de noms de suivi à distance - mais c'est vraiment déroutant.

6Cette règle ne correspond pas à la documentation. J'ai testé contre Git version 2.10.1; les anciens Gits peuvent utiliser une méthode différente.

47
torek