De Git et rebase vs fusion questions
J'ai été en utilisant Git maintenant pour un couple de mois sur un projet avec un autre développeur. J'ai plusieurs années d'expérience avec SVN, donc je suppose que je vous apporte un lot de bagages à la relation.
J'ai entendu dire que Git est excellent pour le branchement et la fusion, et jusqu'à présent, je n'ai juste pas le voir. Bien sûr, la ramification est mort simple, mais quand j'essaie de fusionner, tout va en enfer. Maintenant, j'y suis habitué depuis le SVN, mais il me semble que j'ai juste échangé l'un sous la normale système de gestion de versions pour un autre.
Mon partenaire me dit que mes problèmes viennent de mon désir de fusion de bon gré mal gré, et que je devrais être à l'aide de rebase au lieu de fusionner dans de nombreuses situations. Pour exemple, voici le flux de travail qu'il s'est fixés:
clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature
git checkout master
git merge my_new_feature
Essentiellement, créer une branche, TOUJOURS rebase de maître à la direction générale, et de la fusion de la branche master. Important à noter est que la branche reste toujours local.
Ici est le flux de travail que j'ai commencé avec
clone remote repository
create my_new_feature branch on remote repository
git checkout -b --track my_new_feature origin/my_new_feature
..work, commit, push to origin/my_new_feature
git merge master (to get some changes that my partner added)
..work, commit, push to origin/my_new_feature
git merge master
..finish my_new_feature, push to origin/my_new_feature
git checkout master
git merge my_new_feature
delete remote branch
delete local branch
Il y a deux différences essentielles (je pense): j'utilise de la fusion de toujours au lieu de relocalisation, et je pousse ma branche (et ma branche s'engage), sur le dépôt distant.
Mon raisonnement pour la branche distante, c'est que je veux que mon travail sauvegardé comme je suis en train de travailler. Notre dépôt est automatiquement sauvegardé et peut être restaurée si quelque chose va mal. Mon portable n'est pas, ou pas complètement. Donc, j'ai hate d'avoir le code sur mon ordinateur portable qui n'est pas en miroir à un autre endroit.
Mon raisonnement pour la fusion au lieu de rebase est que l'opération de fusion semble être la norme, et cela semble être une fonctionnalité avancée. Mon sentiment est que ce que je suis en train de faire n'est pas une configuration plus avancée, de sorte que cela ne devrait pas être nécessaire. J'ai même pas lu la nouvelle Pragmatique de Programmation livre sur Git, et ils couvrent de fusion de manière approfondie et à peine mention de rebase.
De toute façon, je suivais mon flux de travail sur un récent de la branche, et quand j'ai essayé de l'intégrer à maîtriser, tout est allé en enfer. Il y avait des tonnes de conflits avec des choses qui ne devrait pas avoir d'importance. Les conflits juste fait aucun sens pour moi. Il m'a fallu une journée pour tout régler, et finalement abouti à un obligé de pousser le maître, depuis que mon maître a tous les conflits résolus, mais la distance n'était toujours pas satisfait.
Qu'est-ce que la "bonne" flux de travail pour quelque chose comme ça? Git est censée faire le branchement et la fusion de super-facile, et je ne suis pas le voir.
Mise à jour 2011-04-15
Cela semble être un très populaire en question, alors j'ai pensé que je mettrais à jour avec mes deux ans d'expérience puisque j'ai d'abord demandé.
Il s'avère que l'origine de flux de travail est correcte, au moins dans notre cas. En d'autres termes, c'est ce que nous faisons et cela fonctionne:
clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge my_new_feature
En fait, notre flux de travail est un peu différent, comme nous avons tendance à ne squash fusionne au lieu de matières premières fusions. (Remarque: C'est controversé, voir ci-dessous.) Cela nous permet de transformer l'ensemble de notre branche en un seul commit sur le master. Puis nous supprimer notre branche. Cela nous permet logiquement de la structure de notre engage sur le maître, même s'ils sont un peu en désordre sur l'une de nos succursales. Donc, c'est ce que nous faisons:
clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git checkout master
git merge --squash my_new_feature
git commit -m "added my_new_feature"
git branch -D my_new_feature
De Squash de Fusion Controverse - Comme plusieurs commentateurs l'ont souligné, le squash de fusion permettra de jeter toute l'histoire sur votre branche. Comme son nom l'indique, il écrase tous les commits vers le bas en un seul. Pour les petites fonctionnalités, cela fait sens car il condense en un seul paquet. Pour plus de fonctionnalités, il est probablement pas une bonne idée, surtout si vos commits sont déjà atomique. Il s'agit vraiment de préférence personnelle.
Github et Bitbucket (d'autres?) Pull requests - Dans le cas où vous vous demandez comment fusionner/rebase se rapporte à Tirer les Demandes, je vous recommande de suivre toutes les étapes ci-dessus jusqu'à ce que vous êtes prêt à fusionner à maîtriser. Au lieu de saisir manuellement la fusion avec git, vous venez d'accepter le PR. Notez que cela ne fera pas une courge de fusion (au moins pas par défaut), mais non de squash, non-fast-forward est la fusion de la convention dans la Demande d'extraction de la communauté (autant que je sache). Plus précisément, il fonctionne comme ceci:
clone the remote repository
git checkout -b my_new_feature
..work and commit some stuff
git rebase master
..work and commit some stuff
git rebase master
..finish the feature, commit
git rebase master
git push # May need to force push
...submit PR, wait for a review, make any changes requested for the PR
git rebase master
git push # Will probably need to force push (-f), due to previous rebases from master
...accept the PR, most likely also deleting the feature branch in the process
git checkout master
git branch -d my_new_feature
git remote prune origin
J'ai appris à aimer Git et ne veux plus jamais retourner à SVN. Si vous avez de la peine, il suffit de coller avec elle et à la fin vous allez voir la lumière au bout du tunnel.
- Malheureusement, la nouvelle Pragmstic Programmation livre est écrit à partir de l'utilisation de Git tout en continuant à penser SVN, et dans ce cas, il a de vous avoir induit en erreur. Dans Git, git rebase garde les choses simples quand ils peuvent être. Votre expérience peut vous dire que votre flux de travail ne fonctionne pas dans Git, pas que Git n'a pas de travail.
- Je ne recommanderais pas de squash de la fusion dans ce cas, comme il enregistre aucune information sur ce qui est fusionné (tout comme svn, mais pas de mergeinfo ici).
- L'amour de la note au bas, j'ai eu une expérience similaire de lutte avec Git, mais maintenant des difficultés à imaginer de ne pas l'utiliser. Merci pour l'explication finale aussi, a beaucoup aidé avec
rebase
compréhension - Après avoir fini la fonctionnalité, ne devriez-vous pas rebase une dernière fois avant de les fusionner new_feature de maître?
- Votre flux de travail perd toute commettre l'histoire de l'supprimé branche 🙁
- Ce post peut aider: stackoverflow.com/a/9204499/631619
- vous devriez également ajouter la mise à jour comme sa propre réponse plutôt qu'une modification à la question.
- Je souhaite que je pourrais +1, vous avez plus de... Votre dernière mise à jour à faire écrasé fusionne est ce que je cherchais et ce que je pense est absent de la plupart des modèles de ramifications (tels que nvie.com/posts/a-successful-git-branching-model). En gardant un nettoyer l'historique de tout les bugs est CONSIDÉRABLEMENT plus inducive à la propreté de l'histoire que de voir chaque commit sur une branche. Si ceux-ci sont importants, casser la branche en petits morceaux...
- dans vos exemples de code, quelle est la différence entre la finition de la fonction" et de "travail et de s'engager"? Quand vous dites que l'arrivée de la fonctionnalité, voulez-vous dire de ne rien faire? Finition fonctionnalité peut signifier "faire plus de travail", auquel cas vous commettre. mais s'engager n'est pas mentionné. "Terminer la fonctionnalité" peut-être redondant et confus. si la fonctionnalité est fini, alors cela signifie que nous pouvons juste faire la prochaine étape. donc il faut juste parler de la prochaine étape, ne pas oublier une étape qui n'a pas besoin de quelque chose à faire, si c'est ce qu'il signifie.
- À votre point d'origine au sujet de vouloir vos modifications sauvegardées sur le serveur au lieu de seulement localement, avez-vous venez de donner à ce sujet? Est-il un moyen de pousser les modifications apportées à un "travail" de la branche, puis déplacez-les vers un "fini" de la direction de la rebase et de squash?
- Je ne fais que répondre sur le "Squash Fusion Controverse"::: il Pense que les deux formes de communication. 1) Sur la branche, des notes personnelles, peut-être un "wip presque terminé" ou "modifications après l'assurance qualité" ou "testing nouvel algorithme". Notes que c'est juste des ordures trois-quatre semaines en bas de la route.::: 2) la Communication avec les autres concepteurs et les gestionnaires. Lorsque sur la branche master, c'est mieux avec un message clair, par exemple "Demande de nouveau mot de passe lien mis en œuvre". Simple et direct
- "Github et Bitbucket (d'autres?) Pull requests" Ne PAS utiliser des outils comme GitHub ou Bitbucket de la courge. Soyez prudent en utilisant automatique de squash ou IntelliJ même. La raison? Eh bien... Tous vos messages en cours sera ajouté au message. Comme je l'ai dit dans le commentaire ci-dessus: Deux formes de communication. Réécrire le message de dire clairement ce que vous avez fait dans une courte phrase.
Vous devez vous connecter pour publier un commentaire.
"Conflits" signifie "parallèle les évolutions d'un même contenu". Si elle va "tous en enfer" lors d'une fusion, cela signifie que vous avez massive des évolutions sur le même ensemble de fichiers.
La raison pour laquelle un rebase est alors de mieux qu'une fusion, c'est que:
Je confirme que le bon flux de travail dans ce cas (évolutions communes à un ensemble de fichiers) est rebase d'abord, puis de fusionner.
Toutefois, cela signifie que, si vous poussez votre succursale locale (pour la sauvegarde de la raison), que la branche ne doit pas être tiré (ou au moins utilisé) par quelqu'un d'autre (puisque la validation de l'histoire sera réécrite par l'successives rebase).
Sur le sujet (rebase puis fusionner les flux de travail), barraponto mentionne dans les commentaires de deux postes intéressants, à la fois de randyfay.com:
(une technique similaire il existe pour le bazar)
git push --force
(au lieu d'ungit pull --rebase
par exemple)TL;DR
Un git rebase flux de travail ne vous protège pas des gens qui sont mauvais pour la résolution des conflits ou des gens qui sont habitués à un SVN flux de travail, comme suggéré dans En Évitant Git Catastrophes: Une Histoire Sanglante. Il ne fait que de la résolution des conflits plus pénible pour eux et les rend plus difficile de récupérer de la mauvaise résolution des conflits. Au lieu de cela, utiliser diff3 de sorte qu'il n'est pas si difficile, en premier lieu.
Rebase flux de travail n'est pas mieux pour la résolution des conflits!
Je suis très pro-rebase pour le nettoyage de l'histoire. Toutefois, si j'ai jamais touché à un conflit, j'ai immédiatement interrompre le rebase et de faire une fusion de la place! - Il vraiment me tue que les gens sont de recommander un rebase flux de travail comme une meilleure alternative à une opération de fusion de flux de travail pour la résolution des conflits (ce qui est exactement ce que cette question a été de savoir).
Si elle va de "tous à l'enfer" lors d'une fusion, ça va aller "tous en enfer", lors d'un rebase, et potentiellement beaucoup plus d'enfer aussi! Voici pourquoi:
Raison n ° 1: Résoudre les conflits une fois, au lieu d'une seule fois pour chaque commit
Lorsque vous rebase au lieu de fusionner, vous devrez procéder à la résolution des conflits à autant de fois que vous avez s'engage à rebase, pour le même conflit!
Véritable scénario
Je branche hors de maître de refactoriser une méthode compliquée dans une branche. Mon refactoring de travail est composé de 15 commet total que je travaille à refactoriser le code et obtenir des revues de code. Une partie de mon refactoring consiste à fixer le mixte des tabulations et des espaces qui étaient présents en maître avant que. C'est nécessaire, mais malheureusement, il sera en conflit avec tous les changements apportés par la suite à cette méthode dans le maître. Bien sûr, alors que je travaille sur cette méthode, quelqu'un fait simple, un changement légitime à la même méthode dans la branche master qui devraient être fusionnées avec mes modifications.
Quand il est temps de fusionner ma branche master, j'ai deux options:
git merge:
Je reçois un conflit. Je vois le changement qu'ils ont fait pour maîtriser et de le fusionner avec le produit final de) ma branche. Fait.
git rebase:
Je reçois un conflit avec mon première commettre. - Je résoudre le conflit et de continuer le rebase.
Je reçois un conflit avec mon deuxième commettre. - Je résoudre le conflit et de continuer le rebase.
Je reçois un conflit avec mon troisième commettre. - Je résoudre le conflit et de continuer le rebase.
Je reçois un conflit avec mon quatrième commettre. - Je résoudre le conflit et de continuer le rebase.
Je reçois un conflit avec mon cinquième commettre. - Je résoudre le conflit et de continuer le rebase.
Je reçois un conflit avec mon sixième commettre. - Je résoudre le conflit et de continuer le rebase.
Je reçois un conflit avec mon septième commettre. - Je résoudre le conflit et de continuer le rebase.
Je reçois un conflit avec mon huitième commettre. - Je résoudre le conflit et de continuer le rebase.
Je reçois un conflit avec mon neuvième commettre. - Je résoudre le conflit et de continuer le rebase.
Je reçois un conflit avec mon dixième commettre. - Je résoudre le conflit et de continuer le rebase.
Je reçois un conflit avec mon onzième commettre. - Je résoudre le conflit et de continuer le rebase.
Je reçois un conflit avec mon douzième commettre. - Je résoudre le conflit et de continuer le rebase.
Je reçois un conflit avec mon treizième commettre. - Je résoudre le conflit et de continuer le rebase.
Je reçois un conflit avec mon quatorzième commettre. - Je résoudre le conflit et de continuer le rebase.
Je reçois un conflit avec mon quinzième commettre. - Je résoudre le conflit et de continuer le rebase.
Vous avez à me faire marcher si ce est de votre flux de travail préféré. Tout ce qu'il faut est un espace correctif qui est en conflit avec un changement effectué sur le maître, et chaque livraison sera en conflit et doit être résolu. Et c'est un simple scénario avec seulement un espace de conflit. À dieu ne plaise, vous avez un réel conflit impliquant les principaux changements de code dans des fichiers et ont à résoudre que plusieurs fois.
Avec tous les extras de la résolution des conflits que vous devez faire, c'est juste augmente la possibilité que vous faites une erreur. Mais les erreurs sont bien dans git, puisque vous pouvez l'annuler, à droite? Sauf, bien sûr...
Raison n ° 2: Avec cela, il n'y a pas d'annulation!
Je pense que nous pouvons tous convenir que la résolution des conflits peut être difficile, et aussi que certaines personnes sont très mauvais. Il peut être très sujettes à des erreurs, qui, pourquoi c'est si grande que git le rend facile à défaire!
Lorsque vous fusionnez une branche, git crée une fusion s'engager à ce que peut être supprimé ou modifié si la résolution des conflits va mal. Même si vous avez déjà poussé la mauvaise fusion s'engager pour le public, faisant autorité repo, vous pouvez utiliser
git revert
pour annuler les modifications introduites par la fusion et de refaire la fusion correctement dans une nouvelle fusion commettre.Lorsque vous rebase une branche, dans le cas probable où la résolution des conflits est mal fait, vous êtes foutu. Chaque livraison contient maintenant la mauvaise fusion, et vous ne pouvez pas simplement refaire le rebase*. Au mieux, vous devez revenir en arrière et modifier chacun de ces validations. Pas de plaisir.
Après un rebase, il est impossible de déterminer ce qui était à l'origine une partie de la commet et ce qui a été présenté comme un résultat d'une mauvaise résolution des conflits.
*Il peut être possible d'annuler un rebase si vous pouvez creuser l'ancien refs de git de journaux internes, ou si vous créez une troisième branche qui pointe à la dernière validation avant le changement d'année de base.
Prendre l'enfer hors de résolution des conflits: l'utilisation diff3
Prendre ce conflit par exemple:
Regardant le conflit, il est impossible de dire ce que chaque branche a changé ou que son intention était. C'est la principale raison, à mon avis, pourquoi la résolution des conflits est confuse et difficile.
diff3 à la rescousse!
Lorsque vous utilisez le diff3, chaque nouveau conflit 3e section, la fusion ancêtre commun.
D'abord examiner la fusion ancêtre commun. Puis comparer chaque côté afin de déterminer chaque branche de l'intention. Vous pouvez voir que la TÊTE a changé EmailMessage à TextMessage. Son intention est de changer la classe utilisée pour TextMessage, en passant les mêmes paramètres. Vous pouvez aussi voir que la fonctionnalité de la direction générale de l'intention est de passer à false au lieu de true pour l' :include_timestamp option. Pour fusionner ces changements, combiner l'intention des deux:
En général:
Suppléant: Résoudre en appliquant manuellement la direction des changements
Enfin, certains conflits sont terribles à comprendre, même avec diff3. Cela arrive en particulier lorsque les diff trouve les lignes dans les communes qui ne sont pas sémantiquement commun (par exemple. les deux branches est arrivé d'avoir une ligne vide à la même place!). Par exemple, une branche des changements de la mise en retrait du corps d'une classe ou réorganise les mêmes méthodes. Dans ces cas, à une meilleure stratégie de résolution peuvent être examiner le changement de chaque côté de la fusion et de l'appliquer manuellement la diff pour les autres fichier.
Regardons comment nous pourrions résoudre un conflit dans un scénario où la fusion
origin/feature1
oùlib/message.rb
conflits.Décider si notre extrait de la branche (
HEAD
, ou--ours
) ou de la branche, nous allons fusionner (origin/feature1
, ou--theirs
) est un simple changement d'appliquer. À l'aide de diff avec le point triple (git diff a...b
) montre que les changements qui ont eu lieub
depuis sa dernière divergence dea
, ou en d'autres termes, de comparer l'ancêtre commun de a et b avec b.Découvrez plus compliqué que la version du fichier. Cela va supprimer tous les marqueurs de conflit et d'utiliser le côté que vous choisissez.
De la complexité de changement vérifié, tirer vers le haut la diff de la plus simple à changer (voir étape 1). Appliquer chaque modification de ce fichier diff pour le fichier en conflit.
git mergetool --tool=vimdiff
lors de la résolution des conflits. Il a beaucoup plus de 3-volet de visualisation du conflit. J' toujours l'utiliser....
. Qu'entendez-vous parpull up the diff of the simpler change
? Comment est-ce fait comme ungit
commande?diff
commandes à l'étape 1. En d'autres termes, regarder les diff pour la plus simple de changer, et à en appliquer les changements à la direction générale avec le plus compliqué de la changer.Dans mon flux de travail, je rebase autant que possible (et j'essaie de le faire souvent. Ne pas laisser les contradictions s'accumulent réduit considérablement la quantité et la gravité des collisions entre les branches).
Toutefois, même dans la plupart rebase à base de flux de travail, il y a une place pour les fusions.
Rappeler que l'opération de fusion crée un nœud qui a deux parents. Maintenant, considérons la situation suivante: j'ai deux entités indépendante brances A et B, et veulent maintenant de développer des choses sur la branche C qui dépend à la fois de A et de B, alors A et B sont prise en revue.
Ce que je fais, alors, est la suivante:
Maintenant, succursale C comprend les variations de A et de B, et que je puisse continuer à se développer sur elle. Si je fais tout changement à Un, puis j'ai reconstruire le graphe des branches de la façon suivante:
De cette façon, je peux effectivement maintenir l'arbitraire des graphiques de branches, mais faire quelque chose de plus complexe que la situation décrite ci-dessus est déjà trop complexe, étant donné qu'il n'existe pas d'outils automatique à faire de l'année de référence lorsque le parent changements.
NE PAS utiliser git push origin --mirror dans PRESQUE n'IMPORTE quelle CIRCONSTANCE.
Il n'a pas demander si vous êtes sûr que vous voulez faire, et vous feriez mieux d'être sûr, car il va effacer tous vos branches distantes ne sont pas sur votre zone locale.
http://twitter.com/dysinger/status/1273652486
Instructions to this machine may lead to unintended consequences, loss of work/data, or even death (at the hands of the sysad). Remember that you are solely responsible for the consequences of your actions
dans le MOTD.J'ai une question après avoir lu votre explication: se Pourrait-il que vous n'avez jamais fait un
avant de faire le " git rebase/fusion de master dans votre branche?
Parce que votre branche master ne sera pas mise à jour automatiquement à partir de votre ami référentiel. Vous devez le faire avec le
git pull origin
. I. e. peut-être que vous seriez toujours rebase à partir d'un qui ne change jamais la branche principale locale? Et puis venir pousser de temps, vous poussant dans un référentiel qui a (local) s'engage à vous n'a jamais vu et donc la poussée d'échec.Dans votre situation, je pense que votre partenaire est correct. Ce qui est intéressant au sujet de la relocalisation est qu'à l'extérieur de votre change de look comme ils tout s'est passé dans une séquence de nettoyage par eux-mêmes. Cela signifie
Vous pouvez toujours continuer à pousser votre privé branche de développement pour le dépôt distant pour le bien de la sauvegarde, mais les autres ne doivent pas considérer que comme un "public" de la branche, puisque vous serez la relocalisation. BTW, une simple commande pour le faire, c'est
git push --mirror origin
.L'article L'emballage du logiciel via Git fait un assez bon travail en expliquant les compromis dans la fusion rapport à la relocalisation. C'est un peu différent, mais le principe est le même, - elle vient essentiellement de déterminer si vos branches soient publics ou privés, et comment vous prévoyez de les intégrer dans la canalisation principale.
origin
, vous devriez miroir pour une troisième dédiée-référentiel de sauvegarde.Ni votre partenaire, ni dans votre flux de travail si vous avez à venir à travers les conflits qui n'a aucun sens. Même si vous aviez, si vous suivez le suggère les flux de travail, puis après la résolution a "forcé" push ne devrait pas être nécessaire. Il suggère que vous n'avez pas fusionné en réalité la branche à laquelle vous étaient en train de pousser, mais ont dû pousser une branche qui n'était pas un descendant de la distance de la pointe.
Je pense que vous avez besoin d'examiner attentivement ce qui s'est passé. Quelqu'un d'autre pourrait avoir (volontairement ou non) remonté le maître branche entre votre création de l'agence locale et le point où vous avez tenté de l'intégrer dans la branche locale?
Par rapport à de nombreux autres systèmes de contrôle de version, j'ai trouvé que l'utilisation de Git implique moins de combats de l'outil et permet de travailler sur les problèmes fondamentaux de votre source de flux. Git n'est pas de la magie, de sorte que les changements contradictoires provoquer des conflits, mais cela devrait rendre facile à écrire pour agir en son suivi de commettre le lien de filiation.
De ce que j'ai observé, git merge tend à maintenir les branches distinctes, même après la fusion, alors que rebase puis de fusionner les combine dans une seule branche.
La deuxième vient de sortir beaucoup plus propre, alors que dans le premier cas, il serait plus facile de savoir qui s'engage appartiennent à la branche, même après la fusion.
"Même si vous êtes un développeur simple avec seulement quelques branches, il vaut la peine de prendre l'habitude de l'aide de rebase et fusionner correctement. Le travail de base, motif ressemblera:
Créer une nouvelle branche B de branche Un
Ajouter/valider les modifications sur la branche B
Rebase mises à jour à partir de la direction générale de
Fusionner les changements de la branche B sur la branche d'Un"
https://www.atlassian.com/git/tutorials/merging-vs-rebasing/
Avec Git il n'y a pas de “bonne” flux de travail. Utilisons ce qui flotte votre bateau. Toutefois, si vous ne cesse de recevoir des conflits lors de la fusion des branches peut-être vous devriez coordonner vos efforts à mieux avec vos compagnons de développeur(s)? Les sons comme les deux d'entre vous garder d'édition les mêmes fichiers. Aussi, faites attention aux espaces et à la subversion des mots-clés (c'est à dire, “$Id$” et autres).