Quand est-il recommandé d'utiliser le rebase Git par rapport à la fusion Git?
Dois-je encore fusionner après un rebasage réussi?
Quand est-il recommandé d'utiliser le rebase Git par rapport à la fusion Git?
Dois-je encore fusionner après un rebasage réussi?
Réponses:
Alors, quand utilisez-vous l'un ou l'autre?
init
un nouveau add
dépôt , le fichier et commit
. Extraire une nouvelle branche de fonctionnalité ( checkout -b feature
.) Modifiez le fichier texte, validez et répétez afin qu'il y ait deux nouvelles validations dans la branche de fonctionnalité. Ensuite checkout master
et merge feature
. Dans log
, je vois mon commit initial sur master, suivi des deux qui ont été fusionnés depuis la fonctionnalité. Si vous merge --squash feature
, la fonctionnalité est fusionnée dans master mais non validée, le seul nouveau commit sur master sera celui que vous réaliserez vous-même.
C'est simple. Avec rebase, vous dites d'utiliser une autre branche comme nouvelle base pour votre travail.
Si vous avez, par exemple, une branche master
, vous créez une branche pour implémenter une nouvelle fonctionnalité, et dites que vous la nommez cool-feature
, bien sûr la branche principale est la base de votre nouvelle fonctionnalité.
Maintenant, à un certain moment, vous souhaitez ajouter la nouvelle fonctionnalité que vous avez implémentée dans la master
branche. Vous pouvez simplement basculer master
et fusionner la cool-feature
branche:
$ git checkout master
$ git merge cool-feature
Mais de cette façon, un nouveau commit factice est ajouté. Si vous voulez éviter l'histoire des spaghettis, vous pouvez rebaser :
$ git checkout cool-feature
$ git rebase master
Et puis fusionnez-le dans master
:
$ git checkout master
$ git merge cool-feature
Cette fois, puisque la branche de sujet a les mêmes validations de master plus les validations avec la nouvelle fonctionnalité, la fusion ne sera qu'une avance rapide.
but this way a new dummy commit is added, if you want to avoid spaghetti-history
- comment c'est mauvais?
Sean Schofield
dans un commentaire: "Rebase est également agréable car une fois que vous avez finalement fusionné vos données dans master (ce qui est trivial comme déjà décrit), vous l'avez placé en" haut "de l'historique de vos validations. les projets où les fonctionnalités peuvent être écrites mais fusionnées plusieurs semaines plus tard, vous ne voulez pas simplement les fusionner dans le master car elles sont "bourrées" dans le chemin du master dans l'histoire. Personnellement, j'aime pouvoir faire git log et voir cette fonctionnalité récente juste en haut. "Notez que les dates de commit sont préservées - rebase ne change pas ces informations."
merge
, rebase
, fast-forward
, etc.) font référence à des manipulations spécifiques d'un graphe orienté acyclique. Ils deviennent plus faciles à raisonner avec ce modèle mental à l'esprit.
Pour compléter ma propre réponse mentionnée par TSamper ,
un rebase est assez souvent une bonne idée à faire avant une fusion, car l'idée est que vous intégriez dans votre branche Y
le travail de la branche B
sur laquelle vous allez fusionner.
Mais encore une fois, avant de fusionner, vous résolvez tout conflit dans votre branche (par exemple: "rebase", comme dans "rejouer mon travail dans ma branche à partir d'un point récent de la branche B
).
Si cela est fait correctement, la fusion ultérieure de votre branche vers la branche B
peut être en avance rapide.
une fusion affecte directement la branche de destination B
, ce qui signifie que les fusions devraient être triviales, sinon cette branche B
peut être longue pour revenir à un état stable (il est temps pour vous de résoudre tous les conflits)
le point de fusionner après un rebase?
Dans le cas que je décris, je rebase B
sur ma branche, juste pour avoir l'opportunité de rejouer mon travail depuis un point plus récent B
, mais en restant dans ma branche.
Dans ce cas, une fusion est toujours nécessaire pour intégrer mon travail "rejoué" B
.
L'autre scénario ( décrit dans Git Ready par exemple), consiste à importer votre travail directement B
via un rebase (qui conserve tous vos bons commits, ou même vous donne la possibilité de les réorganiser via un rebase interactif).
Dans ce cas (où vous rebasez tout en étant dans la branche B), vous avez raison: aucune autre fusion n'est nécessaire:
Un arbre Git par défaut lorsque nous n'avons pas fusionné ni rebasé
on obtient en rebasant:
Ce deuxième scénario concerne: comment puis-je récupérer une nouvelle fonctionnalité dans le maître.
Mon propos, en décrivant le premier scénario de rebase, est de rappeler à tout le monde qu'un rebase peut également être utilisé comme une étape préliminaire à cela (c'est-à-dire "remettre la nouvelle fonctionnalité dans le maître").
Vous pouvez utiliser le rebasage pour amener d'abord le maître "dans" la branche de nouvelles fonctionnalités: le rebasage rejouera les validations de nouvelles fonctionnalités à partir de HEAD master
, mais toujours dans la branche des nouvelles fonctionnalités, déplaçant efficacement le point de départ de votre branche d'un ancien commit principal vers HEAD-master
.
Cela vous permet de résoudre tous les conflits dans votre branche (ce qui signifie, de manière isolée, tout en permettant au maître de continuer à évoluer en parallèle si votre étape de résolution de conflit prend trop de temps).
Ensuite, vous pouvez passer en master et fusionner new-feature
(ou rebaser new-feature
sur master
si vous souhaitez conserver les commits effectués dans votrenew-feature
branche).
Donc:
master
.En cas de doute, utilisez la fusion.
Les seules différences entre un rebase et une fusion sont:
Donc, la réponse courte est de choisir un rebase ou une fusion en fonction de ce à quoi vous voulez que votre historique ressemble .
Il y a quelques facteurs à considérer lors du choix de l'opération à utiliser.
Si oui, ne rebasez pas. Rebase détruit la branche et ces développeurs auront des référentiels cassés / incohérents à moins qu'ils n'utilisent git pull --rebase
. C'est un bon moyen de déranger rapidement d'autres développeurs.
Rebase est une opération destructrice. Cela signifie que si vous ne l'appliquez pas correctement, vous risquez de perdre du travail engagé et / ou de casser la cohérence des référentiels d'autres développeurs.
J'ai travaillé dans des équipes où les développeurs venaient tous d'une époque où les entreprises pouvaient se permettre un personnel dédié pour gérer les succursales et les fusions. Ces développeurs ne connaissent pas grand-chose à Git et ne veulent pas en savoir beaucoup. Dans ces équipes, je ne risquerais pas de recommander le rebasage pour une raison quelconque.
Certaines équipes utilisent le modèle de branche par fonctionnalité où chaque branche représente une fonctionnalité (ou correction de bogue, ou sous-fonctionnalité, etc.) Dans ce modèle, la branche aide à identifier les ensembles de validations associées. Par exemple, on peut rapidement annuler une fonctionnalité en annulant la fusion de cette branche (pour être juste, c'est une opération rare). Ou différenciez une fonctionnalité en comparant deux branches (plus courantes). Rebase détruirait la branche et ce ne serait pas simple.
J'ai également travaillé sur des équipes qui utilisaient le modèle de branche par développeur (nous y sommes tous allés). Dans ce cas, la branche elle-même ne transmet aucune information supplémentaire (le commit a déjà l'auteur). Il n'y aurait aucun mal à rebaser.
La restauration (comme pour l'annulation) d'un rebase est considérablement difficile et / ou impossible (si le rebase avait des conflits) par rapport à l'annulation d'une fusion. Si vous pensez qu'il y a une chance que vous souhaitiez revenir en arrière, utilisez la fusion.
Les opérations de rebase doivent être tirées avec un correspondant git pull --rebase
. Si vous travaillez seul, vous pourrez peut-être vous souvenir de ce que vous devez utiliser au moment opportun. Si vous travaillez en équipe, cela sera très difficile à coordonner. C'est pourquoi la plupart des workflows de rebase recommandent d'utiliser rebase pour toutes les fusions (et git pull --rebase
pour tous les pulls).
En supposant que vous ayez la fusion suivante:
B -- C
/ \
A--------D
Certaines personnes diront que la fusion "détruit" l'historique de validation car si vous ne regardiez que le journal de la branche principale (A - D), vous manqueriez les messages de validation importants contenus dans B et C.
Si c'était vrai, nous n'aurions pas de questions comme celle-ci . Fondamentalement, vous verrez B et C à moins que vous ne demandiez explicitement de ne pas les voir (en utilisant --first-parent). C'est très facile à essayer par vous-même.
Les deux approches fusionnent différemment, mais il n'est pas clair que l'une est toujours meilleure que l'autre et cela peut dépendre du flux de travail du développeur. Par exemple, si un développeur a tendance à s'engager régulièrement (par exemple, il peut s'engager deux fois par jour lors de la transition du travail à la maison), il pourrait y avoir beaucoup de validations pour une branche donnée. Beaucoup de ces validations peuvent ne pas ressembler au produit final (j'ai tendance à refactoriser mon approche une ou deux fois par fonctionnalité). Si quelqu'un d'autre travaillait sur un domaine de code connexe et essayait de rebaser mes modifications, cela pourrait être une opération assez fastidieuse.
Si vous aimez alias rm
pour rm -rf
"gagner du temps", alors peut-être que rebaser est pour vous.
Je pense toujours qu'un jour je tomberai sur un scénario où Git rebase est l'outil génial qui résout le problème. Tout comme je pense que je vais rencontrer un scénario où Git reflog est un outil génial qui résout mon problème. Je travaille avec Git depuis plus de cinq ans maintenant. Ce n'est pas arrivé.
Les histoires désordonnées n'ont jamais vraiment été un problème pour moi. Je ne lis jamais mon histoire de commit comme un roman passionnant. La plupart du temps, j'ai besoin d'une histoire, je vais quand même utiliser Git blame ou Git bissect. Dans ce cas, la validation de la fusion est en fait utile pour moi, car si la fusion a introduit le problème, c'est une information significative pour moi.
Je me sens obligé de mentionner que j'ai personnellement adouci l'utilisation de rebase bien que mes conseils généraux soient toujours valables. J'ai récemment beaucoup interagi avec le projet Angular 2 Material . Ils ont utilisé rebase pour garder un historique de commit très propre. Cela m'a permis de voir très facilement quel commit a corrigé un défaut donné et si ce commit a été inclus ou non dans une version. Il constitue un excellent exemple d'utilisation correcte du rebasage.
De nombreuses réponses indiquent ici que la fusion transforme toutes vos validations en une seule et suggère donc d'utiliser le rebase pour conserver vos validations. Ceci est une erreur. Et une mauvaise idée si vous avez déjà poussé vos commits .
La fusion n'efface pas vos commits. La fusion préserve l'histoire! (il suffit de regarder gitk) Rebase réécrit l'histoire, ce qui est une mauvaise chose après l'avoir poussé .
Utilisez la fusion - ne pas rebaser chaque fois que vous avez déjà poussé.
Voici le point de vue de Linus (auteur de Git) (maintenant hébergé sur mon propre blog, récupéré par la Wayback Machine ). C'est une très bonne lecture.
Ou vous pouvez lire ma propre version de la même idée ci-dessous.
Baser une branche sur le maître:
En revanche, fusionner une branche de sujet en maître:
TLDR: Cela dépend de ce qui est le plus important - une histoire bien rangée ou une vraie représentation de la séquence de développement
Si un historique bien rangé est le plus important, vous devez d'abord rebaser puis fusionner vos modifications, de sorte qu'il est clair exactement quel est le nouveau code. Si vous avez déjà poussé votre branche, ne rebasez pas à moins que vous ne puissiez en supporter les conséquences.
Si la vraie représentation de la séquence est la plus importante, vous fusionneriez sans rebaser.
Fusionner signifie: créer un seul nouveau commit qui fusionne mes modifications dans la destination. Remarque: Ce nouveau commit aura deux parents: le dernier commit de votre chaîne de commit et le dernier commit de l'autre branche que vous fusionnez.
Rebase signifie: Créez une toute nouvelle série de commits, en utilisant mon ensemble actuel de commits comme conseils. En d'autres termes, calculez à quoi auraient ressemblé mes modifications si j'avais commencé à les faire à partir du moment où je rebasais. Par conséquent, après le rebase, vous devrez peut-être tester à nouveau vos modifications et pendant le rebase, vous pourriez avoir quelques conflits.
Compte tenu de cela, pourquoi voudriez-vous rebaser? Juste pour garder l'historique du développement clair. Supposons que vous travaillez sur la fonctionnalité X et que lorsque vous avez terminé, vous fusionnez vos modifications. La destination aura désormais un seul commit qui dirait quelque chose comme "Ajout de la fonctionnalité X". Maintenant, au lieu de fusionner, si vous rebasiez puis fusionniez, l'historique de développement de destination contiendrait toutes les validations individuelles dans une seule progression logique. Cela rend l'examen des modifications plus tard beaucoup plus facile. Imaginez à quel point vous auriez du mal à revoir l'historique de développement si 50 développeurs fusionnaient constamment diverses fonctionnalités.
Cela dit, si vous avez déjà poussé la branche sur laquelle vous travaillez en amont, vous ne devez pas rebaser, mais fusionner à la place. Pour les branches qui n'ont pas été poussées en amont, rebasez, testez et fusionnez.
Une autre fois que vous voudrez peut-être rebaser, c'est quand vous voulez vous débarrasser des commits de votre branche avant de pousser en amont. Par exemple: les validations qui introduisent tôt du code de débogage et les autres validations ultérieures qui nettoient ce code. La seule façon de le faire est d'exécuter un rebase interactif:git rebase -i <branch/commit/tag>
MISE À JOUR: Vous souhaitez également utiliser le rebase lorsque vous utilisez Git pour vous connecter à un système de contrôle de version qui ne prend pas en charge l'historique non linéaire ( Subversion par exemple). Lorsque vous utilisez le pont git-svn, il est très important que les modifications que vous fusionnez à nouveau dans Subversion soient une liste séquentielle de modifications en plus des modifications les plus récentes dans le tronc. Il n'y a que deux façons de le faire: (1) recréer manuellement les modifications et (2) utiliser la commande rebase, ce qui est beaucoup plus rapide.
MISE À JOUR 2: Une autre façon de penser à un rebase est qu'il permet une sorte de mappage de votre style de développement au style accepté dans le référentiel dans lequel vous vous engagez. Disons que vous aimez vous engager en petits morceaux. Vous avez un commit pour corriger une faute de frappe, un commit pour vous débarrasser du code inutilisé et ainsi de suite. Au moment où vous avez terminé ce que vous devez faire, vous avez une longue série de validations. Supposons maintenant que le référentiel dans lequel vous vous engagez encourage les grands commits, donc pour le travail que vous faites, on peut s'attendre à un ou deux commits. Comment prenez-vous votre chaîne de commits et les compressez à ce qui est attendu? Vous utiliseriez un rebase interactif et écraseriez vos minuscules commits en moins de gros morceaux. La même chose est vraie si l'inverse était nécessaire - si votre style était composé de quelques grands commits, mais le référentiel exigeait de longues chaînes de petits commits. Vous utiliseriez également un rebase pour le faire. Si vous aviez fusionné à la place, vous avez maintenant greffé votre style de validation sur le référentiel principal. S'il y a beaucoup de développeurs, vous pouvez imaginer à quel point il serait difficile de suivre une histoire avec plusieurs styles de commit différents après un certain temps.
MISE À JOUR 3: Does one still need to merge after a successful rebase?
Oui, vous le faites. La raison en est qu'un rebase implique essentiellement un "décalage" des commits. Comme je l'ai dit ci-dessus, ces commits sont calculés, mais si vous avez eu 14 commits à partir du point de branchement, alors en supposant que rien ne va mal avec votre rebase, vous aurez 14 commits devant (du point sur lequel vous rebasez) après le rebase est fait. Vous aviez une branche avant un rebase. Vous aurez une branche de la même longueur après. Vous devez toujours fusionner avant de publier vos modifications. En d'autres termes, rebasez autant de fois que vous le souhaitez (encore une fois, seulement si vous n'avez pas poussé vos modifications en amont). Fusionner uniquement après avoir rebasé.
git merge
prend en charge l' --no-ff
option qui l'oblige à effectuer un commit de fusion.
Bien que la fusion soit certainement le moyen le plus simple et le plus courant d'intégrer des modifications, ce n'est pas le seul: la refonte est un autre moyen d'intégration.
Comprendre la fusion un peu mieux
Lorsque Git effectue une fusion, il recherche trois commits:
Avance rapide ou validation de fusion
Dans des cas très simples, l'une des deux branches n'a pas de nouveau commit depuis que la branchement s'est produite - son dernier commit est toujours l'ancêtre commun.
Dans ce cas, effectuer l'intégration est très simple: Git peut simplement ajouter toutes les validations de l'autre branche au-dessus de la validation de l'ancêtre commun. Dans Git, cette forme d'intégration la plus simple est appelée fusion "à avance rapide". Les deux branches partagent alors exactement la même histoire.
Dans de nombreux cas, cependant, les deux branches ont avancé individuellement.
Pour effectuer une intégration, Git devra créer un nouveau commit qui contient les différences entre eux - le commit de fusion.
Human Commits & Merge Commits
Normalement, un commit est soigneusement créé par un être humain. C'est une unité significative qui encapsule uniquement les modifications associées et les annote avec un commentaire.
Un commit de fusion est un peu différent: au lieu d'être créé par un développeur, il est créé automatiquement par Git. Et au lieu d'envelopper un ensemble de modifications connexes, son but est de connecter deux branches, tout comme un nœud. Si vous souhaitez comprendre une opération de fusion ultérieurement, vous devez jeter un œil à l'historique des deux branches et au graphique de validation correspondant.
Intégration avec Rebase
Certaines personnes préfèrent se passer de ces validations de fusion automatiques. Au lieu de cela, ils veulent que l'historique du projet semble évoluer en une seule ligne droite. Rien n'indique qu'il ait été divisé en plusieurs branches à un moment donné.
Passons en revue une opération de rebase étape par étape. Le scénario est le même que dans les exemples précédents: nous voulons intégrer les changements de la branche-B à la branche-A, mais maintenant en utilisant rebase.
Nous le ferons en trois étapes
git rebase branch-A // Synchronises the history with branch-A
git checkout branch-A // Change the current branch to branch-A
git merge branch-B // Merge/take the changes from branch-B to branch-A
Tout d'abord, Git "annulera" toutes les validations sur la branche A qui se sont produites après que les lignes ont commencé à se dériver (après la validation de l'ancêtre commun). Cependant, bien sûr, cela ne les rejettera pas: vous pouvez plutôt penser à ces commits comme étant "sauvés temporairement".
Ensuite, il applique les commits de la branche-B que nous voulons intégrer. À ce stade, les deux branches se ressemblent exactement.
Dans la dernière étape, les nouveaux validations sur la branche-A sont maintenant réappliquées - mais sur une nouvelle position, en plus des validations intégrées de la branche-B (elles sont basées à nouveau).
Le résultat donne l'impression que le développement s'est produit en ligne droite. Au lieu d'une validation de fusion contenant toutes les modifications combinées, la structure de validation d'origine a été conservée.
Enfin, vous obtenez une branche A propre, sans commits indésirables et générés automatiquement.
Remarque: Tiré du post génial de git-tower
. Les inconvénients de rebase
est également une bonne lecture dans le même article.
Avant de fusionner / rebaser:
A <- B <- C [master]
^
\
D <- E [branch]
Après git merge master
:
A <- B <- C
^ ^
\ \
D <- E <- F
Après git rebase master
:
A <- B <- C <- D' <- E'
(A, B, C, D, E et F sont des commits)
Cet exemple et des informations beaucoup plus bien illustrées sur Git se trouvent dans Git The Basics Tutorial .
Cette phrase comprend:
En général, la façon de tirer le meilleur parti des deux mondes est de rebaser les modifications locales que vous avez apportées, mais que vous n'avez pas encore partagées, avant de les pousser afin de nettoyer votre histoire, mais de ne jamais rebaser tout ce que vous avez poussé quelque part .
Cette réponse est largement orientée autour de Git Flow . Les tables ont été générées avec le joli générateur de table ASCII , et les arbres d'historique avec cette merveilleuse commande ( alias as git lg
):
git log --graph --abbrev-commit --decorate --date=format:'%Y-%m-%d %H:%M:%S' --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%ad%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'
Les tableaux sont classés par ordre chronologique inverse pour être plus cohérents avec les arbres historiques. Voyez également la différence entre git merge
et git merge --no-ff
first (vous voulez généralement l'utiliser git merge --no-ff
car cela rend votre histoire plus proche de la réalité):
git merge
Commandes:
Time Branch "develop" Branch "features/foo"
------- ------------------------------ -------------------------------
15:04 git merge features/foo
15:03 git commit -m "Third commit"
15:02 git commit -m "Second commit"
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Résultat:
* 142a74a - YYYY-MM-DD 15:03:00 (XX minutes ago) (HEAD -> develop, features/foo)
| Third commit - Christophe
* 00d848c - YYYY-MM-DD 15:02:00 (XX minutes ago)
| Second commit - Christophe
* 298e9c5 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git merge --no-ff
Commandes:
Time Branch "develop" Branch "features/foo"
------- -------------------------------- -------------------------------
15:04 git merge --no-ff features/foo
15:03 git commit -m "Third commit"
15:02 git commit -m "Second commit"
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Résultat:
* 1140d8c - YYYY-MM-DD 15:04:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/foo' - Christophe
| * 69f4a7a - YYYY-MM-DD 15:03:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * 2973183 - YYYY-MM-DD 15:02:00 (XX minutes ago)
|/ Second commit - Christophe
* c173472 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git merge
contre git rebase
Premier point: toujours fusionner les fonctionnalités en développement, ne jamais rebaser le développement à partir des fonctionnalités . Ceci est une conséquence de la règle d'or de la refondation :
La règle d'or
git rebase
est de ne jamais l'utiliser sur les branches publiques .
Ne jamais rebaser quelque chose que vous avez poussé quelque part.
J'ajouterais personnellement: à moins qu'il ne s'agisse d'une branche de fonctionnalités ET que vous et votre équipe soyez conscients des conséquences .
La question de git merge
vs ne git rebase
s'applique donc presque qu'aux branches de fonctionnalité (dans les exemples suivants, --no-ff
a toujours été utilisée lors de la fusion). Notez que comme je ne suis pas sûr qu'il y ait une meilleure solution ( un débat existe ), je ne fournirai que le comportement des deux commandes. Dans mon cas, je préfère utiliser git rebase
car cela produit un arbre d'histoire plus agréable :)
git merge
Commandes:
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- --------------------------------
15:10 git merge --no-ff features/bar
15:09 git merge --no-ff features/foo
15:08 git commit -m "Sixth commit"
15:07 git merge --no-ff features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Résultat:
* c0a3b89 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 37e933e - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * eb5e657 - YYYY-MM-DD 15:07:00 (XX minutes ago)
| |\ Merge branch 'features/foo' into features/bar - Christophe
| * | 2e4086f - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | | Fifth commit - Christophe
| * | 31e3a60 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | | Fourth commit - Christophe
* | | 98b439f - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ \ Merge branch 'features/foo' - Christophe
| |/ /
|/| /
| |/
| * 6579c9c - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * 3f41d96 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* 14edc68 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git rebase
Commandes:
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10 git merge --no-ff features/bar
15:09 git merge --no-ff features/foo
15:08 git commit -m "Sixth commit"
15:07 git rebase features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Résultat:
* 7a99663 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 708347a - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * 949ae73 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * 108b4c7 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | Fourth commit - Christophe
* | 189de99 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ Merge branch 'features/foo' - Christophe
| |/
| * 26835a0 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * a61dd08 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* ae6f5fc - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
develop
à une branche de fonctionnalitégit merge
Commandes:
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10 git merge --no-ff features/bar
15:09 git commit -m "Sixth commit"
15:08 git merge --no-ff develop
15:07 git merge --no-ff features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Résultat:
* 9e6311a - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 3ce9128 - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * d0cd244 - YYYY-MM-DD 15:08:00 (XX minutes ago)
| |\ Merge branch 'develop' into features/bar - Christophe
| |/
|/|
* | 5bd5f70 - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ \ Merge branch 'features/foo' - Christophe
| * | 4ef3853 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | | Third commit - Christophe
| * | 3227253 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ / Second commit - Christophe
| * b5543a2 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * 5e84b79 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/ Fourth commit - Christophe
* 2da6d8d - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git rebase
Commandes:
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10 git merge --no-ff features/bar
15:09 git commit -m "Sixth commit"
15:08 git rebase develop
15:07 git merge --no-ff features/foo
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Résultat:
* b0f6752 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 621ad5b - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * 9cb1a16 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * b8ddd19 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/ Fourth commit - Christophe
* 856433e - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ Merge branch 'features/foo' - Christophe
| * 694ac81 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * 5fd94d3 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* d01d589 - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git cherry-pick
Lorsque vous n'avez besoin que d'un commit spécifique, git cherry-pick
c'est une bonne solution (l' -x
option ajoute une ligne qui dit " (choisi à partir de commit ...) " dans le corps du message de commit d'origine, donc c'est généralement une bonne idée de l'utiliser - git log <commit_sha1>
pour voir il):
Commandes:
Time Branch "develop" Branch "features/foo" Branch "features/bar"
------- -------------------------------- ------------------------------- -----------------------------------------
15:10 git merge --no-ff features/bar
15:09 git merge --no-ff features/foo
15:08 git commit -m "Sixth commit"
15:07 git cherry-pick -x <second_commit_sha1>
15:06 git commit -m "Fifth commit"
15:05 git commit -m "Fourth commit"
15:04 git commit -m "Third commit"
15:03 git commit -m "Second commit"
15:02 git checkout -b features/bar
15:01 git checkout -b features/foo
15:00 git commit -m "First commit"
Résultat:
* 50839cd - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\ Merge branch 'features/bar' - Christophe
| * 0cda99f - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| | Sixth commit - Christophe
| * f7d6c47 - YYYY-MM-DD 15:03:00 (XX minutes ago)
| | Second commit - Christophe
| * dd7d05a - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | Fifth commit - Christophe
| * d0d759b - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | Fourth commit - Christophe
* | 1a397c5 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ Merge branch 'features/foo' - Christophe
| |/
|/|
| * 0600a72 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | Third commit - Christophe
| * f4c127a - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ Second commit - Christophe
* 0cf894c - YYYY-MM-DD 15:00:00 (XX minutes ago)
First commit - Christophe
git pull --rebase
Je ne suis pas sûr de pouvoir l'expliquer mieux que Derek Gourlay ... Fondamentalement, utilisez git pull --rebase
plutôt que git pull
:) Ce qui manque cependant dans l'article, c'est que vous pouvez l'activer par défaut :
git config --global pull.rebase true
git rerere
Encore une fois, bien expliqué ici . Mais en termes simples, si vous l'activez, vous n'aurez plus à résoudre le même conflit plusieurs fois.
Le livre Pro Git a une très bonne explication sur la page de rebasage .
Fondamentalement, une fusion prendra deux commits et les combinera.
Un rebase ira à l'ancêtre commun sur les deux et appliquera progressivement les modifications les unes sur les autres. Cela donne une histoire «plus propre» et plus linéaire.
Mais lorsque vous rebase, vous abandonnez les validations précédentes et en créez de nouvelles. Vous ne devez donc jamais rebaser un référentiel public. Les autres personnes travaillant sur le référentiel vous détesteront.
Pour cette seule raison, je fusionne presque exclusivement. 99% du temps, mes branches ne diffèrent pas beaucoup, donc s'il y a des conflits, ce n'est qu'à un ou deux endroits.
Git rebase est utilisé pour rendre les chemins de branchement dans l'historique plus propres et la structure du référentiel linéaire.
Il est également utilisé pour garder les branches créées par vous privées, car après avoir rebasé et poussé les modifications sur le serveur, si vous supprimez votre branche, il n'y aura aucune preuve de branche sur laquelle vous avez travaillé. Votre succursale est donc votre préoccupation locale.
Après avoir fait le rebase, nous nous débarrassons également d'un commit supplémentaire que nous avons utilisé pour voir si nous faisons une fusion normale.
Et oui, il faut toujours fusionner après un rebase réussi car la commande rebase place simplement votre travail au-dessus de la branche que vous avez mentionnée lors du rebase, par exemple master, et effectue le premier commit de votre branche en tant que descendant direct de la branche master . Cela signifie que nous pouvons maintenant effectuer une fusion rapide pour apporter des modifications de cette branche à la branche principale.
Quelques exemples pratiques, quelque peu liés au développement à grande échelle où Gerrit est utilisé pour la révision et l'intégration de la livraison:
Je fusionne lorsque j'élève ma branche de fonctionnalité vers un nouveau maître distant. Cela donne un travail d'élévation minimal et il est facile de suivre l'historique du développement des fonctionnalités dans par exemple gitk .
git fetch
git checkout origin/my_feature
git merge origin/master
git commit
git push origin HEAD:refs/for/my_feature
Je fusionne lorsque je prépare un commit de livraison.
git fetch
git checkout origin/master
git merge --squash origin/my_feature
git commit
git push origin HEAD:refs/for/master
Je rebase lorsque ma validation de livraison échoue à l'intégration pour une raison quelconque, et je dois la mettre à jour vers un nouveau maître distant.
git fetch
git fetch <gerrit link>
git checkout FETCH_HEAD
git rebase origin/master
git push origin HEAD:refs/for/master
Il a été expliqué à plusieurs reprises ce qu'est le rebase et ce qu'est la fusion, mais quand devez-vous utiliser quoi?
Quand faut-il utiliser rebase?
Au fur et à mesure que Git rebase change l'historique. Par conséquent, vous ne devez pas l'utiliser lorsque quelqu'un d'autre travaille sur la même branche / si vous l'avez poussé. Mais si vous avez une branche locale, vous pouvez faire un fusionner le master de rebase avant de fusionner votre branche en master pour garder un historique plus propre. Ce faisant, après avoir fusionné dans la branche principale, il ne sera pas visible que vous avez utilisé une branche dans la branche principale - l'historique est "plus propre" car vous n'avez pas "fusionné .." généré automatiquement, mais vous avez toujours le historique complet dans votre branche principale sans avoir généré automatiquement des validations "fusionnées ..".
Assurez-vous cependant que vous utilisez git merge feature-branch --ff-only
pour vous assurer qu'il n'y a pas de conflits lors de la création d'un seul commit lorsque vous fusionnez votre fonctionnalité à nouveau. Ceci est intéressant si vous utilisez des branches de fonctionnalités pour chaque tâche sur laquelle vous travaillez, car vous obtenez l'historique de la branche de fonctionnalités, mais pas un commit "fusionné .."
Un deuxième scénario serait, si vous vous êtes dérivé d'une branche et que vous voulez savoir ce qui a changé dans la branche principale. Rebase vous donne les informations car elle inclut chaque commit.
Quand devriez-vous utiliser la fusion?
Lorsque vous n'avez pas besoin ou ne voulez pas avoir tout l'historique d'une branche de fonctionnalité dans votre branche principale ou si d'autres travaillent sur la même branche / vous l'avez poussée. Si vous souhaitez toujours conserver l'historique, fusionnez simplement le masque dans la branche de fonctionnalité avant de fusionner la branche de fonctionnalité dans le master. Cela se traduira par une fusion rapide où vous avez l'historique de la branche de fonctionnalité dans votre maître (y compris la validation de fusion qui était dans votre branche de fonctionnalité parce que vous avez fusionné le maître dans celle-ci).
Quand dois-je l'utiliser git rebase
? Presque jamais, car il réécrit l'histoire. git merge
est presque toujours le choix préférable, car il respecte ce qui s'est réellement passé dans votre projet.