Comment annuler un «git cherry-pick» réussi?

99

Sur un dépôt local, je viens de l'exécuter git cherry-pick SHAsans aucun conflit ni problème. J'ai alors réalisé que je ne voulais pas faire ce que je venais de faire. Je n'ai poussé cela nulle part.

Comment puis-je supprimer uniquement cette sélection de cerises?

J'aimerais savoir s'il existe un moyen de le faire:

  • quand j'ai d'autres changements locaux
  • quand je n'ai pas d'autres changements locaux

De préférence avec une seule commande pour les deux cas si possible.

Brad Parks
la source

Réponses:

134

Un cherry-pick est fondamentalement un commit, donc si vous voulez l'annuler, il vous suffit d'annuler le commit.

quand j'ai d'autres changements locaux

Stockez vos modifications actuelles afin de pouvoir les réappliquer après la réinitialisation de la validation.

$ git stash
$ git reset --hard HEAD^
$ git stash pop  # or `git stash apply`, if you want to keep the changeset in the stash

quand je n'ai pas d'autres changements locaux

$ git reset --hard HEAD^
Tim
la source
6
En plus de la question et de la réponse, si vous n'êtes pas sûr de ce que vous choisissez, vous pouvez toujours `` sélectionner SHA - no-commit '' et il n'y aura pas de validation, seulement des changements qui peuvent être facilement vérifiés , ceci est utile pour la cueillette partielle des cerises
kuskmen
2
windows: git reset --hard "HEAD ^"
slim
22

Pour annuler votre dernier commit, faites simplement git reset --hard HEAD~.

Edit : cette réponse s'appliquait à une version antérieure de la question qui ne mentionnait pas la préservation des changements locaux; la réponse acceptée de Tim est en effet la bonne. Merci à qwertzguy pour l'avertissement.

David Deutsch
la source
2
Cela devrait être HEAD ^ ou HEAD ~ 1
Andreas Wederbrand
Ils sont tous deux équivalents à HEAD ~ (et HEAD ^ 1, d'ailleurs)
David Deutsch
1
@DavidDeutsch C'est vrai (mais uniquement dans les versions plus récentes de Git), mais majuscule ( HEAD) est plus robuste: considérez le cas malheureux où headest le nom d'une référence existante.
jub0bs
1
@Jubobs - bon point; J'ai changé le boîtier dans ma réponse.
David Deutsch
1
@qwertzguy, bonne prise; en regardant les horodatages, le peu sur les changements locaux a été ajouté à la question une minute après avoir posté cette réponse :)
David Deutsch
8

Si possible, évitez les réinitialisations matérielles. Les réinitialisations matérielles sont l'une des très rares opérations destructives de git. Heureusement, vous pouvez annuler une sélection de cerises sans réinitialisation et éviter tout ce qui est destructeur.

Notez le hachage du cherry-pick que vous souhaitez annuler, dites-le ${bad_cherrypick}. Faites un git revert ${bad_cherrypick}. Maintenant, le contenu de votre arbre de travail est comme avant votre mauvaise sélection.

Répétez votre git cherry-pick ${wanted_commit}, et lorsque vous êtes satisfait de la nouvelle sélection de cerises, faites un git rebase -i ${bad_cherrypick}~1. Pendant le rebase, supprimez les deux ${bad_cherrypick}et son retour correspondant.

La branche sur laquelle vous travaillez n'aura que le bon choix. Aucune réinitialisation nécessaire!

Cuadue
la source
6

git reflog peut venir à votre secours.

Tapez-le dans votre console et vous obtiendrez une liste de votre historique git avec SHA-1 qui les représente.

Vérifiez simplement tout SHA-1 auquel vous souhaitez revenir


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

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 du commit actuel.


detached HEAD

Si vous n'êtes pas sur le dernier commit, c'est-à-dire qu'il HEADpointe vers un commit précédent 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 pour 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 reflogaffichera tout changement qui a mis à jour le HEADet la vérification de l'entrée de reflog désiré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 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
peut-être ajoutergit reset --hard
wyx le
3

Face à ce même problème, j'ai découvert que si vous vous êtes engagé et / ou poussé à distance depuis votre choix de cerise réussi, et que vous souhaitez le supprimer, vous pouvez trouver le SHA du choix de cerise en exécutant:

git log --graph --decorate --oneline

Ensuite, (après avoir utilisé :wqpour quitter le journal), vous pouvez supprimer le cherry-pick en utilisant

git rebase -p --onto YOUR_SHA_HERE^ YOUR_SHA_HERE

YOUR_SHA_HEREest égal au SHA de 40 ou 7 caractères abrégé du commit sélectionné.

Au début, vous ne pourrez pas appliquer vos modifications car votre dépôt distant et votre dépôt local auront des historiques de validation différents. Vous pouvez forcer vos commits locaux à remplacer ce qui se trouve sur votre télécommande en utilisant

git push --force origin YOUR_REPO_NAME

(J'ai adapté cette solution de Seth Robertson : voir «Suppression d'un commit entier».)

brogrammeur
la source
1

Une commande et n'utilise pas la git resetcommande destructive :

GIT_SEQUENCE_EDITOR="sed -i 's/pick/d/'" git rebase -i HEAD~ --autostash

Il supprime simplement le commit, vous remettant exactement dans l'état avant le choix de la cerise, même si vous aviez des modifications locales.

qwertzguy
la source
Il supprime simplement le commit. que pensez-vous faire git reset HEAD^?
Tim
@TimCastelijns annule git resetl'action de validation sans abandonner aucune des modifications. Je vous recommande de lire git-scm.com/docs/git-reset . La question de OP est d'annuler un choix de cerise. Essayez les deux commandes, la mienne vous remettra dans l'état exact où vous étiez avant la sélection. git resetgâchera votre arbre de travail car il laisse les changements choisis par la cerise sur le gâteau et ils deviennent indiscernables de vos changements locaux préexistants.
qwertzguy
oui, désolé, je voulais dire reset --hard. Il ne laisse aucun changement
Tim
1
@TimCastelijns git reset --hardest une commande destructive qui supprimera également les modifications locales non liées et le fera de manière irrécupérable! Ce n'est donc pas ce que le PO a demandé.
qwertzguy
2
Cette réponse serait beaucoup plus utile si vous expliquiez ce que sedfait cette commande, en quoi cela diffère du fait de le faire manuellement et ce que --autostashfait.
Chris Page