Annulation d'un rebase git

3180

Comment puis-je facilement annuler une modification de git?

Mes idées actuelles ne sont que des approches manuelles:

  1. git checkout sur le parent de validation sur les deux branches
  2. Créez une branche temporaire à partir de là
  3. git cherry-pick tout s'engage à la main
  4. remplacer la branche dans laquelle j'ai rebasé par la branche créée manuellement

Dans ma situation actuelle, cela fonctionnerait parce que je peux facilement repérer les commits des deux branches (l'une était mon affaire, l'autre était celle de mon collègue).

Cependant, mon approche me semble sous-optimale et sujette aux erreurs (disons que je venais de rebaser avec 2 de mes propres branches).

Clarification : je parle d'un rebase pendant lequel un tas de commits ont été rejoués. Non seulement un.

webmat
la source
6
Notez également que lors d'un rebase, vous pouvez exclure les validations ou les écraser; ces modifications ne peuvent pas être annulées sans un pointeur sur les nœuds d'origine ou un filtrage à travers le reflog, de sorte que la sélection de cerise ne fonctionnerait pas.
ANeves

Réponses:

4338

Le moyen le plus simple serait de trouver le commit principal de la branche tel qu'il était juste avant le rebase dans le reflog ...

git reflog

et de réinitialiser la branche actuelle (avec les mises en garde habituelles d'être absolument sûr avant de réinitialiser avec l' --hardoption).

Supposons que l'ancien commit était HEAD@{5}dans le journal de référence:

git reset --hard HEAD@{5}

Sous Windows, vous devrez peut-être citer la référence:

git reset --hard "HEAD@{5}"

Vous pouvez vérifier l'historique de l'ancienne tête du candidat en faisant simplement un git log HEAD@{5}( Windows:) git log "HEAD@{5}" .

Si vous n'avez pas désactivé les reflogs par branche, vous devriez pouvoir le faire simplement git reflog branchname@{1}car un rebase détache la tête de branche avant de se reconnecter à la tête finale. Je revérifierais ceci, bien que je ne l'ai pas vérifié récemment.

Par défaut, tous les reflogs sont activés pour les référentiels non nus:

[core]
    logAllRefUpdates = true
