web-dev-qa-db-fra.com

Vérifier un ancien commit et maintenir la tête sur la branche master?

Actuellement pour passer à un autre commit git (sur la même branche ... en fait, sur la branche master!), J'exécute la commande

git checkout ea3d5ed039edd6d4a07cc41bd09eb58edd1f2b3a

Maintenant, chaque fois que je fais ça, ce crétin me dit que je suis maintenant avec une tête détachée. Comment puis-je accéder à un commit plus ancien tout en maintenant la tête sur la même branche?

83
devoured elysium

La plupart du temps, lorsque je fais cela, je passe à une branche temporaire:

git checkout -b temp-branch-name ea3d5ed039edd6d4a07cc41bd09eb58edd1f2b3a

Puis, après avoir terminé, je supprime simplement la branche. 

188
Nick Canzoneri

Cela dépend de ce que vous voulez faire lorsque vous extrayez ce commit. Si tout ce que vous faites est de le vérifier pour pouvoir construire ou tester cette révision, rien de mal à travailler avec une tête détachée. Rappelez-vous simplement de vérifier une branche réelle avant de faire des commits (git checkout master, par exemple), afin de ne pas créer de commits qui ne sont inclus dans aucune branche.

Si, toutefois, vous souhaitez effectuer plus de validations à partir de ce point, vous devez créer une branche. Si vous effectuez des commits qui ne sont pas référencés par une branche, ils peuvent facilement être perdus et seront éventuellement nettoyés par le ramasse-miettes de git, car rien ne se réfère à eux. Vous pouvez créer une nouvelle branche en lançant: 

git checkout -b newbranch ea3d5ed

Pour vous aider à visualiser, voici quelques diagrammes montrant comment le travail sur une tête détachée diffère du travail sur une branche.

Commençons par 3 commits sur master, A, B et C. master est la branche actuelle, donc HEAD pointe sur master, ce qui pointe sur commettre C.

 A B C 
 * - * - * <- master <- HEAD 

