1326 votes

La branche principale et 'origin/master' ont divergé, comment "désolidariser" les branches'?

En quelque sorte, mes branches master et origin/master ont divergé.
En réalité, je ne veux pas qu'elles divergent.

Comment puis-je voir ces différences et les fusionner?

4 votes

Que voulez-vous dire par diverger? Rebases-tu ton master après l'avoir poussé?

48 votes

Je reçois un message disant "Votre branche et 'origin/master' ont divergé, et ont respectivement 1 et 1 commit(s) différent(s)".

0 votes

J'ai mis à jour ma réponse pour refléter ce message d'avertissement "diverged".

1311voto

VonC Points 414372

Vous pouvez revoir les différences avec :

git log HEAD..origin/main

# anciens dépôts
git log HEAD..origin/master

avant de le récupérer (fetch + merge) (voir aussi "Comment obtenir git pour toujours tirer d'une branche spécifique?")

Note: depuis Git 2.28 (T3 2020), la branche par défaut est configurable, et maintenant (2021+) est définie sur main, plus sur master.
Le reste de la réponse reflète cette convention plus récente.


Lorsque vous avez un message comme :

"Votre branche et 'origin/main' ont divergé, # et ont respectivement 1 et 1 commit(s) différent(s)."

Vérifiez si vous avez besoin de mettre à jour origin.
Si origin est à jour, alors certains commits ont été poussés vers origin depuis un autre référentiel pendant que vous faisiez vos propres commits localement.

... o ---- o ---- A ---- B  origin/main (travail en amont)
                   \
                    C  main (votre travail)

Vous avez basé le commit C sur le commit A car c'était le dernier travail que vous aviez récupéré de upstream à ce moment-là.

Cependant, avant d'essayer de repousser vers origin, quelqu'un d'autre a poussé le commit B.
L'historique de développement s'est divergé en chemins séparés.

Vous pouvez alors fusionner ou rebaser. Voir Pro Git: Git Branching - Rebasing pour plus de détails.

Fusionner

Utilisez la commande git merge:

$ git merge origin/main

# anciens dépôts
$ git merge origin/master

Cela dit à Git d'intégrer les changements de origin/main dans votre travail et de créer un commit de fusion.
Le graphique de l'historique ressemble maintenant à ceci:

... o ---- o ---- A ---- B  origin/main (travail en amont)
                   \      \
                    C ---- M  main (votre travail)

La nouvelle fusion, le commit M, a deux parents, représentant chacun un chemin de développement ayant conduit au contenu stocké dans ce commit.

À noter que l'historique derrière M est désormais non linéaire.

Rebaser

Utilisez la commande git rebase:

$ git rebase origin/main

# anciens dépôts
$ git rebase origin/master

Cela dit à Git de rejouer le commit C (votre travail) comme si vous l'aviez basé sur le commit BA.
Les utilisateurs de CVS et Subversion rebasent régulièrement leurs changements locaux sur le travail en amont lorsqu'ils mettent à jour avant de commettre.
Git ajoute simplement une séparation explicite entre les étapes de commit et de rebase.

Le graphique de l'historique ressemble maintenant à ceci:

... o ---- o ---- A ---- B  origin/main (travail en amont)
                          \
                           C'  main (votre travail)

Le commit C' est un nouveau commit créé par la commande git rebase.
Il est différent de C de deux manières :

  1. Il a une histoire différente : B au lieu de A.
  2. Son contenu tient compte des changements à la fois de B et de C; il est identique à M de l'exemple de fusion.

À noter que l'historique derrière C' est toujours linéaire.
Nous avons choisi (pour l'instant) de n'autoriser qu'un historique linéaire dans cmake.org/cmake.git.
Cette approche préserve le flux de travail basé sur CVS utilisé précédemment et peut faciliter la transition.
Une tentative de repousser C' dans notre référentiel fonctionnera (en supposant que vous avez les autorisations et que personne n'a poussé pendant que vous faisiez un rebase).

La commande git pull fournit un moyen abrégé de fetch depuis origin et de rebase le travail local dessus:

$ git pull --rebase

Cela combine les étapes de fetch et de rebase en une seule commande.

7 votes

J'ai trouvé cela en cherchant le même problème, pouvez-vous expliquer pourquoi 'git reset --hard HEAD' n'a pas corrigé le problème ?

22 votes

@Neth : car il ne s'agit pas de modifications mises en scène (c'est-à-dire de modifications présentes dans l'index mais non encore validées), mais de commit locaux (ce qui diffère des commits présents sur le distant). git reset --hard HEAD ne supprimerait que toute modification non validée localement indexée, et ne ferait rien pour concilier les différences entre les commits locaux et distants. Seul une fusion ou un rebasage réconciliera les deux ensembles de commits (local et distant).

4 votes

Waouh, merci pour cette réponse incroyable. Nous avions accidentellement fait un "git pull" sans "--rebase", et "git rebase origin/master" était juste la solution!

