Comment puis-je déplacer HEAD vers un emplacement précédent? (Tête détachée) & Annuler les commits

179

Dans Git, j'essayais de faire un squash commiten fusionnant dans une autre branche puis en réinitialisant HEADà l'endroit précédent via:

git reset origin/master

Mais j'ai besoin de sortir de ça. Comment puis-je ramener HEAD à l'emplacement précédent?

J'ai le fragment SHA-1 ( 23b6772) du commit vers lequel je dois le déplacer. Comment puis-je revenir à ce commit?

timpone
la source
12
HEAD est juste un pointeur vers votre emplacement actuel (ou révision pour être précis). git checkout 23b6772devrait faire.
Yaroslav Admin
Double possible du repo Revert Git vers un commit précédent
Andrew C
1
@YaroslavAdmin Non, ça ne devrait pas . La vérification directe d'un commit est la raison pour laquelle l'état HEAD détaché s'est produit (puisque les branches de suivi à distance ne peuvent pas être extraites elles-mêmes et se reportent automatiquement au commit qu'elles pointent lorsque vous essayez de le faire comme OP l'a fait) Aussi, désolé pour le nécromantique commentaire :-) J'espère que le problème initial est déjà résolu ...
RomainValeri

Réponses:

398

Avant de répondre, ajoutons un peu de contexte, expliquant ce que HEADc'est.

First of all what is HEAD?

HEADest simplement une référence au commit actuel (le plus récent) sur la branche courante.
Il ne peut y en avoir qu'un seul HEADà la fois (excluant git worktree).

Le contenu de HEADest stocké à l'intérieur .git/HEADet contient les 40 octets SHA-1 de la validation actuelle.


detached HEAD

Si vous n'êtes pas sur le dernier commit - c'est-à-dire qu'il HEADpointe vers un commit antérieur dans l'historique, il est appelé detached HEAD.

Entrez la description de l'image ici

Sur la ligne de commande, cela ressemblera à ceci - SHA-1 au lieu du nom de la branche car le HEADne pointe pas vers la pointe de la branche actuelle:

Entrez la description de l'image ici

Entrez la description de l'image ici


Quelques options sur la façon de récupérer à partir d'un HEAD détaché:


git checkout

git checkout <commit_id>
git checkout -b <new branch> <commit_id>
git checkout HEAD~X // x is the number of commits t go back

Cela va extraire une nouvelle branche pointant vers le commit souhaité.
Cette commande récupérera un commit donné.
À ce stade, vous pouvez créer une branche et commencer à travailler à partir de ce point.

# Checkout a given commit.
# Doing so will result in a `detached HEAD` which mean that the `HEAD`
# is not pointing to the latest so you will need to checkout branch
# in order to be able to update the code.
git checkout <commit-id>

# Create a new branch forked to the given commit
git checkout -b <branch name>

git reflog

Vous pouvez également utiliser le reflog.
git reflog affichera tout changement qui a mis à jour le HEADet la vérification de l'entrée de reflog souhaitée rétablira HEADcette validation.

Chaque fois que le HEAD est modifié, il y aura une nouvelle entrée dans le reflog

git reflog
git checkout HEAD@{...}

Cela vous ramènera à votre engagement souhaité

Entrez la description de l'image ici


git reset --hard <commit_id>

"Déplacez" votre HEAD vers le commit souhaité.

# This will destroy any local modifications.
# Don't do it if you have uncommitted work you want to keep.
git reset --hard 0d1d7fc32

# Alternatively, if there's work to keep:
git stash
git reset --hard 0d1d7fc32
git stash pop
# This saves the modifications, then reapplies that patch after resetting.
# You could get merge conflicts if you've modified things which were
# changed since the commit you reset to.
  • Remarque: ( depuis Git 2.7 ), vous pouvez également utiliser le git rebase --no-autostash.

git revert <sha-1>

