La branche Git a divergé après le rebase

112

J'ai rebasé localement une branche qui était déjà poussée.

Git informe que ma branche et ma télécommande ont divergé et que:

"et ont respectivement 109 et 73 commits différents"

Le fait de pousser ma branche résoudra-t-il ce problème - c'est-à-dire est-ce à prévoir après un rebase?

Marty Wallace
la source
J'ai le même problème. Pouvons-nous dire que "la bonne façon de faire" est de rebaser la branche locale et ensuite seulement de pousser?
Mher Didaryan

Réponses:

154

Lorsque vous rebasez une branche, vous devez réécrire les commits pour tout commit qui est au-dessus des commits dans la branche sur laquelle vous rebasez. En effet, l'une des propriétés d'un commit est son parent (ou ses parents). Lorsque vous rebasez, vous changez le parent du commit local le plus ancien sur votre branche - et changez ainsi les hachages de commit de tous vos commits locaux, puisque ce changement bouillonne à travers les commits de manière transitoire.

Puisque vous aviez déjà poussé la branche, vous devriez avoir fusionné dans la branche source, plutôt que de rebaser contre elle. Il est possible de "forcer" votre nouvelle branche (en utilisant le -fdrapeau), mais une poussée normale ne fonctionnera pas, car l'intégrité de l'historique des branches sera perturbée. Si vous collaborez avec d'autres personnes sur cette branche, la poussée de force est une mauvaise idée, car cela rendra les autres collaborateurs très confus lorsque leur histoire ne correspond pas soudainement.

TL; DR - Si vous ne collaborez pas, poussez la branche en utilisant push -f. Si tel est le cas, réinitialisez la branche à l'état précédent et fusionnez-la dans la branche source à la place.

Jason LeBrun
la source
1
"Puisque vous aviez déjà poussé la branche, vous auriez dû fusionner dans la branche source" - pourquoi?
hammett
1
@HamiltonVerissimo Première phrase de Jason: "Quand vous rebasez une branche, vous devez réécrire les commits pour tout commit qui est au-dessus des commits dans la branche sur laquelle vous rebasez." Même si les changements capturés dans les commits «ci-dessus» ont le même contenu logique, ils sont appliqués à une base différente et sont donc des commits différents avec des hachages différents. Si d'autres développeurs travaillent à partir de la branche pré-rebasée, faire un git push -f serait extrêmement perturbateur pour leur flux de travail. Ainsi, puisque cette branche a été poussée vers une source publique, il aurait dû fusionner.
loup
4
vous pouvez également utiliser push --force-with-bail si vous n'êtes pas sûr que quelqu'un d'autre ait déjà poussé quelque chose.
Console
1
@ jason-lebrun L'inconvénient de la fusion des modifications en amont n'est-il pas que vous pouvez écraser votre propre travail sans avertissement? Les conflits de fusion sont-ils détectés de la même manière que lors du rebasage? Par exemple, si je supprime une section d'un fichier dans ma branche parce qu'elle n'est plus nécessaire, mais que quelqu'un d'autre a apporté une modification triviale à la même section en amont, comme supprimer des espaces ou une action globale de recherche / remplacement, ne le ferait pas fusionner cela en plus de ma branche remplacer ma suppression par leur version trivialement modifiée?
Chris Bloom
1
La fusion de l'autre branche avec la vôtre n'est-elle pas une mauvaise idée? Par exemple, fusionner le maître dans une branche de fonctionnalités - cela ne créera-t-il pas un nouveau commit pour cela? Cela n'entraînera-t-il pas un commit supplémentaire une fois que cette fonctionnalité se branche si elle est fusionnée avec master plus tard?
Ross
55

Tous vos commits ont changé d'identifiants, donc le détournement n'est pas vraiment une divergence.

Pour résoudre votre problème, vous devez écraser votre branche distante:

git push -f origin experiment

http://git-scm.com/book/ch3-6.html

Explication:

Voyez comment dans cette image C3 n'est pas mis en tant que C3 après le rebase, mais en tant que C3 '. En effet, ce n'est pas exactement C3, mais il a tous ses changements de code.

Rebase

Sur cette autre image, vous obtenez l'image de ce qu'un rebase est vu lorsqu'une télécommande est impliquée, et pourquoi il y a un détournement.

diverge et git push

Dans tous les cas, après avoir fait la poussée forcée, il vous dira qu'il a fait une (mise à jour forcée), tout devrait être bien à ce stade.

Consultez le lien en haut et recherchez "git push --force". Vous verrez une explication plus détaillée.

mimoralea
la source
3
Ouaip. Je suppose que cela résout le problème. Cependant, la poussée forcée peut ne pas fonctionner lorsque vous travaillez avec une équipe lorsque votre poussée peut écraser tout travail récent poussé par d'autres. Ai-je raison?
Hari Krishna Ganji
Cela pourrait ne pas avoir les effets escomptés. Assurez-vous de fusionner vos modifications en «avance rapide» avant de procéder au rebase.
mimoralea
1

J'ai eu du succès avec le rebase diverge pour un push en faisant ce qui suit:

git checkout mybranch
git pull
git push origin mybranch

L'attraction a résolu la divergence.

AVANT la traction

Your branch and 'origin/mybranch' have diverged,
and have 2 and 1 different commit(s) each, respectively.

Sortie PULL

Fusion faite par récursif. mypath / myfile.py | 12 +++++++++++ - 1 fichiers modifiés, 11 insertions (+), 1 suppressions (-)

APRÈS avoir tiré

Votre succursale est en avance sur 'origin / mybranch' de 3 commits.

APRÈS PUSH

mybranch est 3 en avant de la branche et a toujours un message de fusion de demande de tirage ouvert ajouté à l'historique de validation Fusionner la branche mybranch de remote dans mybranch

Je suppose que c'est probablement ce que fait la poussée de force, et je n'ai pas vérifié.

Comme les autres l'ont dit, évitez un rebase si vous avez déjà une pull request ouverte. Je donne cet exemple comme quelque chose qui a fonctionné pour moi.

zerocog
la source
1

Cela peut être résolu sans une poussée forcée en rebasant la branche cible dans votre branche locale actuelle, en basculant vers votre branche cible, puis en rebasant votre branche locale dans la cible. Cela ne diverge pas puisque le ou les commit (s) manquant (s) sont ajoutés et n'ont plus besoin d'être créés. Exemple pour une explication plus simple:

  1. la branche principale est de développer
  2. Vous extrayez une nouvelle fonctionnalité de branche / doing_stuff
  3. Un membre de l'équipe pousse un nouvel engagement à se développer

Si vous n'avez PAS mis à jour votre branche de développement, alors un "git checkout develop" && "git rebase feature / doing_stuff" fonctionnera correctement car aucun commit n'a été ajouté depuis votre checkout. Cependant, si vous avez extrait develop et supprimé le nouveau commit, vous verrez cette divergence si vous essayez de rebaser en raison d'un nouveau commit vu. Une solution facile sans forcer (généralement pas une bonne idée dans un environnement d'équipe) est de:

  1. fonctionnalité git checkout / doing_stuff
  2. git rebase develop
  3. git checkout develop
  4. git rebase fonctionnalité / doing_stuff

Le rebase de l'étape 2 amène le commit manquant dans la fonction / doing_stuff donc quand l'étape 4 arrive, il est à jour et n'a pas besoin de créer un nouveau commit pour le changement.

C'est une solution qui fonctionne, je sais, car je viens de rencontrer cela et j'ai suivi les étapes ci-dessus pour pousser avec succès le développement sans forcer. Je travaille dans une équipe de plus de 50 développeurs, il est donc interdit de forcer à pousser autre chose que mes propres branches de test, j'ai donc dû trouver une solution.

Chris
la source