Comment puis-je récupérer/resynchroniser après que quelqu'un a poussé un rebase ou un reset à une branche publiée?

nous avons tous entendu qu'il ne faut jamais rebaser les travaux publiés, qu'il est dangereux, etc. Cependant, je n'ai pas vu de recettes postées pour savoir comment faire face à la situation dans le cas où un rebase est publié.

maintenant, notez que cela n'est vraiment faisable que si le dépôt est seulement cloné par un groupe connu (et de préférence petit) de personnes, de sorte que quiconque pousse le rebase ou réinitialiser peut notifier à tout le monde qu'ils devront payer attention la prochaine fois qu'ils vont chercher(!).

une solution évidente que j'ai vu fonctionnera si vous n'avez pas de commits locaux sur foo et il obtient rebasé:

git fetch
git checkout foo
git reset --hard origin/foo

cela va simplement jeter l'état local de foo en faveur de son histoire selon le dépôt à distance.

mais comment faire face à la situation si on a engagé des changements locaux substantiels sur cette branche?

82
demandé sur Aristotle Pagaltzis 2010-11-03 10:08:19
la source

3 ответов

revenir dans la synchronisation après un rebase poussé n'est vraiment pas si compliqué dans la plupart des cas.

git checkout foo
git branch old-foo origin/foo # BEFORE fetching!!
git fetch
git rebase --onto origin/foo old-foo foo
git branch -D old-foo

Ie. tout d'abord, vous mettez en place un signet pour l'endroit où la branche distante était à l'origine, puis vous l'utilisez pour rejouer vos propagations locales à partir de ce point sur la branche distante rebasée.

rebaser, c'est comme la violence: si ça ne résout pas votre problème, il vous en faut plus. ☺

vous pouvez le faire sans signet bien sûr, si vous regardez en haut de la pré-rebase origin/foo commettre ID, et l'utiliser.

c'est aussi la façon dont vous gérez la situation où vous avez oublié de faire un signet avant fetching. Rien n'est perdu – vous avez juste besoin de vérifier le reclog pour la branche distante:

git reflog show origin/foo | awk '
    PRINT_NEXT==1 { print ; exit }
    /fetch: forced-update/ { PRINT_NEXT=1 }'

cela affichera l'identifiant de propagation que origin/foo a indiqué avant le dernier fetch qui a changé son histoire.

vous pouvez alors simplement

git rebase --onto origin/foo $commit foo
72
répondu Aristotle Pagaltzis 2010-11-03 10:08:40
la source

je dirais que la section récupérant du rebase amont de la page de manuel git-rebase couvre à peu près tout cela.

ce n'est vraiment pas différent de récupérer de votre propre rebase - vous déplacez une branche, et rebase toutes les branches qui l'ont eu dans leur histoire sur sa nouvelle position.

11
répondu Cascabel 2010-11-03 18:39:19
la source

à partir de git 1.9 / 2.0 Q1 2014, vous n'aurez pas à marquer l'origine de votre branche précédente avant de la réécrire sur la branche amont, comme décrit dans Aristotle Pagaltzis 's réponse :

Voir commettre 07d406b et commettre d96855f :

après avoir travaillé sur la branche topic créée avec git checkout -b topic origin/master , l'histoire de la branche télécommandée origin/master peut avoir été rebobinée et reconstruite, conduisant à une histoire de cette forme:

                   o---B1
                  /
  ---o---o---B2--o---o---o---B (origin/master)
          \
           B3
            \
             Derived (topic)

origin/master utilisé pour pointer à engage B3 , B2 , B1 et maintenant il pointe à B , et votre branche topic a été commencée par dessus quand origin/master était à B3 .

ce mode utilise le reflog de origin/master pour trouver B3 comme le point de branchement, de sorte que la topic peut être relocalisée sur le dessus de la mise à jour origin/master par:

$ fork_point=$(git merge-base --fork-point origin/master topic)
$ git rebase --onto origin/master $fork_point topic

C'est pourquoi la commande git merge-base a une nouvelle option:

--fork-point::

trouver le point auquel une branche (ou toute histoire qui conduit à <commit> ) bifurqué d'une autre branche (ou toute référence) <ref> .

Cela ne cherche pas seulement l'ancêtre commun des deux commits, mais prend également en compte le carnet de notes de <ref> pour voir si l'histoire conduisant à <commit> bifurqué d'une incarnation antérieure de la branche <ref> 1519930920".


la commande git pull --rebase " calcule le point de bifurcation de la branche rebasée en utilisant le recalculer les entrées de la branche " base " (généralement une branche de suivi à distance) sur laquelle le travail de la branche était basé, afin de faire face au cas où la branche "base" a été recalculée et reconstruite.

par exemple, si l'histoire ressemble à où:

  • l'extrémité actuelle de la " base "branche est à B , mais fetch plus tôt observé que son extrémité utilisée pour être B3 et puis B2 et puis B1 avant d'arriver à la commit actuelle, et
  • la branche étant rebasée en haut de la dernière "base" est basée sur commit B3 ,

il tente de trouver B3 en passant par la sortie de " git rev-list --reflog base " (i.e. B , B1 , B2 , B3 ) jusqu'à ce qu'il trouve un commit qui est un ancêtre de la pointe actuelle " Derived (topic) ".

à l'interne, nous avons get_merge_bases_many() qui peut calculer ceci avec un-go.

Nous voudrions une fusion-base entre Derived et une fusion fictive commit qui résulterait en fusionnant tous les bouts historiques de " base (origin/master) ".

Quand une telle propagation existe, nous devrions obtenir un seul résultat, qui correspond exactement à l'une des entrées de " base ".


Git 2.1 (Q3 2014) va ajouter rendre cette fonctionnalité plus robuste à ceci: voir commit 1e0dacd par John Keeping ( johnkeeping )

gérer correctement le scénario où nous avons la topologie suivante:

    C --- D --- E  <- dev
   /
  B  <- [email protected]{1}
 /
o --- B' --- C* --- D*  <- master

où:

  • B' est une version fixe de B qui n'est pas patch-identique à B ;
  • C* et D* sont de patch-identique à C et D respectivement, et les conflits textuellement si appliqué dans le mauvais ordre;
  • E dépend textuellement de D .

le résultat correct de git rebase master dev est que B est identifié comme le point de fourche de dev et master , de sorte que C , D , E sont les commits qui doivent être rejoués sur master ; mais C et D sont patch-identique avec C* et D* et donc peuvent être supprimés, de sorte que le résultat final est:

o --- B' --- C* --- D* --- E  <- dev

Si la fourche-point n'est pas identifié, puis en choisissant B sur une branche contenant B' les résultats dans un conflit et si le patch-identique s'engage ne sont pas correctement identifiés puis en choisissant C sur une branche contenant D (ou l'équivalent D* ) entraîne un conflit.

10
répondu VonC 2017-05-23 15:18:04
la source

Autres questions sur git git-reset git-rebase rebase