branche principale et «origine / maître» ont divergé, comment «sous-diviser» les branches »?

965

D'une certaine manière, mon maître et ma branche origine / maître ont divergé.
En fait, je ne veux pas qu'ils divergent.

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

Franc
la source
2
que voulez-vous dire par divergence? rebasez-vous votre maître après l'avoir poussé?
hasen
13
Je reçois un message disant "Votre branche et 'origine / maître' ont divergé, # et ont respectivement 1 et 1 commit (s) différent (s)."
Frank
J'ai mis à jour ma réponse pour refléter ce message d'avertissement "divergé".
VonC
La réponse acceptée à une autre question peut également être utile pour résoudre certains cas où cela pourrait entrer en jeu (par exemple, vous essayez de déplacer votre maître, mais il a déjà été poussé): stackoverflow.com/questions/2862590/…
lindes
8
L'explication sur ce blog m'a aidé infiniment plus que n'importe quelle réponse ci-dessous: sebgoo.blogspot.com/2012/02/…

Réponses:

1014

Vous pouvez revoir les différences avec:

git log HEAD..origin/master

avant de le tirer (fetch + merge) (voir aussi "Comment faire pour que git tire toujours depuis une branche spécifique?" )


Lorsque vous avez un message comme:

"Votre branche et 'origine / maître' ont divergé, # et ont respectivement 1 et 1 commit (s) différent (s)."

, vérifiez si vous devez mettre à jourorigin . Si originest à jour, certaines validations ont été transférées origindepuis un autre référentiel pendant que vous effectuez vos propres validations localement.

... o ---- o ---- A ---- B  origin/master (upstream work)
                   \
                    C  master (your work)

Vous avez basé le commit C sur le commit A car c'était le dernier travail que vous aviez récupéré en amont à l'époque.

Cependant, avant que vous n'essayiez de repousser à l'origine, quelqu'un d'autre a poussé la validation B.
L'historique du développement a divergé dans des chemins distincts.

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

Fusionner

Utilisez la commande git merge:

$ git merge origin/master

Cela indique à Git d'intégrer les modifications de origin/masterdans votre travail et de créer un commit de fusion.
Le graphique de l'histoire ressemble maintenant à ceci:

... o ---- o ---- A ---- B  origin/master (upstream work)
                   \      \
                    C ---- M  master (your work)

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

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

Rebase

Utilisez la commande git rebase:

$ git rebase origin/master

Cela indique à Git de rejouer la validation C (votre travail) comme si vous l'aviez basée sur la validation B au lieu de A. Les utilisateurs
CVS et Subversion rebasinent régulièrement leurs modifications locales au-dessus du travail en amont lorsqu'ils mettent à jour avant la validation.
Git ajoute simplement une séparation explicite entre les étapes de validation et de rebase.

Le graphique de l'histoire ressemble maintenant à ceci:

... o ---- o ---- A ---- B  origin/master (upstream work)
                          \
                           C'  master (your work)

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

  1. Il a une histoire différente: B au lieu de A.
  2. Son contenu explique les changements à la fois en B et en C; c'est la même chose que M dans l'exemple de fusion.

Notez que l'histoire derrière C 'est toujours linéaire.
Nous avons choisi (pour l'instant) de n'autoriser que l'histoire linéaire dans cmake.org/cmake.git.
Cette approche préserve le workflow basé sur CVS utilisé précédemment et peut faciliter la transition.
Une tentative de pousser C 'dans notre référentiel fonctionnera (en supposant que vous avez des autorisations et que personne n'a poussé pendant que vous rebasiez).

La commande git pull fournit un moyen raccourci pour récupérer à partir de l'origine et rebaser le travail local dessus:

$ git pull --rebase

Ceci combine les étapes de récupération et de rebase ci-dessus en une seule commande.

VonC
la source
5
J'ai trouvé cela en recherchant le même problème, pouvez-vous expliquer pourquoi 'git reset --hard HEAD' n'a pas résolu le problème?
Neth
12
@Neth: car il ne s'agit pas de modifications par étapes (ie modifications présentes dans l'index mais pas encore validées), mais de validations locales (qui diffèrent des validations présentes sur la télécommande). git reset --hard HEADne ferait que supprimer toute modification non indexée commis locale, et ne ferait rien pour concilier les différences entre les locaux et distants commits . Seule une fusion ou un rebase rapprochera les deux ensembles de commits (le local et le distant).
VonC
4
Wow, merci pour cette réponse géniale. Nous avions accidentellement fait un "git pull" sans "--rebase", et "git rebase origin / master" était juste la solution!
mrooney
3
Que diriez-vous - je veux juste ignorer / vider mes modifications locales et être avec ma branche locale où se trouve la télécommande? En d'autres termes, je veux mastermontrer Bvotre exemple.
CygnusX1
23
@ CygnusX1 qui serait git reset --hard origin/mastercomme mentionné dans la réponse ci-dessous: stackoverflow.com/a/8476004/6309
VonC
726

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

git reset --hard origin/master

Ensuite, cela réinitialise simplement ma copie (locale) de master (que je suppose vissée) au bon point, comme représenté par origine / master (distant).