"Annuler" la plage de validation ou de validation donnée.
La commande de réinitialisation "annulera" toutes les modifications apportées au commit donné.
Un nouveau commit avec le correctif d'annulation sera validé tandis que le commit original restera également dans l'historique.

# Add a new commit with the undo of the original one.
# The <sha-1> can be any commit(s) or commit range
git revert <sha-1>

Ce schéma illustre quelle commande fait quoi.
Comme vous pouvez le voir, reset && checkoutmodifiez le fichier HEAD.

Entrez la description de l'image ici

CodeWizard
la source
Si vous n'êtes pas sur le dernier commit - ce qui signifie que HEAD pointe vers un commit précédent dans l'historique, il s'appelle HEAD détaché, sauf si ce commit précédent dans l'historique est la pointe d'une branche différente. D'après mon expérience, vous pourriez dire que vous êtes détaché si HEAD ne pointe pas vers un commit qui est également pointé par une branche. Cela ne s'applique pas aux tags.
Tim
Vous pouvez être en HEAD détaché et en même temps avoir une branche avec le même commit que le HEAD de cette branche. Je ne comprends pas votre commentaire
CodeWizard
3
J'ai des problèmes avec votre utilisation du balisage de code en ligne pour les titres :)
jub0bs
Impossible de trouver un meilleur moyen de le souligner. n'hésitez pas à modifier. vous êtes plus que bienvenu
CodeWizard
22

Faire

git reset 23b6772

Pour voir si vous êtes sur la bonne position:

git status

Vous verrez quelque chose

Sur le maître de branche Votre branche est derrière 'origin / master' par 17 commits et peut être avancée rapidement.

Réécrivez ensuite l'historique sur votre télécommande pour refléter le changement:

git push --force-with-lease // a useful command @oktober mentions in comments
amulier
la source
1
Soyez extrêmement prudent avec git push --force. Dans de nombreuses situations, cela fera de vous la personne la moins populaire de l'équipe pendant un petit moment ....
Kay V
pour ajouter à la note ci-dessus, je viens de tomber sur cette citation à about.gitlab.com/blog/2014/11/26/keeping-your-code-protected et j'ai dû l'ajouter: "Une seule commande git push --force peut facilement gâcher la journée pour beaucoup de gens: les dépôts [186 Jenkins] ont leurs têtes de branche rembobinées pour pointer vers des commits plus anciens, et en fait, les nouveaux commits ont été égarés après le mauvais git-push. " - développeur très impopulaire ....
Kay V
2
@KayV jetez un œil à git push --force-with-lease(article Thoughtbot : thinkingbot.com/blog/git-push-force-with-lease )
octobre
1
Drapeau utile, @oktober et un bon article. Merci de l'ajouter ici et de m'en avoir parlé.
Kay V le
1
Merci! cela m'a aidé à annuler une mauvaise fusion. puisque les fusions ne réagissent pas de la même manière revertque les commits, je me suis retrouvé dans une situation incroyablement difficile. force-with-leasem'a donné la confiance nécessaire pour réécrire l'histoire git de la branche sans affecter le travail des autres. Bravo!
anon58192932
11

Solution la plus rapide possible (juste 1 étape)

Utilisation git checkout -

Vous verrez Switched to branch <branch_name>. Confirmez que c'est la branche que vous voulez.


Brève explication: cette commande ramènera HEAD à sa dernière position. Voir la note sur les résultats à la fin de cette réponse.


