Git workflow et rebase vs merge questions

971

J'utilise Git depuis quelques mois sur un projet avec un autre développeur. J'ai plusieurs années d'expérience avec SVN , donc je suppose que j'apporte beaucoup de bagages à la relation.

J'ai entendu dire que Git est excellent pour les branchements et les fusions, et jusqu'à présent, je ne le vois pas. Bien sûr, la ramification est très simple, mais quand j'essaye de fusionner, tout va tout au diable. Maintenant, je suis habitué à cela de SVN, mais il me semble que je viens d'échanger un système de version sous-pair pour un autre.

Mon partenaire me dit que mes problèmes proviennent de mon désir de fusionner bon gré mal gré et que je devrais utiliser rebase au lieu de fusionner dans de nombreuses situations. Par exemple, voici le workflow qu'il a défini:

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éez une branche de fonction, TOUJOURS rebaser du maître à la branche et fusionner de la branche au maître. Il est important de noter que la succursale reste toujours locale.

Voici le workflow avec lequel j'ai commencé

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 toujours la fusion au lieu du rebasage, et je pousse ma branche de fonctionnalité (et ma branche de fonctionnalité se valide) vers le référentiel distant.

Mon raisonnement pour la branche distante est que je veux que mon travail soit sauvegardé pendant que je travaille. Notre référentiel est automatiquement sauvegardé et peut être restauré en cas de problème. Mon ordinateur portable n'est pas, ou pas aussi complètement. Par conséquent, je déteste avoir du code sur mon ordinateur portable qui ne se reflète pas ailleurs.

Mon raisonnement pour la fusion au lieu de rebaser est que la fusion semble être standard et rebaser semble être une fonctionnalité avancée. Mon instinct est que ce que j'essaie de faire n'est pas une configuration avancée, donc le rebase ne devrait pas être nécessaire. J'ai même parcouru le nouveau livre de programmation pragmatique sur Git, et ils couvrent largement la fusion et mentionnent à peine le rebase.

Quoi qu'il en soit, je suivais mon flux de travail sur une branche récente, et quand j'ai essayé de le fusionner à nouveau en master, tout est allé en enfer. Il y a eu des tonnes de conflits avec des choses qui n'auraient pas dû avoir d'importance. Les conflits n'avaient tout simplement aucun sens pour moi. Il m'a fallu une journée pour tout régler, et a finalement abouti à une poussée forcée vers le maître distant, car mon maître local a résolu tous les conflits, mais le distant n'était toujours pas satisfait.

Quel est le flux de travail "correct" pour quelque chose comme ça? Git est censé rendre les branchements et les fusions super faciles, et je ne le vois tout simplement pas.

Mise à jour 2011-04-15

Cela semble être une question très populaire, alors j'ai pensé que je mettrais à jour mes deux années d'expérience depuis ma première demande.

Il s'avère que le flux de travail d'origine est correct, 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, car nous avons tendance à faire des fusions de squash au lieu de fusions brutes. ( Remarque: cela est controversé, voir ci-dessous. ) Cela nous permet de transformer l'ensemble de notre branche de fonctionnalités en un seul commit sur master. Ensuite, nous supprimons notre branche de fonctionnalités. Cela nous permet de structurer logiquement nos commits sur master, même s'ils sont un peu désordonnés sur nos branches. Voici donc 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

Controverse de fusion de squash - Comme plusieurs commentateurs l'ont souligné, la fusion de squash va jeter tout l'historique de votre branche de fonctionnalités. Comme son nom l'indique, il écrase tous les commits en un seul. Pour les petites fonctionnalités, cela a du sens car il les condense vers le bas dans un seul paquet. Pour les fonctionnalités plus importantes, ce n'est probablement pas une bonne idée, surtout si vos commits individuels sont déjà atomiques. Cela se résume vraiment à la préférence personnelle.

Github et Bitbucket (autres?) Demandes Pull - Au cas où vous vous demandez comment la fusion / rebase est liée aux demandes Pull, je vous recommande de suivre toutes les étapes ci-dessus jusqu'à ce que vous soyez prêt à fusionner à nouveau vers le maître. Au lieu de fusionner manuellement avec git, vous acceptez simplement le PR. Notez que cela ne fera pas de fusion de squash (du moins pas par défaut), mais que le non-squash, l'avance rapide n'est pas la convention de fusion acceptée dans la communauté Pull Request (pour autant que je sache). Plus précisément, cela 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 je ne veux plus jamais revenir à SVN. Si vous avez du mal, restez avec et finalement vous verrez la lumière au bout du tunnel.