AVERTISSEMENT : vous perdrez toutes les modifications non encore poussées origin/master.

skiphoppy
la source
21
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 de toute façon)
PandaWood
7
Cela nécessite d'être sur la branche principale avant ("git checkout master").
blueyed
Cela m'est arrivé une fois quand je suis arrivé le plus tard de l'origine, puis quelqu'un d'autre a fait une poussée forcée vers l'origine qui a fait revenir le commit d'origine d'origine. Donc ma branche locale avait le commit annulé, et quand j'ai essayé de retirer le dernier de l'origine, cela disait que je devais fusionner. Dans ce cas, il était logique de réinitialiser --hard origin / (branch) car le problème avait déjà été corrigé dans l'origine.
Landon Poch
96
Vous devriez probablement avertir les utilisateurs que cela leur fera perdre tous les changements non encore poussés à l'origine
Pedro Loureiro
6
@PedroLoureiro Les commits sont vraiment perdus, vous pouvez toujours trouver les commits avec git reflogou les voir dedans gitk --all. Mais pourtant, bien sûr, la réinitialisation matérielle est autre chose qu'un rebase.
sebkraemer
52
git pull --rebase origin/master 

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

Modifier: extrait les validations de l'origine / du masque et applique vos modifications à l'historique de branche nouvellement extrait.

asitmoharna
la source
89
veuillez mentionner ce que fait la commande, sinon les gens pourraient l'exécuter et finir par bousiller
Baz1nga
1
S'il n'y a pas de problème, vous devriez vous retrouver avec votre master contenant toutes les modifications origine / master et tous vos commits locaux seront rejoués par dessus. Ça me semble bien.
Philipp Claßen
7
Sauf lorsqu'il y a de réelles différences et que cela vous laisse dans un rebase avorté.
ffledgling
Cela génère une erreur: erreur: impossible de fusionner les modifications. Le patch a échoué à 0024 Modèles de demande et de réponse
IgorGanapolsky
32

Je me suis retrouvé dans cette situation lorsque j'ai essayé de rebaser une branche qui suivait une branche distante, et j'essayais de la rebaser sur master. Dans ce scénario, si vous essayez de rebaser, vous trouverez très probablement que votre branche a divergé et cela peut créer un gâchis qui n'est pas pour les git nubees!

Disons que vous êtes sur la branche my_remote_tracking_branch, qui a été branchée à partir de master

$ git status

# Sur la branche my_remote_tracking_branch

rien à valider (répertoire de travail propre)

Et maintenant, vous essayez de rebaser du maître en tant que:

git rebase master

ARRÊTEZ MAINTENANT et épargnez-vous des ennuis! Utilisez plutôt la fusion comme:

git merge master

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

D'un autre côté, si votre branche est uniquement une branche locale (c'est-à-dire qu'elle n'est pas encore envoyée à une télécommande), vous devez certainement faire un rebase (et votre branche ne divergera pas dans ce cas).

Maintenant , si vous lisez ceci parce que vous avez déjà êtes dans un scénario « divergé » en raison de ce changement de base, vous pouvez revenir à la dernière validation d'origine ( par exemple dans un état divergé non) en utilisant:

git reset --hard origin / my_remote_tracking_branch

