Refaire une fusion inversée dans Git

231

J'ai rencontré un petit problème ici: j'avais une branche spécifique au problème 28sdans Git, que j'ai fusionnée dans la developbranche générale . Il s'avère que je l'avais fait trop vite, j'ai donc utilisé git-revert pour annuler la fusion. Maintenant, cependant, le temps est venu de fusionner 28sen develop, mais la commande git-fusion voit la fusion d' origine, et annonce joyeusement que tout va bien et les branches ont déjà été fusionnées. Qu'est-ce que je fais maintenant? Créer un 'Revert "Revert" 28s -> develop ""' commit? Cela ne semble pas être un bon moyen de le faire, mais je ne peux en imaginer aucun autre pour le moment.

À quoi ressemble la structure arborescente:

Sortie du journal Git

Toms Mikoss
la source
4
C'est GitX ( gitx.frim.nl ).
Toms Mikoss

Réponses:

169

Vous devez "revenir en arrière". Selon vous, comment l'original est-il revenu, cela peut ne pas être aussi simple qu'il y paraît. Regardez le document officiel sur ce sujet .

---o---o---o---M---x---x---W---x---Y
              /
      ---A---B-------------------C---D

autoriser:

---o---o---o---M---x---x-------x-------*
              /                       /
      ---A---B-------------------C---D

Mais tout fonctionne-t-il? Bien sûr. Vous pouvez annuler une fusion, et d'un point de vue purement technique, git l'a fait très naturellement et n'a eu aucun vrai problème.
Il a simplement considéré que c'était un changement de "l'état avant la fusion" à "l'état après la fusion", et c'était tout.
Rien de compliqué, rien d'étrange, rien de vraiment dangereux. Git le fera sans même y penser.

Donc, d'un point de vue technique, il n'y a rien de mal à annuler une fusion, mais sous l'angle du flux de travail, c'est quelque chose que vous devriez généralement essayer d'éviter .

Si possible, par exemple, si vous trouvez un problème qui a été fusionné dans l'arborescence principale, plutôt que d'annuler la fusion, essayez vraiment de :

  • bissectez le problème dans la branche que vous avez fusionnée et corrigez-le,
  • ou essayez d'annuler le commit individuel qui l'a provoqué.

Oui, c'est plus complexe, et non, ça ne marchera pas toujours (parfois la réponse est: "oups, je n'aurais vraiment pas dû le fusionner, car il n'était pas encore prêt, et j'ai vraiment besoin de défaire tous les fusionner"). Donc, vous devez vraiment annuler la fusion, mais lorsque vous souhaitez refaire la fusion, vous devez maintenant le faire en annulant la restauration.

J-16 SDiZ
la source
10
Bon lien (+1). J'ai pris la liberté de copier une partie du document dans votre réponse afin de permettre aux lecteurs de voir immédiatement les options pertinentes dans ce cas. Si vous n'êtes pas d'accord, n'hésitez pas à revenir.
VonC
5
Nous avons juste rencontré un cas où nous devions le faire et nous avons constaté que le plaisir ne s'arrêtait pas là. C'était une branche de longue date qui a été fusionnée, nous avons donc dû continuer à la mettre à jour. Mon approche ici: tech.patientslikeme.com/2010/09/29/…
jdwyah
J'ai suivi le billet de blog de @ jdwyah et c'était glorieux (sérieusement, c'était génial que ça ait juste fonctionné).
hellatan
2
@jdwyah qui semble être un lien cassé, mais cela ressemble à une lecture intéressante. Voici un miroir archive.org mais il manque les images: web.archive.org/web/20111229193713/http://…
Alex KeySmith
15
Le billet de blog a été ressuscité, merci: blog.jdwyah.com/2015/07/dealing-with-git-merge-revisions.html
jdwyah
53

Supposons que vous ayez une telle histoire

---o---o---o---M---W---x-------x-------*
              /                      
      ---A---B

Où A, B ont échoué et W - revient à M

Donc, avant de commencer à corriger les problèmes détectés, je sélectionne W pour m'engager dans ma branche

git cherry-pick -x W

Puis je reviens W commit sur ma branche

git revert W 

Après je peux continuer à réparer.

L'histoire finale pourrait ressembler à:

---o---o---o---M---W---x-------x-------*
              /                       /     
      ---A---B---W---W`----------C---D

Lorsque j'envoie un PR, cela montre clairement que le PR est annulé et ajoute de nouveaux commits.

Maksim Kotlyar
la source
3
Cela pourrait être utile, mais est si rare sur les détails (que sont C, D dans le dernier diagramme) que c'est plus frustrant qu'utile
Isochronous
4
@Isochronous C et D semblent être des commits qui corrigent les problèmes introduits par A et B.
Thomas
@Thomas exactement
Maksim Kotlyar
Il est bon que vous ayez mis en évidence l'utilisation d'un PR dans ce cas, car il peut fournir un `` contrôle d'esprit '' final avant de fusionner à nouveau avec le maître.
Willem van Ketwich du
utiliser pourquoi redémarrez-vous votre branche à partir du W d'origine? C'est-à-dire, laissez votre sujet continuer de W, avec W` suivi de C et D? Cela supprime certains doublons
Erik Carstensen
12

Pour annuler le retour sans trop gâcher votre flux de travail:

  • Créer une copie de corbeille locale de develop
  • Annuler la validation de la restauration sur la copie locale de develop
  • Fusionnez cette copie dans votre branche de fonctionnalités et envoyez votre branche de fonctionnalités à votre serveur git.

Votre branche de fonctionnalité devrait maintenant pouvoir être fusionnée normalement lorsque vous y êtes prêt. Le seul inconvénient ici est que vous aurez quelques commits de fusion / restauration supplémentaires dans votre historique.

Sam Dufel
la source
Juste pour éviter toute confusion supplémentaire, j'ai également créé une copie «poubelle» de ma branche de fonctionnalités et y ai fusionné le développement inversé.
jhhwilliams
Je vous remercie! C'est la seule réponse qui explique réellement comment le faire au lieu de dire que vous ne devriez pas le faire. Vraiment utile.
Emmy
Merci, cela m'a vraiment aidé. :)
Daniel Stracaboško
11

Pour annuler une restauration dans GIT:

git revert <commit-hash-of-previous-revert>
Richard
la source
1
En utilisant cela dans ma branche de travail pour annuler le retour, puis de nouveaux RP à développer. Maintenant, git voit tous les changements qui étaient dans le PR précédent qui a été annulé. Merci.
Danstan
3

Au lieu d'utiliser, git-revertvous auriez pu utiliser cette commande dans la develbranche pour supprimer (annuler) le mauvais commit de fusion (au lieu de simplement le rétablir).

git checkout devel
git reset --hard COMMIT_BEFORE_WRONG_MERGE

Cela ajustera également le contenu du répertoire de travail en conséquence. Attention :

  • Enregistrez vos modifications dans la branche develop (car la mauvaise fusion) car elles aussi seront effacées par le git-reset. Tous les commits après celui que vous spécifiez car l' git resetargument aura disparu!
  • De plus, ne le faites pas si vos modifications ont déjà été extraites d'autres référentiels car la réinitialisation réécrira l'historique.

Je recommande d'étudier git-resetattentivement la page de manuel avant d'essayer.

Maintenant, après la réinitialisation, vous pouvez réappliquer vos modifications devel, puis faire

git checkout devel
git merge 28s

Ce sera une véritable fusion d' 28sen develcomme la première (qui est maintenant effacée de l'histoire de git).