Maintenant, si nous commettons, git créera un commit ayant C comme parent (car c'est le commit actuel, désigné depuis HEAD via master), et mettra à jour master pour qu'il pointe vers ce nouveau commit. Tous nos commits sont maintenant dans master, et HEAD pointe vers le nouveau commit à travers master.

 A B C D 
 * - * - * - * <- master <- HEAD 

Voyons maintenant B, nous donnant une HEAD détachée.

 A B C D 
 * - * - * - * <- master 
 ^ 
\- HEAD 

Tout fonctionne bien ici; nous pouvons examiner tous les fichiers, construire notre programme, le tester, etc. Nous pouvons même créer de nouveaux commits; mais si nous le faisons, il n'y a pas de branche sur laquelle nous sommes, nous ne pouvons donc pas indiquer de branche à ce nouveau commit. La seule chose qui pointe là-dessus est HEAD:

 A B C D 
 * - * - * - * <- master 
\
 * <- HEAD 
 E 

Si nous décidons par la suite de vérifier à nouveau master, rien ne fera référence à E. 

 A B C D 
 * - * - * - * <- master <- HEAD 
\
 * 
 E 

Comme il n'y a rien qui s'y réfère, il peut être difficile à trouver, et git considère les commits sans aucune référence à abandonner (ils surviennent assez souvent si vous rebassez, écrasez des correctifs ou effectuez une autre manipulation amusante de l'historique; ils représentent généralement des correctifs abandonnés que vous ne vous souciez plus de). Après un certain temps, git considérera que c'est un déchet, qu'il sera jeté lors de la prochaine exécution du ramassage des ordures.

Ainsi, au lieu de vérifier une révision simple et d'obtenir une tête détachée, si vous avez envie de faire plus de validations, vous devez utiliser git checkout -b branch B pour créer une branche et la vérifier. Désormais, vos commits ne seront pas perdus, car ils seront inclus dans une branche, que vous pourrez facilement consulter et fusionner ultérieurement.

 A B C D 
 * - * - * - * <- master 
 ^ 
\- branche <- HEAD 

Si vous oubliez de le faire et créez des commits à partir d'une branche, vous n'avez pas à vous inquiéter. Vous pouvez créer une branche faisant référence à la révision principale avec git checkout -b branch. Si vous êtes déjà revenu dans la branche master et réalisez que vous avez oublié un commit errant, vous pouvez le trouver en utilisant git reflog , qui vous montrera un historique de ce que commets HEAD au cours des derniers jours. Tout ce qui est encore dans le reflog ne sera pas ramassé, et les références sont généralement conservées dans le reflog pendant au moins 30 jours.

78
Brian Campbell

Si vous voulez simplement revenir à un engagement précédent pour jouer avec celui-ci sans y apporter de modification, vous pouvez le faire.

git co <previous-commit-id>

vous serez sur une branche appelée "(pas de branche)" après cette commande.

Confirmez ceci par

git br

Après avoir joué avec ce code précédemment validé, vous pouvez passer à la branche dans laquelle vous vous trouviez.

git co <the-branch-you-were-on>

Le "(pas de branche)" sera supprimé automatiquement. De cette façon, vous n'avez pas besoin de créer une branche temporaire.

7
Zack Xu

HEAD de Git est simplement un pointeur indiquant ce qui se trouve dans le répertoire de travail. Si vous voulez extraire un commit qui n'est pas le chef d'une branche, il vous suffit de rediriger votre HEAD pour qu'il pointe vers ce commit. Il n’ya aucun moyen de le contourner. Vous pouvez créer une branche temporaire lors de cette validation, mais HEAD sera néanmoins dirigé loin du maître.

C’est la courte explication. Nous espérons que la verbosité ci-dessous aidera à comprendre en quoi HEAD et maître sont différents:

Normalement, les choses ressemblent à ceci:

C ← refs/heads/master ← HEAD 
↓
B
↓
A

Ce qui revient à dire: «Le parent de C est B, et le parent de B est A. Le maître de branche pointe vers C et j'ai actuellement extrait le contenu de ce dernier. De plus, lorsque je commettrai, le maître sera mis à jour. ”

Certaines hypothèses implicites sont nécessaires pour une compréhension approfondie du graphe de validation. À savoir, les commits font uniquement référence à leurs parents et le contenu d'une branche est constitué de ces commits (et uniquement de ces commits) auxquels on peut accéder en suivant les liens parent. Le contenu (non modifié) de l'arbre de travail et l'index doivent correspondre à la validation nommée par HEAD, de manière indirecte («symbolique») ou directement («détachée»).

Ainsi, si vous souhaitez extraire un ancien commit, le HEAD doit être mis à jour pour qu'il pointe vers le commit souhaité. git-checkout fait justement cela:

C ← refs/heads/master 
↓
B ← HEAD
↓
A

Maintenant, vous avez laissé votre succursale derrière vous, car vous regardez quelque chose de vieux. C’est parfaitement correct, comme le dit le conseil de la «tête détachée» avec calme (c’est moi qui souligne):

Vous pouvez regarder autour de vous, faire des modifications expérimentales et les valider, et vous pouvez supprimer tous les commits que vous avez effectués dans cet état sans affecter les branches en effectuant une autre extraction.

D'autre part, bien que réinitialiser votre branche obtienne également HEAD où il le faut, l'effet serait très différent!

C
↓
B ← refs/heads/master ← HEAD
↓
A

Le commit C deviendra un déchet, puisque vous avez déclaré que vous ne souhaitiez plus qu'il fasse partie de la branche master.

En bref, tout ce que vous avez à faire est de comprendre ce que git veut dire par «HEAD» - c’est où vous êtes _ et non pas où se trouve une branche donnée. Et si où vous _ ne correspond pas à celui où se trouve une branche, il n’ya pas d’autre choix que d’utiliser un HEAD détaché.

(Peut-être aussi regarder dans GitHub, gitk ou gitweb pour parcourir l'historique de commit, si le fait de faire dérailler votre HEAD continue de vous énerver.)

5
Josh Lee

La question est un peu vague, mais si vous voulez simplement changer les fichiers dans votre arbre de travail, vous pouvez simplement faire ceci:

git checkout [commit|branch] -- .

Vous pouvez ensuite organiser les modifications et créer un nouveau commit si vous le souhaitez. C'est assez utile parfois.

1
Lari Hotari

Je pense avoir compris vos questions. Voici ce que j'ai trouvé pour le résoudre. et il n'y a pas de solution graphique, vous pouvez uniquement utiliser la commande pour le résoudre, et c'est très simple.

étape 1: créez un tag de l’ancien commit que vous voulez revenir en arrière.

comme tag v2.0

étape 2: git checkout v2.0

la voici: votre HEAD pointe maintenant sur le commit 'v2.0', mais le master pointe toujours sur le dernier commit.

C:\Program Files\Git\doc\git\html\git-checkout.html ce document peut vous aider 

ou tapez git help <checkout>

0
Lion Lai