Michée
la source
31
Malheureusement, le nouveau livre de programmation pragmatique est principalement écrit à partir de l'utilisation de Git tout en continuant à penser en SVN, et dans ce cas, il vous a induit en erreur. Dans Git, le rebase garde les choses simples quand elles le peuvent. Votre expérience pourrait vous dire que votre flux de travail ne fonctionne pas dans Git, pas que Git ne fonctionne pas.
Paul
18
Je ne recommanderais pas la fusion de squash dans ce cas, car il n'enregistre aucune information sur ce qui est fusionné (tout comme svn, mais pas de mergeinfo ici).
Marius K
7
J'adore la note en bas, j'ai eu une expérience similaire de lutte avec Git, mais j'ai maintenant du mal à imaginer de ne pas l'utiliser. Merci aussi pour l'explication finale, beaucoup aidé à rebasecomprendre
Jon Phenow
6
Une fois la fonctionnalité terminée, ne devez-vous pas rebaser une dernière fois avant de fusionner new_feature en master?
softarn
17
Votre flux de travail perd tout l'historique des validations de la branche supprimée :(
Max Nanasy

Réponses:

372

"Conflits" signifie "évolutions parallèles d'un même contenu". Donc, si ça va "tout en enfer" lors d'une fusion, cela signifie que vous avez des évolutions massives sur le même ensemble de fichiers.

La raison pour laquelle un rebasage est alors meilleur qu'une fusion est que:

  • vous réécrivez votre historique de commit local avec celui du maître (puis réappliquez votre travail, résolvant alors tout conflit)
  • la fusion finale sera certainement une "avance rapide", car elle aura tout l'historique de validation du maître, plus seulement vos modifications à réappliquer.

Je confirme que le flux de travail correct dans ce cas (évolutions sur un ensemble commun de fichiers) est d' abord rebaser, puis fusionner .

