Je suis nouveau dans l'utilisation de Git, j'ai cloné une branche depuis GitHub et toutes les branches sont affichées lorsque j'ai tapé git branch
. Après avoir terminé mon travail, je l'ai poussé dans une nouvelle succursale avec succès. Après cela, j'ai copié le dossier dans un autre répertoire (parce que je voulais avoir une sauvegarde pour éviter les conflits), je l'ai entré et j'ai tapé git branch
. Seulement 3 branches affichées, sachant que j'en ai 4 sur Github.
J'ai essayé de résoudre le problème en clonant la branche dans un nouveau dossier (tapé git clone -b <branch-name> <repo-link-https>
) et maintenant seule la branche que j'ai clonée apparaît.
Des suggestions s'il vous plaît?
3 Réponses :
Remarques:
git clone --branch <branch> --single-branch
Pour les branches, utilisez git branch -avv
pour obtenir une liste de toutes les branches locales et distantes.
Puis réessayez votre copie, et comparez git branch -avv
fois terminé dans le nouveau dossier copié: s'il manque une branche distante, une simple git fetch
suffira.
Afin de vous assurer que vous avez toutes les informations à jour sur les branches de Github (votre télécommande), vous pouvez faire une git fetch
:
git branch -av
Où le drapeau --all
récupère les branches de toutes les télécommandes. Si vous souhaitez simplement voir toutes les branches (sur votre machine et sur votre GitHub), vous pouvez faire une git branch
:
git fetch --all
Où -a
montre les branches du local et des télécommandes, et -v
donne une sortie plus verbeuse.
Lorsque vous clonez un référentiel existant, votre Git crée un référentiel nouveau et différent, et copie dans ce nouveau référentiel tous les 1 des commits et aucune des branches du référentiel d'origine. La dernière étape de git clone
consiste à créer une branche. Ce nom de branche est le vôtre , pas le leur; il est simplement orthographié de la même manière que l'un de leurs noms.
Au fur et à mesure que vous travaillez avec votre clone - un référentiel différent - vous pouvez lui ajouter de plus en plus de branches. Si vous y ajoutez toutes les mêmes branches qui se trouvent dans le référentiel d'origine, vous avez maintenant tous leurs commits et tous leurs noms de branches (comme vos propres branches, remarquez). Mais jusque-là, vous avez juste tous leurs engagements . C'est bien, car Git ne concerne pas les branches. Git concerne les commits .
1 La description précise est beaucoup plus compliquée que celle-ci, mais en la considérant comme «copiez tous leurs commits et aucune de leurs branches» vous permettra de démarrer.
J'ai essayé de résoudre le problème en clonant la branche dans un nouveau dossier (tapé
git clone -b
) et maintenant seule la branche que j'ai clonée apparaît.
Lorsque vous créez un nouveau clone - qui, encore une fois, est un nouveau dépôt, où vous obtenez tous les commits du dépôt précédent mais aucune de ses branches pour le moment - la dernière étape de la commande git clone
est d'exécuter un git checkout
ou git switch
commande 2 qui crée une branche. L'indicateur -b
existe pour que vous puissiez dire à votre Git lequel de ses noms de branche copier, comme dernière étape. Si vous omettez l'indicateur -b
, votre Git demande à son référentiel Git - celui que vous clonez - quelle branche il recommande. Mais de toute façon, vous n'obtenez qu'une seule branche.
Vous n'avez en fait besoin d' aucun nom de branche pour travailler dans Git. Cependant, vous avez besoin d' une sorte de nom, et les noms de branche sont le meilleur type de nom ici. C'est pourquoi votre Git fait un nom à la fin du processus de git clone
. Chaque nom que vous créez vous donne une autre chose avec laquelle travailler.
Pour comprendre ce qui se passe, lisez la suite. Si vous êtes convaincu que votre question immédiate a été répondue, vous pouvez vous arrêter ici.
2 La commande git switch
été ajoutée pour la première fois dans Git version 2.23, pour diviser la git checkout
trop compliquée en deux commandes distinctes, git switch
et git restore
. Le git checkout
existant reste; vous pouvez l'utiliser à la place des deux nouvelles commandes plus simples. Les nouvelles commandes simplifiées sont en un sens plus sûres, cependant: la commande git switch
essaie d'être très sûre, tout comme la moitié de git checkout
qu'elle a copiée. La commande git restore
, cependant, est délibérément dangereuse en ce qu'elle détruira irrévocablement le travail; il copie l'autre moitié de git checkout
. Donc, si vous utilisez git checkout
, vous pouvez accidentellement invoquer la moitié «détruire mon travail» lorsque vous pensez invoquer la moitié «faire des choses en toute sécurité».
Pour comprendre ce que fait Git ici et pourquoi il fait cela comme ça, commencez par le fait que Git lui-même est vraiment une question de commits. Il ne s'agit pas de branches, bien que les noms de branche vous aident (et Git) à trouver des commits. Il ne s'agit pas de fichiers, bien que les commits contiennent des fichiers. C'est vraiment une question de commits: tout ce que fait Git est au service de la conservation et de l'ajout de commits. Les commits sont là où les choses commencent et sont le but de tout le reste.
Cela signifie qu'il est crucial de comprendre ce Commit est, comment vous nommez un particulier commettras, et comment vous faites un commit. Commençons par le nom.
Vous pourriez penser qu'un nom de branche nommerait un commit - et c'est en quelque sorte le cas, mais indirectement. En fait, chaque commit est nommé par son numéro. Chaque commit a un numéro unique. Aucun autre commit ne peut jamais avoir ce numéro: une fois que ce commit est fait, ce numéro est alloué à ce commit. Parce que cette validation prend ce nombre pour toujours, le nombre doit être vraiment grand, et c'est le cas. Actuellement, chaque commit Git obtient un des 2160 nombres possibles. 3 Ce nombre est exprimé en hexadécimal sous la forme d'une grosse chaîne laide comme e31aba42fb12bdeb0f850829e008e1e3f43af500
(il s'agit d'un commit réel dans un référentiel Git pour Git lui-même).
Ce numéro fonctionne toujours: si vous avez ce commit, c'est son numéro, et git show e31aba42fb12bdeb0f850829e008e1e3f43af500
le montrera, par exemple. Vous pouvez généralement abréger le nombre, à aussi peu que les quatre premiers caractères si cela n'est pas ambigu, donc si vous avez un clone du dépôt Git pour Git, git show e31aba42fb12bdeb0f850829e008
presque à coup git show e31aba42fb12bdeb0f850829e008
. Mais git show e31a
ne le fait pas car il pourrait être court pour ce commit, ou pour commit e31a17f741...
, par exemple. Bien que e31ab
fonctionne aujourd'hui, à mesure que de nouveaux commits sont ajoutés, il peut cesser de fonctionner.
Ces chiffres semblent aléatoires, mais ne le sont pas. En fait, chacun est une somme de contrôle cryptographique du contenu complet du commit. 4 Git effectue une double vérification lors de l'extraction de l'un de ses objets internes, y compris les commits, que la somme de contrôle correspond toujours, afin de détecter les échecs de stockage: vous dites à Git de trouver un commit (ou un autre objet) par ID de hachage et il vérifie que l'ID de hachage correspond toujours. Cela signifie donc qu'aucune partie d'un commit - ou de l'un des autres objets internes de Git - ne peut jamais changer non plus. Vous pouvez en créer de nouveaux , chacun obtenant un nouvel identifiant différent, mais cela n'affecte pas les existants, qui restent dans le référentiel.
3 Il est prévu de refaire le système de numérotation pour utiliser 2 256 numéros, avec une sorte de transition laide.
4 En fait, tous les objets internes de Git utilisent ce schéma. Cela signifie que tous les objets enregistrés sont figés pour toujours. C'est ainsi que Git gèle et déduplique le contenu des fichiers, par exemple.
Maintenant que nous en connaissons une - et la manière la plus profonde, pour ainsi dire - de rechercher un commit, par son ID de hachage, il est temps de regarder ce qu'il y a à l'intérieur de chaque commit. Chaque commit comprend deux parties:
Un commit contient un instantané complet de tous vos fichiers. Ce sont les données principales de la plupart des commits (et généralement aussi la majeure partie du référentiel). Chaque fichier est stocké en tant qu'objet blob interne, en utilisant cette même astuce de codage de nom de hachage. Cela déduplique automatiquement les fichiers, de sorte que si vous effectuez une centaine de commits d'affilée qui réutilisent principalement la plupart de leurs fichiers, ils ne prennent pas vraiment d'espace supplémentaire.
Chaque commit contient également des métadonnées , ou des informations sur le commit lui-même: qui l'a fait, quand et pourquoi, par exemple. La partie «pourquoi» est votre message de journal: votre propre explication à vous-même et / ou aux autres plus tard. Pourquoi ce commit est- il meilleur que le dernier? Ou du moins, pourquoi c'est différent, si ce n'est pas forcément mieux. Le but de ce commit particulier pourrait être de corriger un bogue, ou d'ajouter une nouvelle fonctionnalité, ou de préparer quelque chose pour ajouter une nouvelle fonctionnalité, ou autre. Le commit lui-même a le code source mis à jour, mais pas nécessairement quoi que ce soit sur le bogue que la mise à jour est censée corriger. C'est votre chance d'expliquer cela.
Il y a une partie des métadonnées que Git génère pour vous, puis utilise plus tard, que vous voyez rarement directement, et c'est la suivante: chaque commit contient l'ID de hachage brut de son prédécesseur immédiat. Cette chaîne s'engage ensemble, à l' envers , dans une chaîne de validations qui se termine par le dernier commit.
Nous pouvons dessiner ceci. Imaginez que nous ayons un référentiel avec seulement trois commits. Au lieu de vrais ID de hachage, nous utiliserons des lettres majuscules uniques pour remplacer les validations. Le tout premier commit sera A
, le suivant sera B
et le troisième commit sera commit C
:
E <-- main, origin/main / A--B--C \ D <-- dev (HEAD), origin/dev
Puisque le commit C
est le dernier, il a l'ID de hachage du commit B
antérieur dans ses métadonnées. On dit que C
pointe vers B
De la même manière, engagez B
points sur A
Puisque A
est le tout premier commit jamais effectué, il lui manque cette flèche pointant vers l'arrière: il ne pointe nulle part. Git appelle cela un (ou le) commit racine . C'est là que nous arrêtons de travailler à l'envers.
J'ai mentionné il y a un instant que chaque commit a un instantané complet de chaque fichier. Mais si Git affiche un commit, Git vous montre ce qui a changé . Comment et pourquoi Git fait-il cela?
Le pourquoi est peut-être le plus facile à expliquer. Si vous voulez voir tous les fichiers qui sont dans le commit, vous pouvez simplement vérifier le commit. Git copiera tous ces fichiers hors du commit - où, rappelez-vous, ils sont stockés dans un format Git gelé spécial, dédupliqué (et compressé aussi) - dans des fichiers informatiques ordinaires. Vous avez probablement un tas de visionneuses de fichiers qui sont plus compétentes que Git ne pourrait jamais l'être: elles peuvent vous montrer des images sous forme d' images, ouvrir des documents textuels dans des éditeurs de texte, ouvrir des PDF avec des visionneuses PDF, etc. Mais votre visionneuse de fichiers ne peut probablement pas comparer l'instantané entier avec l'instantané entier précédent. Git peut .
Git peut comparer assez facilement le snapshot C
snapshot B
, car le commit C
contient l'ID de hachage du commit B
Donc Git peut simplement extraire les deux commits. De plus, en raison de la façon dont Git déduplique les fichiers, Git peut immédiatement connaître - et même pas prendre la peine de les extraire - les fichiers dupliqués. Git n'a besoin que d'extraire et de comparer les différents fichiers. Git le fera et construira un ensemble de modifications qui transformeront les anciens fichiers en nouveaux. C'est ce que Git va vous montrer: cet ensemble d'instructions.
(Notez que Git crée l'ensemble d'instructions à la demande. Jusqu'à ce que vous demandiez à Git de comparer deux commits, tout ce que Git a sont les deux instantanés. Vous pouvez obtenir différents ensembles d'instructions en fonction des options que vous passez à la commande de comparaison. Par exemple, Git peut faire la vérification des différences en fonction des mots, ou ignorer autrement certains types de changements d'espaces blancs. Les capacités de Git ici ne sont pas toujours aussi bonnes que nous pourrions le souhaiter, mais nous pouvons utiliser quelques astuces. Elles sont hors de portée pour cette réponse particulière, cependant.)
Nous savons déjà que si nous mémorisons les gros ID de hachage laids (ou les notons par écrit), nous pouvons les utiliser pour trouver des commits. Mais c'est ridicule. Nous avons un ordinateur . Pourquoi l' ordinateur n'écrit-il pas les identifiants de hachage pour nous?
C'est ce que fait un nom de branche. Mais c'est un peu sournois. Ce qu'un nom de branche fait vraiment, c'est stocker uniquement l'ID de hachage du dernier commit. Dessinons à nouveau ce référentiel à trois commit et ajoutons un nom, main
, qui identifie le dernier commit:
git switch dev
Ici, au lieu d'essayer de se souvenir de l' ID de hachage de C
, nous savons simplement que le nom main
fait pour nous. Donc, git checkout main
(pré-2.23 Git) ou git switch main
(2.23 et versions ultérieures) nous obtient le dernier commit - actuellement C
- quel que soit son ID de hachage.
Nous pouvons maintenant ajouter un nouveau nom qui pointe également vers le commit C
:
E <-- main (HEAD), origin/main / A--B--C \ D <-- origin/dev
Maintenant, nous avons besoin d'une dernière chose: lequel de ces noms utilisons-nous? Pour le moment, cela n'a pas beaucoup d'importance, car les deux noms sélectionnent commit C
Mais attachons le nom spécial HEAD
à l'un des deux noms de branche, comme ceci:
E <-- origin/main / A--B--C \ D <-- origin/dev
Si nous git switch dev
, nous attachons à nouveau le nom spécial HEAD
au nom dev
, comme ceci:
E / A--B--C \ D
Maintenant, faisons un nouveau commit. Sans nous soucier de la façon dont nous faisons un nouveau commit, supposons simplement que tout est fait. Ce nouveau commit D
pointera nécessairement vers le commit C
existant, car nous avons créé D
partir de C
Cela ressemble à ceci:
E <-- main (HEAD) / A--B--C \ D <-- dev
Mais D
est maintenant le dernier commit, donc Git doit mettre à jour un nom . Quel nom doit-il mettre à jour? La réponse est claire: il devrait mettre à jour celui auquel HEAD
est attaché:
A--B--C <-- main \ D <-- dev (HEAD)
Nous avons maintenant deux noms de succursales, et les deux noms spécifions deux différents commits « dernier ». Le dernier commit sur main
est C
, et le dernier commit sur dev
est D
Commit D
pointe vers le commit C
, qui pointe vers B
, qui pointe vers A
; donc les quatre commits sont sur branche dev
, tandis que trois d'entre eux sont sur main
.
Si nous retournons au nom main
et y faisons un nouveau commit, nous obtenons:
A--B--C \ D
ce qui signifie que nous avons maintenant trois commits qui sont partagés sur les deux branches, et un commit qui est uniquement sur main
et un commit qui est uniquement sur dev
. Maintenant, nous avons besoin des deux noms pour trouver les cinq commits; un nom trouvera un commit, qui trouvera les trois commits partagés , mais nous avons besoin de l'autre nom pour trouver le dernier commit restant.
Notez que les noms de branche bougent . En fait, ils se déplacent automatiquement, lorsque nous faisons de nouveaux commits: quel que soit le nom de la branche HEAD
est attaché, il se déplace automatiquement pour englober le nouveau commit. Tous les autres noms de branche restent en place à ce stade, mais comme ce sont nos noms de branche, nous en avons le contrôle. Nous pouvons demander à notre Git de déplacer ces noms à tout moment. La seule contrainte est que nous devons avoir un commit pour déplacer le nom.
Lorsque nous clonons le référentiel de quelqu'un d'autre, nous obtenons tous leurs commits et aucune de leurs branches. Comment cela marche-t-il? Eh bien, supposons que nous ayons ce qui précède, avec deux noms de branche réels main
et dev
sélectionnant respectivement les commits E
et D
Nous créons maintenant un nouveau référentiel dans lequel nous copions les cinq commits, ce qui nous donne:
A--B--C <-- main, dev (HEAD)
Nous avons en fait besoin de deux noms pour trouver tous les commits. Mais nous n'avons pas besoin de noms de succursales . L'autre Git, travaillant avec l'autre référentiel, a des noms de branche, car ce sont ses branches qu'il déplacera à mesure qu'il effectuera de nouveaux commits. Donc, ce que fait notre Git, c'est copier leurs noms mais les changer . Nous demandons à notre Git de prendre leurs noms de branche et de créer nos noms de suivi à distance , en ajoutant quelque chose - généralement origin/
- aux noms. 5 Nous obtenons donc:
A--B--C <-- main (HEAD), dev
Git refusera d'attacher le nom spécial HEAD
à l'un de ces noms de suivi à distance. HEAD
ne peut être attaché qu'à un nom de branche . Donc, la dernière étape de notre git clone
est d'utiliser l'option -b
, ou leur recommandation, pour choisir l'un de ces deux noms, et créer un nom de branche à partir de celui-ci, comme ceci:
A--B--C <-- main, dev
Notez que notre nom de branche sélectionne le même commit que le nom de suivi à distance que notre git clone
créé à partir de son nom de branche. Mais nous n'avons maintenant qu'un seul nom de branche, pas deux. Si nous courons:
A--B--C <-- main
cela utilise une fonctionnalité spéciale fournie par Git, qui trouve leur origin/dev
et crée notre propre nouveau nom dev
:
A <-B <-C
et maintenant nous avons deux noms de branche. Mais nous ne l'avons pas fait au départ. Notez que nous avons également maintenant vérifié le commit D
, plutôt que le commit E
, car git switch
(ou git checkout
, si nous l'utilisons) non seulement change les branches, mais sélectionne également le commit que le nom de la branche identifie, comme le commit qui est à vérifier, et donc à notre disposition pour travailler avec.
5 Techniquement, un nom de suivi à distance se trouve dans un espace de noms distinct. Notre Git ne se contente pas de coller l' origin/
en avant, il remplace les refs/heads/
par des refs/remotes/origin/
. L' origin
nom est en fait une télécommande et nous pouvons avoir autant de télécommandes que nous le souhaitons dans notre référentiel Git. Mais c'est un sujet pour une autre question.
Merci beaucoup! Vous avez tout clarifié et résolu mon problème.
Mais pourquoi ne clonez-vous qu'une seule branche alors que vous avez besoin de toutes les branches ?
Parce que je n'avais besoin que de cette branche spécifique pour travailler avec ... c'est ce que j'ai pensé en premier