1142voto

skiphoppy Points 16563

J'ai eu ce problème et je suis perplexe quant à ce qui l'a causé, même après avoir lu les réponses ci-dessus. Ma solution a été de faire

git reset --hard origin/main

Ensuite, cela réinitialise tout simplement ma copie de main (locale) (que je suppose être corrompue) au point correct, tel que représenté par main (distant) origin/main.

ATTENTION : Vous perdrez tous les changements non encore poussés vers origin/main.

43 votes

Oui, cela ressemble un peu à l'option des mannequins, mais s'il n'y a pas de réel danger et que vous êtes ici pour une solution rapide - cela fonctionne (pour moi en tout cas)

12 votes

Cela nécessite d'être sur la branche principale avant ("git checkout master").

0 votes

Salut skiphoppy, merci pour le conseil. Je suis d'accord avec PandaWood (sans offense) que cela semble être une option un peu simpliste. Mais en disant cela, je ne suis pas très expérimenté avec les aspects plus avancés de Git.

71voto

asitmoharna Points 679
git pull --rebase origin/main 

est une commande unique qui peut vous aider la plupart du temps.

Modifier: Tire les commits de l'origine/main et applique vos modifications à l'historique de la branche nouvellement tirée.

121 votes

Veuillez mentionner ce que la commande fait, sinon les gens pourraient l'exécuter et finir par la gâcher

3 votes

Si tout se passe bien, vous devriez vous retrouver avec votre master contenant toutes les modifications origin/master plus tous vos commits locaux seront rejoués par-dessus. Semble bon pour moi.

8 votes

Sauf quand il y a de réelles différences et que cela vous laisse dans un rebase avorté.

39voto

paneer_tikka Points 885

Je me suis retrouvé dans cette situation lorsque j'ai essayé de rebasé une branche qui suivait une branche distante, et j'essayais de la rebaser sur main. Dans ce scénario, si vous essayez de rebaser, vous finirez probablement par constater que votre branche diverge et cela peut créer un gâchis qui n'est pas fait pour les débutants en git !

Disons que vous êtes sur la branche ma_branche_suivi_distante, qui a été créée à partir de main

$ git status

# Sur la branche ma_branche_suivi_distante

rien à valider (répertoire de travail propre)

Et maintenant vous essayez de rebaser à partir de master comme suit :

git rebase main

ARRÊTEZ MAINTENANT et épargnez-vous des ennuis ! Au lieu de cela, utilisez la fusion comme ceci :

git merge main

Oui, vous vous retrouverez avec des commits supplémentaires sur votre branche. Mais à moins que vous ne souhaitiez "dé-diverger" les branches, ce sera un flux de travail beaucoup plus fluide que le rebasage. Consultez ce blog pour une explication beaucoup plus détaillée.

En revanche, si votre branche est seulement une branche locale (c'est-à-dire pas encore poussée vers un distant), vous devriez certainement faire un rebasage (et votre branche ne divergera pas dans ce cas).

Maintenant, si vous lisez ceci parce que vous êtes déjà dans un scénario "divergé" en raison d'un tel rebasage, vous pouvez revenir au dernier commit provenant de l'origine (c'est-à-dire dans un état non divergé) en utilisant :

git reset --hard origin/ma_branche_suivi_distante

6 votes

Une règle de base est d'utiliser rebase si la branche sur laquelle vous réalisez le rebase n'a pas été publiée (et utilisée par d'autres personnes). Sinon, utilisez merge. Si vous réalisez un rebase sur des branches déjà publiées (et utilisées), vous devez coordonner une conspiration pour réécrire l'historique à travers chaque développeur ayant utilisé votre branche.

1 votes

Malheureusement, je n'ai pas lu ce message avant d'exécuter git rebase master...

0 votes

Si je fais git rebase master tout en étant sur la branche 'foobar', alors techniquement foobar est divergé de origin/foobar jusqu'à ce que je fasse un git push -f, non ?

34voto

Darren Cook Points 5743

Dans mon cas, voici ce que j'ai fait pour provoquer le message diverged : J'ai fait un git push mais j'ai ensuite utilisé git commit --amend pour ajouter quelque chose au message de commit. Ensuite, j'ai également effectué un autre commit.

Donc, dans mon cas, cela signifiait simplement que origin/master était obsolète. Comme je savais que personne d'autre ne touchait à origin/master, la correction était triviale : git push -f (où -f signifie force)

9 votes

+1 pour git push -f pour écraser les modifications précédemment validées et poussées vers l'origine. Je suis également sûr que personne d'autre n'a touché le dépôt.

5 votes

Commande très risquée. Veuillez écrire une brève information concernant le facteur de risque de la commande.

2 votes

@Trickster: J'avais déjà décrit le risque : "comme je savais que personne d'autre ne touchait origin/master". Je crois que, dans ce cas, il ne s'agit pas d'une commande risquée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X