knweiss
la source
8
Pour ceux qui ne sont pas très familiers avec git et qui voudront peut-être suivre ces instructions: faites attention à combiner reset --hardet push origin. Sachez également qu'une poussée forcée vers l'origine peut vraiment gâcher les relations publiques ouvertes sur GitHub.
funroll le
Très utile pour résoudre certains problèmes de fusion sur un serveur git privé. Merci!
mix3d
1
+1 pour cette technique. Potentiellement destructeur, mais peut vous faire économiser beaucoup de maux de tête (et un historique altéré) lorsqu'il est appliqué judicieusement.
siliconrockstar
1

Je viens de trouver ce message face au même problème. Je trouve ci-dessus un moyen effrayant de réinitialiser les disques durs, etc. Je finirai par supprimer quelque chose que je ne veux pas et je ne pourrai pas le récupérer.

Au lieu de cela, j'ai vérifié le commit auquel je voulais que la branche revienne, par exemple git checkout 123466t7632723. Puis converti en branche git checkout my-new-branch. J'ai ensuite supprimé la branche dont je ne voulais plus. Bien sûr, cela ne fonctionnera que si vous êtes capable de jeter la branche que vous avez gâchée.

Claire
la source
1
Le git reflogvous protégera contre une réinitialisation matérielle pendant quelques mois au cas où vous découvririez plus tard que vous avez besoin des commits perdus. Le reflog est limité à votre dépôt local.
Todd
1

Je vous suggère de suivre les étapes ci-dessous pour annuler un retour, par exemple SHA1.

git checkout develop #go to develop branch
git pull             #get the latest from remote/develop branch
git branch users/yourname/revertOfSHA1 #having HEAD referring to develop
git checkout users/yourname/revertOfSHA1 #checkout the newly created branch
git log --oneline --graph --decorate #find the SHA of the revert in the history, say SHA1
git revert SHA1
git push --set-upstream origin users/yourname/revertOfSHA1 #push the changes to remote

Créez maintenant PR pour la branche users/yourname/revertOfSHA1

Venkataraman R
la source
1
  1. créer une nouvelle branche lors de la validation avant la fusion d'origine - appelez-la 'develop-base'
  2. effectuer un rebase interactif de «develop» au-dessus de «develop-base» (même s'il est déjà au dessus). Pendant le rebase interactif, vous aurez la possibilité de supprimer à la fois le commit de fusion et le commit qui a inversé la fusion, c'est-à-dire supprimer les deux événements de l'historique git

À ce stade, vous aurez une branche de développement propre à laquelle vous pourrez fusionner votre fonctionnalité comme vous le faites régulièrement.

Misha Mostov
la source
Cette approche créera un problème si la branche de développement est partagée avec d'autres.
PSR