paneer_tikka
la source
5
Une règle d'or consiste à utiliser rebasesi la branche que vous rebasez n'a pas été publiée (et utilisée par d'autres personnes). Sinon, utilisez merge. Si vous rebasez des branches déjà publiées (et utilisées), vous devez coordonner un complot pour réécrire l'historique de tous les développeurs qui ont utilisé votre branche.
Mikko Rantalainen
1
Malheureusement je n'ai pas lu ce message avant de faire le git rebase master...
Vitaly Isaev
Si je fais git rebase master alors que sur la branche 'foobar' alors techniquement foobar est divergé de origin / foobar jusqu'à ce que je fasse un git push -f, non?
relipse
1
git reset --hard origin/my_remote_tracking_branchest ce qui a vraiment fonctionné
roothahn
24

Dans mon cas, voici ce que j'ai fait pour provoquer le message divergé : je l'ai fait git pushmais j'ai ensuite git commit --amendajouté quelque chose au message de validation. Ensuite, j'ai également fait un autre commit.

Dans mon cas, cela signifiait simplement que l'origine / le maître était obsolète. Parce que je savais que personne d'autre ne touchait origine / maître, la correction était triviale: git push -f (où -fsignifie forcer)

Darren Cook
la source
8
+1 pour git push -fécraser les modifications précédemment validées et transmises à l'origine. Je suis également sûr que personne d'autre n'a touché le référentiel.
zacharydl
4
Commande très risquée. Veuillez écrire une brève information concernant le facteur de risque de la commande.
J4cK
1
@Trickster: J'avais déjà décrit le risque: "car je savais que personne d'autre ne touchait origine / maître". Je crois que, dans ce cas, ce n'est pas une commande risquée.
Darren Cook
1
Si quelqu'un commet sur master et qu'une personne exécute la commande git push -f alors c'est une commande à haut risque
J4cK
11

Dans mon cas, j'ai poussé les modifications vers origin/masterpuis j'ai réalisé que je n'aurais pas dû le faire :-( Cela a été compliqué par le fait que les modifications locales étaient dans un sous-arbre. Je suis donc retourné au dernier bon commit avant le "mauvais" local. changements (en utilisant SourceTree) puis j'ai reçu le "message de divergence".

Après avoir corrigé mon désordre localement (les détails ne sont pas importants ici), je voulais "remonter dans le temps" la origin/masterbranche distante afin qu'elle soit à nouveau synchronisée avec le local master. La solution dans mon cas était:

git push origin master -f

Notez l' -finterrupteur (force). Cela a supprimé les «mauvais changements» qui avaient été poussés origin/masterpar erreur et maintenant les branches locales et distantes sont synchronisées.

Veuillez garder à l'esprit qu'il s'agit d'une opération potentiellement destructrice. N'effectuez-la donc que si vous êtes sûr à 100% que "reculer" le maître distant à temps est OK.

Laryx Decidua
la source
Toujours utile mais ne répond sûrement pas à la question.
Thibault D.20
1
@ThibaultD. même si ce n'était pas le cas, c'est exactement ce que je cherchais.
Neil Chowdhury
Je reçois You are not allowed to force push code to a protected branch on this project.. J'essaye de pousser jusqu'à ma fourchette.
BanAnanas
J'ai dû retirer la protection sur gitlab repo stackoverflow.com/questions/32246503/…
BanAnanas
5

Je sais qu'il y a beaucoup de réponses ici, mais je pense qu'il git reset --soft HEAD~1mérite une certaine attention, car cela vous permet de conserver les modifications dans le dernier commit local (non poussé) tout en résolvant l'état divergent. Je pense que c'est une solution plus polyvalente que de tirer avec rebase, car le commit local peut être revu et même déplacé vers une autre branche.

La clé utilise --soft, au lieu de la dure --hard. S'il y a plus d'un commit, une variation de HEAD~xdevrait fonctionner. Voici donc toutes les étapes qui ont résolu ma situation (j'ai eu 1 commit local et 8 commits dans la télécommande):

1) git reset --soft HEAD~1 pour annuler la validation locale. Pour les étapes suivantes, j'ai utilisé l'interface de SourceTree, mais je pense que les commandes suivantes devraient également fonctionner:

2) git stash pour cacher les modifications de 1). Maintenant, tous les changements sont sûrs et il n'y a plus de divergence.

3) git pull pour obtenir les modifications à distance.

4) git stash pop ou git stash applypour appliquer les dernières modifications cachées, suivies d'une nouvelle validation, si vous le souhaitez. Cette étape est facultative, avec 2) , lorsque vous souhaitez supprimer les modifications dans la validation locale. En outre, lorsque vous souhaitez vous engager dans une autre branche, cette étape doit être effectuée après le passage à la branche souhaitée.

Bianca Daniciuc
la source
En fait, ces jours-ci, le pull --rebaseserait caché automatiquement de toute façon. stackoverflow.com/a/30209750/6309
VonC
4

Pour voir les différences:

git difftool --dir-diff master origin/master

Cela affichera les changements ou les différences entre les deux branches. Dans araxis (My favorite), il l'affiche dans un style de dossier différent. Affichage de chacun des fichiers modifiés. Je peux ensuite cliquer sur un fichier pour voir les détails des modifications dans le fichier.

C Johnson
la source
1
Utilisation intéressante de git-dir: +1
VonC
3

Dans mon cas, cela a été causé par le fait de ne pas avoir engagé ma résolution de conflit.

Le problème est dû à l'exécution de la git pullcommande. Des changements d'origine ont entraîné des conflits avec mon référentiel local, que j'ai résolu. Cependant, je ne les ai pas commis. La solution à ce stade est de valider les modifications ( git commitle fichier résolu)

Si vous avez également modifié certains fichiers depuis la résolution du conflit, la git statuscommande affichera les modifications locales en tant que modifications locales non étagées et fusionnera la résolution en tant que modifications locales étagées. Cela peut être résolu correctement en validant les modifications de la fusion d'abord par git commit, puis en ajoutant et en validant les modifications non programmées comme d'habitude (par exemple par git commit -a).

Mohammad Reza Esmaeilzadeh
la source
0

J'ai eu le même message lorsque j'essayais de modifier le dernier message de validation, de validation déjà poussée, en utilisant: git commit --amend -m "New message" Lorsque j'ai poussé les modifications en utilisant, git push --force-with-lease repo_name branch_name il n'y avait aucun problème.

LoveForDroid
la source
0

J'ai rencontré ce problème lorsque j'ai créé une branche basée sur la branche A par

git checkout -b a

puis je mets le flux ascendant de la branche a à la branche d'origine B par

git branch -u origin/B

Ensuite, j'ai reçu le message d'erreur ci-dessus.

Une façon de résoudre ce problème pour moi était,

  • Supprimer la branche a
  • Créer une nouvelle branche b par
git checkout -b b origin/B
YanQing
la source