CB Bailey
la source
115
Git reflog est génial, rappelez-vous simplement que vous pouvez obtenir une meilleure sortie formatée avec git log -g(astuce de progit.org/book de Scott Chacon ).
karmi
60
@Zach: git rebase --abort( -in'a aucun sens avec --abort) sert à abandonner un rebase qui n'a pas été terminé - soit parce qu'il y avait des conflits, soit parce qu'il était interactif ou les deux; il ne s'agit pas d'annuler un rebasage réussi, c'est de cela qu'il s'agit. Vous utiliseriez rebase --abortou reset --hardselon la situation dans laquelle vous vous trouviez. Vous ne devriez pas avoir besoin de faire les deux.
CB Bailey
311
Juste au cas où, effectuez une sauvegarde première: git tag BACKUP. Vous pouvez y revenir en cas de problème:git reset --hard BACKUP
kolypto
6
Si vous avez effectué de nombreuses validations, HEAD @ {#} que vous recherchez sera précédé commit:de rebase:. Cela semble évident, mais cela m'a un peu dérouté.
Warpling
4
Rejoindre le parti après un rebase accidentel: D. Ne ferait-il pas git reset --hard ORIG_HEADl'affaire aussi bien immédiatement après le rebase accidentel?
quaylar
1488

En fait, rebase enregistre votre point de départ ORIG_HEAD, c'est donc généralement aussi simple que:

git reset --hard ORIG_HEAD

Cependant, le reset, rebaseet mergetous enregistrent votre HEADpointeur d' origine dans ORIG_HEADainsi, si vous avez exécuté l'une de ces commandes depuis le rebase que vous essayez d'annuler, vous devrez utiliser le reflog.

Pat Notz
la source
34
Dans le cas où il ORIG_HEADn'est plus utile, vous pouvez également utiliser la branchName@{n}syntaxe, où nest la nième position antérieure du pointeur de branche. Ainsi, par exemple, si vous rebasez une featureAbranche sur votre masterbranche, mais que vous n'aimez pas le résultat de la rebase, vous pouvez simplement faire git reset --hard featureA@{1}pour réinitialiser la branche exactement à l'endroit où elle se trouvait avant de rebaser. Vous pouvez en savoir plus sur la syntaxe branch @ {n} dans les documents officiels de Git pour les révisions .
15
C'est le plus simple. Suivez-le avec un git rebase --abortbien.
Seph
1
@DaBlick, cela a bien fonctionné pour moi après un rebase complètement réussi sans conflits git 2.17.0.
dvlsg
4
Et permettez-moi de le compléter: git reset --hard ORIG_HEADpeut utiliser à plusieurs reprises pour revenir en arrière encore et encore. Dites, si A --- rebase à --- B --- rebase à --- C, maintenant je suis à C, je peux revenir à A en utilisant deux foisgit reset --hard ORIG_HEAD
CalvinChe
5
@Seph Pouvez-vous expliquer pourquoi vous proposez un suivi git rebase --abort?
UpTheCreek
386

La réponse de Charles fonctionne, mais vous voudrez peut-être faire ceci:

git rebase --abort

nettoyer après le reset.

Sinon, vous pouvez obtenir le message « Interactive rebase already started».

Allan
la source
4
Celui-ci a supprimé la partie "| REBASE" dans mon invite. +1
Wouter Thielen
62
Ce n'était pas la question. La question demande comment annuler un rebase terminé.
Arunav Sanyal
2
Devrait être un commentaire sur la réponse de Charles, car il ne répond pas à la question sur son
Murmel
Hm, je n'avais pas à faire ça.
Viktor Seč
1
Le seul qui a fonctionné pour moi!
Alexandre Picard
90

La réinitialisation de la branche à l'objet de validation pendant de son ancienne astuce est bien sûr la meilleure solution, car elle restaure l'état précédent sans aucun effort. Mais si vous avez perdu ces validations (par exemple, parce que vous avez récupéré votre référentiel entre-temps, ou s'il s'agit d'un nouveau clone), vous pouvez toujours rebaser la branche à nouveau. La clé est le --ontocommutateur.

Supposons que vous ayez une branche de sujet appelée avec imagination topic, que vous avez bifurquée masterlorsque la pointe de la commande a masterété 0deadbeefvalidée. À un moment donné pendant que topicvous étiez sur la branche, vous l'avez fait git rebase master. Vous voulez maintenant annuler cela. Voici comment:

git rebase --onto 0deadbeef master topic

Cela prendra toutes les validations topicqui ne sont pas masteractivées et les rejouera par dessus 0deadbeef.

Avec --onto, vous pouvez réorganiser votre histoire dans à peu près n'importe quelle forme .

S'amuser. :-)

Aristote Pagaltzis
la source
3
Je pense que c'est la meilleure option en raison de sa flexibilité. J'ai ramifié b1 hors du maître, puis rebasé b1 dans une nouvelle branche b2, puis j'ai voulu rétablir b1 pour qu'il soit à nouveau basé sur le maître. J'adore git - merci!
ripper234
2
C'est la meilleure option ici! Il a conservé toutes les modifications que j'ai apportées à ma branche actuelle et a supprimé toutes les modifications indésirables!
Alicia Tang
Je dirais qu'avec une combinaison de --ontoet -ivous pouvez réorganiser votre histoire dans à peu près n'importe quelle forme. Utilisez gitk (ou gitx sur mac) pour voir les formes que vous créez :-).
rjmunro
69

En fait, j'ai mis une balise de sauvegarde sur la branche avant de faire une opération non triviale (la plupart des rebases sont triviales, mais je le ferais si cela semble complexe).

Ensuite, la restauration est aussi simple que git reset --hard BACKUP.

Alex Gontmakher
la source
2
Je fais ça aussi. Il est également utile de git diff BACKUP..HEAD pour vous assurer que vous avez changé ce que vous vouliez dire.
Paul Bone
5
J'avais l'habitude de le faire aussi, mais depuis que je suis plus à l'aise avec le reflog, je ne pense plus que ce soit nécessaire. Le reflog fait essentiellement cela en votre nom chaque fois que vous changez de HEAD.
Pete Hodgson
4
Eh bien, je préfère les noms significatifs, car rechercher le bon élément dans le reflog n'est parfois pas amusant du tout.
Alex Gontmakher
12
En fait, vous n'avez même pas besoin de créer une branche de sauvegarde, vous pouvez simplement utiliser la branchName@{n}syntaxe, voici nla nième position antérieure du pointeur de branche. Ainsi, par exemple, si vous rebasez une featureAbranche sur votre masterbranche, mais que vous n'aimez pas le résultat de la rebase, vous pouvez simplement faire git reset --hard featureA@{1}pour réinitialiser la branche exactement à l'endroit où elle se trouvait avant de rebaser. Vous pouvez en savoir plus sur la branch@{n}syntaxe dans les documents officiels de Git pour les révisions .
2
En fait, vous n'avez même pas besoin d'utiliser la syntaxe ci-dessus, selon la réponse de Pat Notz , l'original HEADde la branche est temporairement stocké dans ORIG_HEAD. Mais la technique d'utilisation d'une étiquette de branche de sauvegarde fonctionne également, c'est juste plus d'étapes.
68

Dans le cas où vous aviez poussé votre branche vers un référentiel distant (généralement son origine) et que vous avez ensuite effectué un rebase réussi (sans fusion) ( git rebase --abortdonne "Pas de rebase en cours"), vous pouvez facilement réinitialiser la branche en utilisant la commande:

git reset --hard origin / {branchName}

Exemple:

$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is ahead of 'origin/{branchName}' by 135 commits.
  (use "git push" to publish your local commits)

nothing to commit, working directory clean

$ ~/work/projects/{ProjectName} $ git reset --hard origin/{branchName}
HEAD is now at 6df5719 "Commit message".

$ ~/work/projects/{ProjectName} $ git status
On branch {branchName}
Your branch is up-to-date with 'origin/{branchName}.

nothing to commit, working directory clean
Maksym
la source
C'est la bonne réponse pour moi. Rebaser et valider avant rebaser avait le même ID de validation, et revenir à HEAD {1} ne reviendrait tout simplement pas à rebaser!
Bill Kotsias
23

L'utilisation reflogn'a pas fonctionné pour moi.

Ce qui a fonctionné pour moi était similaire à celui décrit ici . Ouvrez le fichier dans .git / logs / refs nommé d'après la branche qui a été rebasée et recherchez la ligne qui contient "rebase finsihed", quelque chose comme:

5fce6b51 88552c8f Kris Leech <[email protected]> 1329744625 +0000  rebase finished: refs/heads/integrate onto 9e460878

Extraire le deuxième commit répertorié sur la ligne.

git checkout 88552c8f

Une fois confirmé, cela contenait mes changements perdus, je me suis ramifié et j'ai poussé un soupir de soulagement.

git log
git checkout -b lost_changes
Kris
la source
3
Whoa - à partir de ce lien, "Il y a une mise en garde: j'ai perdu l'historique de la branche mais dans ce cas, cela n'avait pas vraiment d'importance. J'étais simplement heureux d'avoir récupéré mes modifications." ?
ruffin
16

Pour plusieurs validations, n'oubliez pas que toute validation fait référence à tout l'historique menant à cette validation. Ainsi, dans la réponse de Charles, lisez «l'ancien commit» comme «le plus récent des anciens commits». Si vous réinitialisez ce commit, tout l'historique menant à ce commit réapparaîtra. Cela devrait faire ce que vous voulez.

Greg Hewgill
la source
11

Suite à la solution de @Allan et @Zearin, je souhaite simplement pouvoir faire un commentaire mais je n'ai pas assez de réputation, j'ai donc utilisé la commande suivante:

Au lieu de faire git rebase -i --abort (notez le -i ), je devais simplement le faire git rebase --abort( sans le -i ).

En utilisant les deux -iet --aborten même temps, Git me montre une liste d'utilisation / d'options.

Mon statut de branche précédent et actuel avec cette solution est donc:

matbhz@myPc /my/project/environment (branch-123|REBASE-i)
$ git rebase --abort

matbhz@myPc /my/project/environment (branch-123)
$
Matheus Felipe
la source
11

Si vous avez réussi à rebaser contre une branche distante et que git rebase --abortvous ne pouvez pas, vous pouvez toujours faire quelques astuces pour enregistrer votre travail et ne pas avoir de push forcé. Supposons que votre branche actuelle qui a été rebasée par erreur soit appelée your-branchet effectue le suiviorigin/your-branch

  • git branch -m your-branch-rebased # renommer la branche actuelle
  • git checkout origin/your-branch # paiement vers le dernier état connu d'origine
  • git checkout -b your-branch
  • vérifier git log your-branch-rebased, comparer git log your-branchet définir les commits manquants dansyour-branch
  • git cherry-pick COMMIT_HASH pour chaque commit your-branch-rebased
  • pousser vos changements. Veuillez noter que deux succursales locales sont associées remote/your-branchet que vous ne devez pousseryour-branch
Sergey P. aka azur
la source
4

Disons que je rebase master dans ma branche de fonctionnalité et que je reçois 30 nouveaux commits qui cassent quelque chose. J'ai constaté que, souvent, il est plus facile de simplement supprimer les mauvais commits.

git rebase -i HEAD~31

Rebase interactive pour les 31 derniers commits (cela ne fait pas de mal si vous en choisissez beaucoup trop).

Prenez simplement les commits dont vous voulez vous débarrasser et marquez-les avec "d" au lieu de "choisir". Désormais, les validations sont supprimées, ce qui annule la réinitialisation (si vous supprimez uniquement les validations que vous venez d'obtenir lors de la réinitialisation).

Hardev
la source
3

Pour les débutants / ceux qui ont trop peur de faire une réinitialisation matérielle, vous pouvez extraire le commit du reflog, puis l'enregistrer en tant que nouvelle branche.

git reflog

Trouvez le commit juste avant de commencer le rebasage. Vous devrez peut-être faire défiler plus bas pour le trouver (appuyez sur Entrée ou PageDown). Prenez note du numéro HEAD et remplacez 57:

git checkout HEAD@{57}

Passez en revue la branche / commits, si cela semble bon, créez une nouvelle branche en utilisant cette HEAD:

git checkout -b new_branch_name
Andrew
la source
2

Si vous êtes sur une succursale, vous pouvez utiliser:

git reset --hard @{1}

Il n'y a pas seulement un journal de référence pour HEAD (obtenu par git reflog), il existe également des reflogs pour chaque branche (obtenu par git reflog <branch>). Donc, si vous êtes masteractivé git reflog master, la liste de toutes les modifications apportées à cette branche sera répertoriée. Vous pouvez consulter que les changements par master@{1}, master@{2}, etc.

git rebase changera généralement HEAD plusieurs fois mais la branche actuelle ne sera mise à jour qu'une seule fois.

@{1}est simplement un raccourci pour la branche en cours , il est donc égal à master@{1}si vous êtes activé master.

git reset --hard ORIG_HEADne fonctionnera pas si vous l'avez utilisé git resetlors d'une interaction rebase.

devconsole
la source
1

Ce que je fais d'habitude c'est git reset #commit_hash

au dernier commit où je pense que le rebase n'a eu aucun effet.

puis git pull

Maintenant, votre branche doit correspondre exactement comme maître et les validations rebasées ne doivent pas y être.

Maintenant, on peut juste choisir les commits sur cette branche.

mrigendra
la source
1

J'ai essayé toutes les suggestions avec reset et reflog sans succès. La restauration de l'historique local d'IntelliJ a résolu le problème des fichiers perdus

user3638751
la source
Merci! Je n'ai jamais utilisé l'historique local auparavant, mais cela s'est avéré être l'option la plus simple pour moi de récupérer du code rebasé accidentellement. Fonction très agréable mais quelque peu cachée.
Magnus W
0

git reset --hard origin / {branchName}

est la bonne solution pour réinitialiser toutes vos modifications locales effectuées par rebase.

Damodar P
la source
1
si vous effectuez une réinitialisation matérielle, origin/branchvous risquez de perdre les modifications entre HEAD et ce point. Vous ne voulez pas cela d'une manière générale.
bluesmonk
C'est exactement ce que je voulais dans mon cas. Donc vote positif.
Mandar Vaze
-4

Si vous gâchez quelque chose dans un rebase git, par exemple git rebase --abort, pendant que vous avez des fichiers non validés , ils seront perdus et git reflogne vous aideront pas. Cela m'est arrivé et vous devrez sortir des sentiers battus ici. Si vous avez de la chance comme moi et que vous utilisez IntelliJ Webstorm, vous pouvez right-click->local historyet pouvez revenir à un état précédent de vos fichiers / dossiers, quelles que soient les erreurs que vous avez faites avec le logiciel de version. Il est toujours bon d'avoir une autre sécurité intégrée en cours d'exécution.

stevek
la source
5
git rebase --abortAbandonne un rebasage actif, il n'undo un rebasage. De plus, utiliser deux VCS en même temps est une mauvaise idée. C'est une fonctionnalité intéressante du logiciel Jetbrains, mais vous ne devez pas utiliser les deux. Il est préférable d'apprendre simplement Git, en particulier lorsque vous répondez à des questions sur Stack Overflow concernant Git.
dudewad