Cependant, cela signifie que, si vous poussez votre branche locale (pour une raison de sauvegarde), cette branche ne doit pas être tirée (ou au moins utilisée) par quelqu'un d'autre (car l'historique de validation sera réécrit par la rebase successive).


Sur ce sujet (rebaser puis fusionner le workflow), barraponto mentionne dans les commentaires deux articles intéressants, tous deux de randyfay.com :

En utilisant cette technique, votre travail va toujours au-dessus de la branche publique comme un patch qui est à jour avec le courant HEAD.

(une technique similaire existe pour le bazar )

VonC
la source
27
Pour une technique qui permet le rebasage et le partage, voir softwareswirl.blogspot.com/2009/04/…
mhagger
2
randyfay.com/node/91 et randyfay.com/node/89 sont de merveilleuses lectures. ces articles m'ont fait comprendre ce qui était fatigué avec mon flux de travail, et ce qu'un flux de travail idéal serait.
Capi Etheriel
juste pour que les choses soient claires, le rebasage de la branche principale sur votre section locale consiste essentiellement à mettre à jour toute histoire que votre section locale a peut-être manquée et dont le maître a connaissance après toute sorte de fusion?
hellatan
@dtan ce que je décris ici est de rebaser le local sur le maître Vous ne mettez pas exactement à jour l'historique local, mais réappliquez plutôt l'historique local au-dessus du maître afin de résoudre tout conflit au sein de la branche locale.
VonC
386

TL; DR

Un workflow git rebase ne vous protège pas contre les personnes qui sont mauvaises dans la résolution des conflits ou contre les personnes habituées à un workflow SVN, comme suggéré dans Éviter les catastrophes Git: une histoire sanglante . Cela rend la résolution des conflits plus fastidieuse pour eux et rend plus difficile la récupération après une mauvaise résolution des conflits. Utilisez plutôt diff3 pour que ce ne soit pas si difficile en premier lieu.


Rebaser le workflow n'est pas mieux pour la résolution des conflits!

Je suis très pro-rebase pour nettoyer l'histoire. Cependant, si je frappe un conflit, j'interromps immédiatement le rebase et je fais une fusion à la place! Cela me tue vraiment que les gens recommandent un workflow de rebase comme meilleure alternative à un workflow de fusion pour la résolution des conflits (c'est exactement ce sur quoi portait cette question).

S'il va "tout en enfer" lors d'une fusion, il ira "tout en enfer" lors d'un rebase, et potentiellement beaucoup plus en enfer aussi! Voici pourquoi:

Raison n ° 1: résoudre les conflits une fois, au lieu d'une fois pour chaque validation

Lorsque vous rebaserez au lieu de fusionner, vous devrez effectuer la résolution des conflits autant de fois que vous vous êtes engagé à rebaser, pour le même conflit!

Scénario réel

Je dérive du master pour refactoriser une méthode compliquée dans une branche. Mon travail de refactoring est composé de 15 commits au total alors que je travaille pour le refactoriser et obtenir des révisions de code. Une partie de ma refactorisation consiste à corriger les tabulations et les espaces mixtes qui étaient présents dans master auparavant. Ceci est nécessaire, mais malheureusement, il entrera en conflit avec toute modification apportée par la suite à cette méthode dans master. Effectivement, pendant que je travaille sur cette méthode, quelqu'un apporte une modification simple et légitime à la même méthode dans la branche principale qui devrait être fusionnée avec mes modifications.

Lorsqu'il est temps de fusionner ma branche avec Master, j'ai deux options:

git merge: j'ai un conflit. Je vois le changement qu'ils ont fait pour maîtriser et le fusionner avec (le produit final de) ma branche. Terminé.

git rebase: J'ai un conflit avec mon premier commit. Je résous le conflit et continue le rebase. J'ai un conflit avec mon deuxième commit. Je résous le conflit et continue le rebase. J'ai un conflit avec mon troisième commit. Je résous le conflit et continue le rebase. J'ai un conflit avec mon quatrième engagement. Je résous le conflit et continue le rebase. J'ai un conflit avec mon cinquième commit. Je résous le conflit et continue le rebase. J'ai un conflit avec mon sixième engagement. Je résous le conflit et continue le rebase. J'ai un conflit avec mon septièmecommettre. Je résous le conflit et continue le rebase. J'ai un conflit avec mon huitième engagement. Je résous le conflit et continue le rebase. J'ai un conflit avec mon neuvième engagement. Je résous le conflit et continue le rebase. J'ai un conflit avec mon dixième commit. Je résous le conflit et continue le rebase. J'ai un conflit avec mon onzième commit. Je résous le conflit et continue le rebase. J'ai un conflit avec mon douzième commit. Je résous le conflit et continue le rebase. J'ai un conflit avec mon treizième engagement. Je résous le conflit et continue le rebase. J'ai un conflit avec mon quatorzièmecommettre. Je résous le conflit et continue le rebase. J'ai un conflit avec mon quinzième commit. Je résous le conflit et continue le rebase.

Vous avez plaisantez si c'est votre flux de travail préféré. Il suffit d'un correctif d'espace qui entre en conflit avec une modification effectuée sur le maître, et chaque validation entrera en conflit et devra être résolue. Et ceci est un scénario simple avec seulement un conflit d'espaces. Que le ciel interdise que vous ayez un vrai conflit impliquant des changements de code majeurs entre les fichiers et que vous deviez le résoudre plusieurs fois.

Avec toute la résolution de conflit supplémentaire que vous devez faire, cela augmente simplement la possibilité que vous fassiez une erreur . Mais les erreurs sont bonnes dans git puisque vous pouvez annuler, non? Sauf bien sûr ...

Raison n ° 2: Avec rebase, 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 mauvaises dans ce domaine. Il peut être très sujet aux erreurs, c'est pourquoi il est si génial que git le rend facile à défaire!

Lorsque vous fusionnez une branche, git crée un commit de fusion qui peut être ignoré ou modifié si la résolution du conflit se passe mal. Même si vous avez déjà poussé la mauvaise validation de fusion vers le référentiel public / faisant autorité, vous pouvez utiliser git revertpour annuler les modifications apportées par la fusion et refaire la fusion correctement dans une nouvelle validation de fusion.

Lorsque vous rebasez une branche, dans le cas probable où la résolution des conflits est mal effectuée, vous êtes foutu. Chaque commit 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 des commits concernés. Pas drôle.

Après un rebase, il est impossible de déterminer ce qui faisait initialement partie des validations et ce qui a été introduit suite à une mauvaise résolution de conflit.

* Il peut être possible d'annuler un rebase si vous pouvez extraire les anciennes références des journaux internes de git, ou si vous créez une troisième branche qui pointe vers le dernier commit avant le rebasage.

Sortez de la résolution des conflits: utilisez diff3

Prenez ce conflit par exemple:

<<<<<<< HEAD
TextMessage.send(:include_timestamp => true)
=======
EmailMessage.send(:include_timestamp => false)
>>>>>>> feature-branch

En regardant le conflit, il est impossible de dire ce que chaque branche a changé ou quelle était son intention. C'est à mon avis la principale raison pour laquelle la résolution des conflits est confuse et difficile.

diff3 à la rescousse!

git config --global merge.conflictstyle diff3

Lorsque vous utilisez diff3, chaque nouveau conflit aura une 3ème section, l'ancêtre commun fusionné.

<<<<<<< HEAD
TextMessage.send(:include_timestamp => true)
||||||| merged common ancestor
EmailMessage.send(:include_timestamp => true)
=======
EmailMessage.send(:include_timestamp => false)
>>>>>>> feature-branch

Examinez d'abord l'ancêtre commun fusionné. Comparez ensuite chaque côté pour déterminer l'intention de chaque branche. Vous pouvez voir que HEAD a changé EmailMessage en TextMessage. Son intention est de changer la classe utilisée en TextMessage, en passant les mêmes paramètres. Vous pouvez également voir que l'intention de la branche de fonctionnalité est de passer false au lieu de true pour l'option: include_timestamp. Pour fusionner ces modifications, combinez l'intention des deux:

TextMessage.send(:include_timestamp => false)

En général:

  1. Comparez l'ancêtre commun à chaque branche et déterminez quelle branche présente le changement le plus simple
  2. Appliquer cette modification simple à la version du code de l'autre branche, afin qu'elle contienne à la fois la modification la plus simple et la plus complexe
  3. Supprimez toutes les sections du code de conflit autres que celle dans laquelle vous venez de fusionner les modifications

Autre solution: résoudre en appliquant manuellement les modifications de la branche

Enfin, certains conflits sont terribles à comprendre même avec diff3. Cela se produit surtout lorsque diff trouve des lignes communes qui ne sont pas sémantiquement communes (par exemple, les deux branches se trouvaient avoir une ligne vierge au même endroit!). Par exemple, une branche modifie l'indentation du corps d'une classe ou réorganise des méthodes similaires. Dans ces cas, une meilleure stratégie de résolution peut consister à examiner la modification de chaque côté de la fusion et à appliquer manuellement le diff à l'autre fichier.

Voyons comment nous pourrions résoudre un conflit dans un scénario où la fusion origin/feature1où les lib/message.rbconflits.

  1. Décidez si notre branche actuellement extraite ( HEAD, ou --ours) ou la branche que nous fusionnons ( origin/feature1, ou --theirs) est une modification plus simple à appliquer. L'utilisation de diff avec triple point ( git diff a...b) montre les changements survenus bdepuis sa dernière divergence par rapport à a, ou en d'autres termes, compare l'ancêtre commun de a et b à b.

    git diff HEAD...origin/feature1 -- lib/message.rb # show the change in feature1
    git diff origin/feature1...HEAD -- lib/message.rb # show the change in our branch
    
  2. Découvrez la version la plus compliquée du fichier. Cela supprimera tous les marqueurs de conflit et utilisera le côté que vous choisissez.

    git checkout --ours -- lib/message.rb   # if our branch's change is more complicated
    git checkout --theirs -- lib/message.rb # if origin/feature1's change is more complicated
    
  3. Avec le changement compliqué vérifié, tirez vers le haut le diff du changement plus simple (voir l'étape 1). Appliquez chaque modification de ce diff au fichier en conflit.

Edward Anderson
la source
4
Comment la fusion de tous les conflits en une seule fois fonctionnerait-elle mieux que les engagements individuels? J'ai déjà des problèmes en fusionnant des validations simples (en particulier de personnes qui ne divisent pas les validations en parties logiques ET fournissent des tests suffisants pour la vérification). En outre, le rebase n'est pas pire que la fusion en ce qui concerne les options de sauvegarde, l'utilisation intelligente du rebase interactif et des outils comme tortoisegit (qui permet de sélectionner les validations à inclure) aidera beaucoup.
prusswan
8
J'ai l'impression d'avoir abordé la raison du n ° 1. Si les validations individuelles ne sont pas logiquement cohérentes, raison de plus pour fusionner la branche logiquement cohérente, afin que vous puissiez réellement comprendre le conflit. Si commit 1 est bogué et commit 2 le corrige, la fusion de commit 1 sera source de confusion. Il y a des raisons légitimes pour lesquelles vous pourriez avoir 15 conflits d'affilée, comme celui que j'ai décrit ci-dessus. Votre argument pour que le rebase ne soit pas pire n'est pas non plus fondé. Rebase mélange les mauvaises fusions dans les bons commits originaux et ne laisse pas les bons commits pour vous permettre de réessayer. Fusionner le fait.
Edward Anderson
6
Je suis entièrement d'accord avec toi nilbus. Super article; cela clarifie certaines choses. Je me demande si rerere serait d'une quelconque aide ici. Aussi, merci pour la suggestion d'utiliser diff3, je vais certainement l'activer en ce moment.
derick
45
+1 pour m'avoir parlé de diff3 seul - combien de fois regardait un conflit incompréhensible maudissant quiconque est responsable de ne pas me dire ce que l'ancêtre commun avait à dire. Merci beaucoup.
John
4
Cela aurait dû être la réponse acceptée. Le flux de travail de rebase est également horrible car il masque le fait qu'il y avait une énorme divergence dans la base de code à un moment donné, ce qui pourrait être utile de savoir si vous voulez comprendre comment le code que vous regardez a été écrit. Seules les petites branches qui n'entrent pas en conflit doivent être rebasées sur le maître.
Robert Rüger
32

Dans mon flux de travail, je rebase autant que possible (et j'essaye de le faire souvent. Ne pas laisser les écarts s'accumuler réduit drastiquement la quantité et la gravité des collisions entre les branches).

Cependant, même dans un flux de travail basé principalement sur la refonte, il existe une place pour les fusions.

Rappelez-vous que la fusion crée en fait un nœud qui a deux parents. Considérons maintenant la situation suivante: j'ai deux brances de fonctionnalités indépendantes A et B, et je souhaite maintenant développer des éléments sur la branche de fonctionnalités C qui dépend à la fois de A et de B, tandis que A et B sont examinés.

Ce que je fais alors, c'est ce qui suit:

  1. Créez (et extrayez) la branche C en haut de A.
  2. Fusionnez-le avec B

Maintenant, la branche C inclut des modifications à la fois de A et de B, et je peux continuer à développer dessus. Si je modifie A, je reconstruis le graphique des branches de la manière suivante:

  1. créer la branche T sur le nouveau sommet de A
  2. fusionner T avec B
  3. rebaser C sur T
  4. supprimer la branche T

De cette façon, je peux réellement maintenir des graphiques arbitraires 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'y a pas d'outil automatique pour effectuer le rebasage lorsque le parent change.

Alex Gontmakher
la source
1
Vous pourriez obtenir la même chose avec juste des rebases. La fusion n'est en fait pas nécessaire ici (sauf si vous ne voulez pas dupliquer les commits - mais je ne vois guère cela comme un argument).
odwl
1
En effet, je ne veux pas dupliquer les commits. J'aimerais garder la structure en vol de mon travail aussi propre que possible. Mais c'est une question de goût personnel et ne convient pas nécessairement à tout le monde.
Alex Gontmakher
Je suis 100% d'accord avec le premier paragraphe. (La réponse d'Edward fonctionne là où ce n'est pas le cas, mais je préfère que tous les projets dans le monde fonctionnent comme vous le suggérez). Le reste de la réponse semble un peu farfelu en ce sens que travailler sur C pendant que A et B sont en cours est déjà en quelque sorte risqué (au moins dans la mesure où cela dépend vraiment de A et B), et même à la fin vous ne conserverait probablement pas les fusions (C serait rebasé en plus des plus récents et des plus grands).
Alois Mahdal
23

NE PAS utiliser git push origin --mirror SOUS PRESQUE AUCUNE CIRCONSTANCE.

Il ne vous demande pas si vous êtes sûr de vouloir faire cela, et vous feriez mieux d'être sûr, car cela effacera toutes vos branches distantes qui ne sont pas sur votre box locale.

http://twitter.com/dysinger/status/1273652486

Scott Brown
la source
6
Ou ne faites pas des choses dont vous n'êtes pas sûr du résultat? Une machine que j'avais l'habitude d'administrer avait 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.
richo
utilisez-le si vous avez un dépôt en miroir (bien que dans mon cas, il est maintenant exécuté par un utilisateur spécial au
dépôt
14

J'ai une question après avoir lu votre explication: se pourrait-il que vous n'ayez jamais fait

git checkout master
git pull origin
git checkout my_new_feature

avant de faire le 'git rebase / merge master' dans votre branche de fonctionnalité?

Parce que votre branche principale ne se mettra pas à jour automatiquement à partir du référentiel de votre ami. Vous devez le faire avec le git pull origin. C'est-à-dire que vous rebaseriez toujours à partir d'une branche maîtresse locale sans changement? Et puis viennent le temps de push, vous poussez dans un référentiel qui a des commits (locaux) que vous n'avez jamais vus et donc le push échoue.

knweiss
la source
13

Dans votre situation, je pense que votre partenaire a raison. Ce qui est bien avec le rebasage, c'est que pour l'extérieur, vos changements semblent tous se produire dans une séquence propre par eux-mêmes. Ça signifie

  • vos modifications sont très faciles à examiner
  • vous pouvez continuer à faire de petits commits sympas et pourtant vous pouvez rendre les ensembles de ces commits publics (en les fusionnant dans master) d'un seul coup
  • lorsque vous regardez la branche master publique, vous verrez différentes séries de validations pour différentes fonctionnalités par différents développeurs, mais elles ne seront pas toutes mélangées

Vous pouvez toujours continuer à pousser votre branche de développement privé vers le référentiel distant pour des raisons de sauvegarde, mais d'autres ne devraient pas traiter cela comme une branche "publique" car vous allez rebaser. BTW, une commande simple pour ce faire est git push --mirror origin.

L'article Logiciel d'emballage utilisant Git explique assez bien les compromis entre fusion et rebasage. C'est un contexte un peu différent, mais les principes sont les mêmes - il s'agit essentiellement de savoir si vos succursales sont publiques ou privées et comment vous envisagez de les intégrer au réseau principal.

Pat Notz
la source
1
Le lien vers le logiciel de packaging utilisant git ne fonctionne plus. Je n'ai pas pu trouver un bon lien pour modifier la réponse originale.
Chetan
Vous ne devez pas mettre en miroir origin, vous devez mettre en miroir vers un troisième référentiel de sauvegarde dédié.
Miral
12

Quoi qu'il en soit, je suivais mon flux de travail sur une branche récente, et quand j'ai essayé de le fusionner à nouveau en master, tout est allé en enfer. Il y a eu des tonnes de conflits avec des choses qui n'auraient pas dû avoir d'importance. Les conflits n'avaient tout simplement aucun sens pour moi. Il m'a fallu une journée pour tout régler, et a finalement abouti à une poussée forcée vers le maître distant, car mon maître local a résolu tous les conflits, mais le distant n'était toujours pas satisfait.

Dans vos flux de travail, ni ceux de votre partenaire, ni ceux que vous avez suggérés, vous n'auriez pas dû rencontrer des conflits qui n'avaient aucun sens. Même si vous l'aviez fait, si vous suivez les workflows suggérés, une résolution forcée ne devrait plus être nécessaire après la résolution. Cela suggère que vous n'avez pas réellement fusionné la branche vers laquelle vous poussiez, mais que vous avez dû pousser une branche qui n'était pas un descendant de la pointe distante.

Je pense que vous devez regarder attentivement ce qui s'est passé. Quelqu'un d'autre aurait-il (délibérément ou non) rembobiné la branche maître distante entre votre création de la branche locale et le point auquel vous avez tenté de la fusionner à nouveau 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 lutte contre l'outil et vous permet de travailler sur les problèmes fondamentaux de vos flux source. Git n'effectue pas de magie, donc les changements conflictuels provoquent des conflits, mais il devrait faciliter la tâche d'écriture par le suivi de la filiation de la validation.

CB Bailey
la source
Vous sous-entendez que l'OP a un rebase ou une erreur non découvert dans son processus, non?
krosenvold
8

"Même si vous êtes un développeur unique avec seulement quelques branches, cela vaut la peine de prendre l'habitude d'utiliser correctement le rebase et la fusion. Le modèle de travail de base ressemblera à:

  • Créer une nouvelle branche B à partir de la branche A existante

  • Ajouter / valider des modifications sur la branche B

  • Rebaser les mises à jour de la branche A

  • Fusionner les modifications de la branche B vers la branche A "

https://www.atlassian.com/git/tutorials/merging-vs-rebasing/

Rakka Rage
la source
7

D'après ce que j'ai observé, git merge a tendance à garder les branches séparées même après la fusion, tandis que rebase puis merge les combine en une seule branche. Ce dernier sort beaucoup plus propre, alors que dans le premier, il serait plus facile de savoir quels commits appartiennent à quelle branche même après la fusion.

Pepe
la source
4

Avec Git, il n'y a pas de flux de travail «correct». Utilisez tout ce qui flotte sur votre bateau. Cependant, si vous rencontrez constamment des conflits lors de la fusion de succursales, vous devriez peut-être mieux coordonner vos efforts avec vos collègues développeurs? On dirait que vous continuez à éditer les mêmes fichiers. Faites également attention aux mots clés d'espaces et de subversion (c.-à-d. «$ Id $» et autres).

Bombe
la source
0

J'utilise uniquement le flux de travail de rebase, car il est visuellement plus clair (non seulement dans GitKraken, mais aussi dans Intellij et dans gitk, mais je recommande le plus souvent): vous avez une branche, elle provient du maître et elle retourne au maître . Lorsque le diagramme est propre et beau, vous saurez que rien ne va jamais en enfer .

entrez la description de l'image ici

Mon flux de travail est presque le même que le vôtre, mais avec une seule petite différence: j'en squashengage un dans ma branche locale avant rebasema branche sur les dernières modifications master, car:

rebasefonctionne sur la base de chaque commit

ce qui signifie que si vous avez 15 commits changeant la même ligne que mastervous, vous devez vérifier 15 fois si vous ne courez pas, mais ce qui compte, c'est le résultat final, non?

Ainsi, l'ensemble du flux de travail est:

  1. Commander masteret tirer pour vous assurer que vous disposez de la dernière version

  2. À partir de là, créez une nouvelle succursale

  3. Faites votre travail là-bas, vous pouvez librement vous engager plusieurs fois, et pousser à distance, pas de soucis, car c'est votre branche.

  4. Si quelqu'un vous dit, "hé, mon PR / MR est approuvé, maintenant il est fusionné pour maîtriser", vous pouvez les chercher / les retirer. Vous pouvez le faire à tout moment ou à l'étape 6.

  5. Après avoir fait tout votre travail, validez-les, et si vous avez plusieurs validations , écrasez-les (ce sont toutes vos tâches, et combien de fois vous modifiez une ligne de code n'a pas d'importance; la seule chose importante est la version finale). Poussez-le ou non, cela n'a pas d'importance.

  6. Passez à la caisse master, pullencore une fois pour vous assurer que vous disposez des dernières informations masterlocales. Votre diagramme doit ressembler à ceci:

entrez la description de l'image ici

Comme vous pouvez le voir, vous êtes sur votre branche locale, qui provient d'un statut obsolète master, tandis que master(local et distant) a progressé avec les changements de votre collègue.

  1. Revenez à votre succursale et rebasez-la pour la maîtriser. Vous n'aurez maintenant qu'un seul commit, donc vous ne résolvez les conflits qu'une seule fois (et dans GitKraken, vous n'avez qu'à faire glisser votre branche masteret à choisir "Rebase"; une autre raison pour laquelle j'aime ça.) Après cela, vous serez comme:

entrez la description de l'image ici

  1. Alors maintenant, vous avez toutes les modifications les plus récentes master, combinées avec les modifications de votre branche. Vous pouvez maintenant pousser vers votre télécommande et, si vous avez déjà poussé, vous devrez forcer la poussée; Git vous dira que vous ne pouvez pas simplement avancer rapidement. C'est normal, à cause du rebase, vous avez changé le point de départ de votre branche. Mais vous ne devriez pas avoir peur: utilisez la force avec sagesse . Au final, la télécommande est aussi votre branche donc vous n'affectez pas mastermême si vous faites quelque chose de mal.

  2. Créez le PR / MR et attendez qu'il soit approuvé, mastervotre contribution aura donc . Félicitations! Ainsi, vous pouvez maintenant masterpasser à la caisse , retirer vos modifications et supprimer votre branche locale afin de nettoyer le diagramme. La branche distante doit également être supprimée, si cela n'est pas fait lorsque vous la fusionnez dans master.

Le diagramme final est à nouveau clair et net:

entrez la description de l'image ici

WesternGun
la source