web-dev-qa-db-fra.com

Annuler Git Rebase

J'ai effectué un git rebase master sur ma branche et je n'ai réalisé que ce n'était pas ce que je voulais avant de l'avoir poussé vers la télécommande. C'est seulement moi et 1 autre personne qui travaillons sur le projet, donc je sais qu'ils n'ont pas apporté les derniers changements.

En lisant d'autres questions sur StackOverflow, il a dit d'utiliser git reflog et alors git reset --hard HEAD@{n} à avant le rebasage. J'ai fait cela pour accéder à un commit que j'ai créé avant le rebase, mais cela n'a pas restauré les choses comme avant.

Suis-je en train de manquer une étape? Existe-t-il un moyen de forcer l'autre personne à pousser son repo pour restaurer les choses comme elles étaient?

Merci

24
user1960364

Comme Makoto l'a déjà noté , vous ne devriez probablement pas prendre la peine de défaire ce rebase: c'est probablement ce que vous vouliez. Néanmoins, n'hésitez pas à lire pour savoir comment l'annuler.


Utilisez le reflog pour la branche, car il sera plus facile à lire. (Le reflog HEAD a les mêmes informations, mais contient beaucoup plus de choses, il est donc plus difficile de trouver ce que vous cherchez.)

Par exemple, si je venais de rebaser mybranch, je verrais:

$ git reflog mybranch
nnnnnnn mybranch@{0}: rebase finished: refs/heads/mybranch onto biguglysha1
ooooooo mybranch@{1}: commit: some sort of commit message
...

Le nom mybranch@{1} est donc synonyme (en ce moment) de ooooooo, l'ancien SHA-1 abrégé. Chaque fois que vous faites quelque chose à la branche (comme git reset) le nombre à l'intérieur du @{...} la partie changera, tandis que les SHA-1 sont toujours permanents, il est donc un peu plus sûr d'utiliser le SHA-1 (complet ou abrégé) pour le copier-coller.

Si vous alors:

$ git checkout mybranch # if needed

et:

$ git reset --hard ooooooo  # or mybranch@{1}

vous devriez avoir le dos d'origine. C'est parce que rebase simplement copies valide et déplace ensuite l'étiquette. Après le rebase, mais avant la réinitialisation, le graphique de validation ressemble à ceci, où A à C sont "vos" commits:

          A - B - C              <-- (only in reflog now)
        /
... - o - o - o - A' - B' - C'   <-- mybranch (after rebase)

et git reset simplement1 efface l'étiquette de branche actuelle et la colle sur le SHA-1 fourni (en transformant d'abord un nom de reflog en SHA-1 si nécessaire). Par conséquent, après le reset:

          A - B - C              <-- mybranch, plus older reflog
        /
... - o - o - o - A' - B' - C'   <-- (only in reflog now)

Notez que maintenant, après -reset, les copies de validation faites par rebase sont celles "abandonnées" qui ne se trouvent que dans les entrées de reflog. Les originaux, qui avaient été abandonnés, sont à nouveau revendiqués sous mybranch.

La façon de penser à ce genre de choses est de dessiner le graphique de validation (avec de nouvelles validations pointant vers leurs validations parentes), puis de dessiner des étiquettes de branche avec de longues flèches pointant vers le graphique de validation. Le graphique n'a jamais2 changements sauf pour ajouter nouveaux commits, qui ont de nouveaux et différents gros SHA-1 laids (c'est pourquoi j'utilise des lettres comme AB et C à la place, et des additifs comme A' pour les copies). Les SHA-1 sont garantis uniques3 et sont permanents, mais les étiquettes avec leurs longues flèches sont effacées et redirigées tout le temps. (Si vous faites cela sur un tableau blanc, vous devez généralement utiliser du noir pour le graphique de validation et une couleur, ou plusieurs couleurs, pour les étiquettes.)


1Bien, git reset fait plus que simplement déplacer l'étiquette, sauf si vous ajoutez des indicateurs de ligne de commande. Par défaut, il déplace l'étiquette et réinitialise l'index; avec --hard, il déplace l'étiquette et réinitialise l'index et nettoie votre arbre de travail. Avec --soft it juste déplace l'étiquette, laissant l'index et l'arbre de travail seuls. Avec git étant ce qu'il est, il y a un tas d'autres drapeaux qui tordent le sens encore plus, mais ce sont les trois grands: --soft, rien alias --mixed, et --hard.

2Si git n'ajoutait que des choses, votre repo deviendrait énorme au fil du temps. Donc, finalement, les commits "inaccessibles" - ceux qui n'ont pas d'étiquettes, et même pas d'entrées de reflog restantes, pointant vers elles, et qui ne sont pointées par certains commit qui le fait ont une étiquette , ou tout autre commit pointé - éventuellement ces commits inaccessibles (et tout autre objet inaccessible) sont supprimés lorsque git s'exécute git gc pour vous automatiquement. Vous pouvez les forcer à être retirés plus tôt, mais il y a rarement une bonne raison de vous embêter.

3Git lui-même dépend de la garantie. Il est mathématiquement possible, mais extrêmement improbable, que deux objets différents se retrouvent avec le même SHA-1. Si cela se produit, git se casse.4 Si la distribution est suffisamment bonne, la probabilité est de 1 sur 2160, ce qui est vraiment minuscule. C'est une bonne chose car le "Paradoxe d'anniversaire" soulève la possibilité assez rapidement, mais parce qu'il a commencé si petit, il reste petit et en pratique, cela n'a jamais été un problème.

4De par sa conception, la "casse" est que git cesse simplement d'ajouter des objets, de sorte que tout est encore bon jusqu'à présent. Vous devez ensuite passer à n'importe quel nouveau système conçu pour gérer des référentiels de milliards d'objets, le git de "prochaine génération" ou autre.

54
torek

J'ai de bonnes et de mauvaises nouvelles pour vous.

La bonne nouvelle est que vous êtes dans l'état que vous exprimez ce que vous voulez ! Le dernier commit sur master est en effet maintenant dans votre branche, et tout va bien avec l'univers.

La mauvaise nouvelle est que maintenant, avec votre opération de rebase, vous avez effectivement déplacé votre branche vers la pointe du maître; c'est-à-dire que votre branche de développement est maintenant dans le même état que si vous l'aviez simplement branchée depuis la pointe du maître.

Mais c'est [considéré comme] une bonne chose: à condition que vous n'ayez eu aucun conflit de fusion, vous n'avez pas perdu de données dans ce processus, et vous n'avez pas non plus de commit de fusion sans valeur sur votre branche.

Le livre explique très bien ce qu'est un rebase , donc je ne le répéterai pas. D'après les opérations que vous décrivez, vous ne devriez avoir perdu aucun travail.


Si vous souhaitez recréer votre historique d'origine avant de forcer la poussée, alors quelqu'un avec un historique non tiré peut forcer sa branche vers le référentiel distant. Cela entraînera l'état de votre repo comme ce qui était sur leur boîte au moment où ils ont poussé.

3
Makoto