Nous avons tous entendu qu'il ne fallait jamais rebaser un travail publié, que c'était dangereux, etc. Cependant, je n'ai vu aucune recette publiée pour savoir comment gérer la situation au cas où un rebaser est publié.
Maintenant, notez que cela n'est vraiment possible que si le référentiel n'est cloné que par un groupe de personnes connu (et de préférence petit), de sorte que quiconque pousse le rebase ou la réinitialisation puisse informer tout le monde qu'il devra faire attention la prochaine fois qu'il récupérer (!).
Une solution évidente que j'ai vue fonctionnera si vous n'avez pas de commit local sur foo
et qu'elle est rebasée:
git fetch
git checkout foo
git reset --hard Origin/foo
Cela supprimera simplement l'état local de foo
au profit de son historique selon le référentiel distant.
Mais comment gérer la situation si l'on a commis des changements locaux importants sur cette branche?
Revenir en synchronisme 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
C'est à dire. vous définissez d'abord un signet pour l'emplacement d'origine de la branche distante, puis vous l'utilisez pour rejouer vos validations locales à partir de ce point sur la branche distante rebasée.
Le remodelage est comme la violence: s'il ne résout pas votre problème, vous en avez juste besoin de plus. ☺
Vous pouvez le faire sans le signet bien sûr, si vous recherchez le pré-rebase Origin/foo
ID de validation, et utilisez-le.
C'est aussi ainsi que vous traitez la situation où vous avez oublié de créer un signet avant aller chercher. Rien n'est perdu - il vous suffit de vérifier le reflog de la branche distante:
git reflog show Origin/foo | awk '
PRINT_NEXT==1 { print $1; exit }
/fetch: forced-update/ { PRINT_NEXT=1 }'
Cela affichera l'ID de validation qui Origin/foo
a souligné avant la dernière extraction qui a changé son histoire.
Vous pouvez alors simplement
git rebase --onto Origin/foo $commit foo
À 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 rebaser sur la branche amont réécrite, comme décrit dans Aristotle Pagaltzis 's answer =:
Voir commit 07d406b et commit d96855f :
Après avoir travaillé sur la branche
topic
créée avecgit checkout -b topic Origin/master
, L'historique de la branche de suivi à distanceOrigin/master
Peut avoir été rembobiné et reconstruit, conduisant à un historique de cette forme:
o---B1
/
---o---o---B2--o---o---o---B (Origin/master)
\
B3
\
Derived (topic)
où
Origin/master
pointait sur les commitsB3
,B2
,B1
et maintenant il pointe surB
et votretopic
la branche a été démarrée par dessus quandOrigin/master
était àB3
.Ce mode utilise le reflog de
Origin/master
Pour trouverB3
Comme point de départ, afin que letopic
puisse être rebasé au-dessus du mis à jourOrigin/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::
Recherchez le point auquel une branche (ou tout historique menant à
<commit>
) Se bifurque à partir d'une autre branche (ou de toute référence)<ref>
.
Cela ne recherche pas seulement l'ancêtre commun des deux commits, mais prend également en compte le reflog de<ref>
Pour voir si l'historique menant à<commit>
Issu d'une incarnation antérieure de la branche<ref>
.
La commande "
git pull --rebase
" Calcule le point de départ de la branche en cours de rebasage à l'aide des entrées de reflog de la branche "base
" (généralement une branche de suivi à distance) sur laquelle le travail de la branche était basé pour faire face au cas où la branche "base" a été rembobinée et reconstruite.
Par exemple, si l'historique ressemblait à où:
- la pointe actuelle de la branche "
base
" est àB
, mais une extraction antérieure a observé que sa pointe était auparavantB3
, puisB2
, puisB1
Avant d'arriver au commit actuel, et- la branche qui est rebasée au-dessus de la dernière "base" est basée sur la validation
B3
,il essaie de trouver
B3
en passant par la sortie de "git rev-list --reflog base
" (c'est-à-direB
,B1
,B2
,B3
) Jusqu'à ce qu'il trouve une validation qui est un ancêtre de l'astuce actuelle "Derived (topic)
".En interne, nous avons
get_merge_bases_many()
qui peut calculer cela en une seule fois.
Nous voudrions une base de fusion entreDerived
et une validation de fusion fictive qui résulterait de la fusion de toutes les astuces historiques de "base (Origin/master)
".
Lorsqu'un tel commit existe, nous devrions obtenir un seul résultat, qui correspond exactement à l'une des entrées de reflog de "base
".
Git 2.1 (Q3 2014) ajoutera rendre cette fonctionnalité plus robuste à cela: voir commit 1e0dacd by John Keeping (johnkeeping
)
gérer correctement le scénario où nous avons la topologie suivante:
C --- D --- E <- dev
/
B <- master@{1}
/
o --- B' --- C* --- D* <- master
où:
B'
Est une version corrigée de B
qui n'est pas identique au patch avec B
;C*
Et D*
Sont identiques au patch à C
et D
respectivement et entrent en conflit textuellement s'ils sont appliqués 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 départ 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 identiques au patch avec C*
et D*
et peuvent donc être supprimés, de sorte que le résultat final est:
o --- B' --- C* --- D* --- E <- dev
Si le point de départ n'est pas identifié, la sélection de B
sur une branche contenant B'
Entraîne un conflit et si les validations identiques au patch ne sont pas correctement identifiées, la sélection de C
sur une branche contenant D
(ou de manière équivalente D*
) entraîne un conflit.
Je dirais que la section récupération après rebase en 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 à partir de votre propre rebase - vous déplacez une branche et rebase toutes les branches qui l'avaient dans leur histoire vers sa nouvelle position.