Comment puis-je récupérer / resynchroniser après que quelqu'un pousse un rebasage ou une réinitialisation à une branche publiée?
-
28-09-2019 - |
Question
Nous avons tous entendu que l'on ne devrait jamais le travail publié rebasage, qu'il est dangereux, etc. Cependant, je ne l'ai pas vu de recettes affichées sur la façon de faire face à la situation au cas où un rebasage publié .
Maintenant, ne note que ce n'est vraiment possible que si le dépôt est uniquement cloné par un groupe connu (et de préférence faible) de personnes, de sorte que celui qui pousse le rebasage ou remise à zéro peut informer tout le monde qu'ils devront faire attention prochaine fois qu'ils fetch (!).
Une solution évidente que je l'ai vu fonctionnera si vous n'avez pas commits locaux sur foo
et il devient rebasées:
git fetch
git checkout foo
git reset --hard origin/foo
Ce sera tout simplement jeter l'état local de foo
en faveur de son histoire par le dépôt distant.
Mais comment peut-on face à la situation si l'on a engagé des changements locaux importants sur cette branche?
La solution
Pour revenir en phase après un rebasage poussé est vraiment pas si compliqué que ça 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. D'abord, vous configurez un signet pour lequel la branche à distance était à l'origine, alors vous l'utiliser pour rejouer vos commits locaux partir de ce point sur la branche à distance rebasées.
rebasage est comme la violence: si elle ne résout pas votre problème, il vous suffit plus. ☺
Vous pouvez le faire sans le signet bien sûr, si vous regardez le pré-rebasage origin/foo
commettez ID et utiliser.
Ceci est également la façon dont vous traitez avec la situation dans laquelle vous avez oublié de faire un signet avant aller chercher. Rien ne se perd - il vous suffit de vérifier la reflog pour la branche à distance:
git reflog show origin/foo | awk '
PRINT_NEXT==1 { print $1; exit }
/fetch: forced-update/ { PRINT_NEXT=1 }'
imprimera la commettras ID qui origin/foo
pointé avant la dernière lecture qui a changé son histoire.
Vous pouvez alors simplement
git rebase --onto origin/foo $commit foo
Autres conseils
Je dirais que la section récupération de l'amont rebasage du git- page de manuel rebasage couvre à peu près tout cela.
Il est pas vraiment différent de récupérer de votre propre rebasage - vous déplacez une branche, et rebasage toutes les branches qu'il avait dans leur histoire sur sa nouvelle position
. A partir de git 1.9 / 2.0 Q1 2014, vous ne devez marquer l'origine précédente de la branche avant rebasage sur la branche amont réécrite, comme décrit dans Aristote Pagaltzis est réponse :
Voir engager 07d406b et commit d96855f :
Après avoir travaillé sur la branche
topic
créé avecgit checkout -b topic origin/master
, l'histoire deorigin/master
branche de suivi à distance peut-être rembobiné et reconstruit, conduisant à une histoire de cette forme:
o---B1
/
---o---o---B2--o---o---o---B (origin/master)
\
B3
\
Derived (topic)
où
origin/master
utilisé pour point commitsB3
,B2
,B1
et maintenant il pointe àB
, et votre branchetopic
a été lancé au-dessus de celui-ci en arrière quandorigin/master
était àB3
.Ce mode utilise le reflog de
origin/master
pour trouverB3
comme point de fourchette, de sorte que letopic
peut être rebasées au-dessus de laorigin/master
mis à jour 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 où une branche (ou toute l'histoire qui mène à
<commit>
) en forme de fourche d'une autre branche (ou toute référence)<ref>
.
Cela ne veut pas regarder juste pour l'ancêtre commun des deux commits, mais tient également compte de la reflog de<ref>
pour voir si l'histoire menant à<commit>
fourchue d'une incarnation antérieure de la branche<ref>
.
La commande «
git pull --rebase
» calcule le point de fourche de la branche étant rebasées en utilisant les entrées reflog de la branche «base
» (typiquement une branche de suivi à distance) du travail de la branche a été basée sur, en vue de faire face au cas qui la branche « de base » a été rembobiné et reconstruit.
Par exemple, si l'histoire ressemblait où:
- l'extrémité actuelle de la branche «
base
» est àB
, mais a observé que précédemment La récupération de la pointe utilisée pour êtreB3
puisB2
puisB1
avant d'arriver à la validation en cours et- la branche étant rebasées au-dessus de la dernière « base » est basée sur
B3
engagement,il essaie de trouver
B3
en passant par la sortie de «git rev-list --reflog base
» (à savoirB
,B1
,B2
,B3
) jusqu'à ce qu'il trouve un commit qui est un ancêtre de la pointe de courant «Derived (topic)
».En interne, nous avons
get_merge_bases_many()
qui peut calculer cela avec un go.
Nous voulons une fusion-base entreDerived
et une fusion fictive commettras qui résulterait de la fusion de tous les conseils historiques de «base (origin/master)
».
Lorsqu'un tel commit exist, nous devrions obtenir un seul résultat qui correspond exactement l'une des entrées de reflog «base
».
Git 2.1 (Q3 2014) ajoutera rendre cette fonctionnalité plus robuste à ce sujet: voir commettre 1e0dacd par John Keeping (johnkeeping
)
gérer correctement le scénario dans lequel nousont la topologie suivante:
C --- D --- E <- dev
/
B <- master@{1}
/
o --- B' --- C* --- D* <- master
où:
-
B'
est une version fixe d'B
qui est patch identique pasB
; -
C*
etD*
sont patch-identique àC
etD
respectivement et les conflits si elle est appliquée textuellement dans le mauvais ordre; -
E
dépend textuellement surD
.
Le résultat correct de git rebase master dev
est que B
est identifié comme étant le point de la fourchette dev
et master
, de sorte que C
, D
, E
sont les commits qui doivent être rejoué sur master
; mais C
et D
sont patch-identique à C*
et D*
et peut donc être diminué, de sorte que le résultat final est le suivant:
o --- B' --- C* --- D* --- E <- dev
Si le point de fourche est pas identifié, puis la cueillette B
sur une branche contenant les résultats de B'
dans un conflit et si les commits patch identiques ne sont pas correctement identifiés puis choisir C
sur une branche contenant D
(ou de manière équivalente D*
) entraîne un conflit.