Mnémonique: cette approche est un peu comme utiliser cd -pour revenir à votre répertoire précédemment visité. La syntaxe et les cas applicables correspondent assez bien (par exemple, c'est utile lorsque vous voulez réellement que HEAD revienne là où il était).


Solution plus méthodique (2 étapes, mais mémorable)

L'approche rapide résout la question du PO. Mais que se passe-t-il si votre situation est légèrement différente: disons que vous avez redémarré Bash puis vous vous retrouvez avec HEAD détaché. Dans ce cas, voici 2 étapes simples et faciles à retenir.

1. Choisissez la succursale dont vous avez besoin

Utilisation git branch -v

Vous voyez une liste des succursales locales existantes. Saisissez le nom de la succursale qui correspond à vos besoins.

2. Déplacez HEAD dessus

Utilisation git checkout <branch_name>

Vous verrez Switched to branch <branch_name>. Succès!


Résultats

Quelle que soit la méthode, vous pouvez maintenant continuer à ajouter et à valider votre travail comme auparavant: vos prochaines modifications seront suivies <branch_name>.

Notez que les deux git checkout -et git checkout <branch_name>donneront des instructions supplémentaires si vous avez validé des modifications pendant que HEAD était détaché.

Kay V
la source
Cela ne fonctionne pas parce que si je fais ( en supposant que 8acc968 est HEAD ~ 2) git checkout 8acc968puis git branch -va MyBranchdans la liste ci - dessous ... mais git checkout MyBranchsupprime mes commentaires.
amuliar
Salut @amuliar - git checkout 8acc968vérifiera un commit, pas une branche. Si MyBranchvous avez les validations souhaitées, essayez git checkout MyBranch. S'il ne contient pas les modifications du commit 8acc968, vous devrez fusionner ces modifications après avoir extrait la branche.
Kay V
Merci d'avoir répondu! Je l'ai fait git checkoutpour voir un commit précédent et je voulais revenir au dernier commit. Mais sans le dernier hachage de commit, j'étais quasiment perdu. Cette solution est parfaite pour ma situation!
zyy
4

La question peut être lue comme suit:

J'étais en état détaché avec HEADat 23b6772et tapé git reset origin/master(parce que je voulais écraser). Maintenant , j'ai changé d' avis, comment dois - je retourner à HEADêtre à 23b6772?

La réponse simple est: git reset 23b6772

Mais j'ai répondu à cette question parce que j'en avais marre de taper (copier-coller) les hachages de commit ou son abréviation chaque fois que je voulais faire référence au précédent HEADet que je cherchais sur Google pour voir s'il y avait une sorte de raccourci.

Il s'avère que oui!

git reset -(ou dans mon cas git cherry-pick -)

Ce qui était d'ailleurs la même chose que cd -de revenir au répertoire courant précédent dans * nix! Alors hourra, j'ai appris deux choses avec une pierre.

antak
la source
0

Lorsque vous exécutez la commande, git checkout commit_idHEAD détaché de 13ca5593d(say commit-id)et la branche sera disponible plus longtemps.

Revenir à l'emplacement précédent exécutez la commande pas à pas -

  1. git pull origin branch_name (dites maître)
  2. git checkout branch_name
  3. git pull origin branch_name

Vous serez de retour à l'emplacement précédent avec un commit mis à jour depuis le référentiel distant.

Deepak Kumar
la source
0

Aujourd'hui, j'ai vérifié par erreur un commit et j'ai commencé à travailler dessus, faisant quelques commits sur un état de détachement HEAD. Ensuite, j'ai poussé vers la branche distante en utilisant la commande suivante:

git push origin HEAD: <My-remote-branch>

ensuite

git checkout <My-remote-branch>

ensuite

git pull

J'ai finalement obtenu tous mes changements dans ma branche que j'ai faits dans Detach HEAD.

Zaid Mirza
la source
0

Ce n'est peut-être pas une solution technique, mais cela fonctionne. (si l'un de vos coéquipiers a la même succursale en local)

Supposons que votre nom de branche soit branch-xxx .

Étapes à suivre:

  • Ne pas mettre à jour ou tirer - rien
  • Créez simplement une nouvelle branche ( branch-yyy ) à partir de branch-xxx sur sa machine
  • C'est tout, toutes vos modifications existantes seront dans cette nouvelle branche ( branch-yyy ). Vous pouvez continuer votre travail avec cette branche.

Remarque: Encore une fois, ce n'est pas une solution technique, mais cela aidera à coup sûr.

Sathish Kumar
la source