Pourquoi git effectue-t-il des fusions d'avance rapide par défaut?

641

Issu de mercurial, j'utilise des branches pour organiser les fonctionnalités. Naturellement, je veux également voir ce flux de travail dans mon histoire.

J'ai commencé mon nouveau projet en utilisant git et terminé ma première fonctionnalité. Lors de la fusion de la fonctionnalité, j'ai réalisé que git utilise l'avance rapide, c'est-à-dire qu'il applique mes modifications directement à la branche principale si possible et oublie ma branche.

Donc, penser à l'avenir: je suis le seul à travailler sur ce projet. Si j'utilise l'approche par défaut de git (fusion rapide), mon historique se traduirait par une branche maître géante. Personne ne sait que j'ai utilisé une branche distincte pour chaque fonctionnalité, car au final je n'aurai que cette branche master géante. Cela ne vous semblera-t-il pas professionnel?

Par ce raisonnement, je ne veux pas de fusion rapide et je ne vois pas pourquoi c'est la valeur par défaut. Qu'est-ce qui est si bon?

Florian Pilz
la source
38
Remarque: voir également sandofsky.com/blog/git-workflow.html , et évitez le ' no-ff' avec ses "checkpoint commits" qui rompent la bissecte ou le blâme.
VonC
15
regrettez-vous d'utiliser git sur un projet d'un seul homme?
HaveAGuess
27
Absolument pas! Dans mon dossier de travail, j'ai 7 projets individuels où j'utilise git. Permettez-moi de reformuler cela: j'ai commencé de nombreux projets depuis que j'ai posé cette question et ils sont tous versionnés via git. Pour autant que je sache, seuls git et mercurial supportent le versioning local, ce qui est essentiel pour moi depuis que je m'y suis habitué. Il est facile à configurer et vous avez toujours toute l'histoire avec vous. Dans les projets de groupe, c'est encore mieux, car vous pouvez vous engager sans interférer avec votre code de travail en cours. De plus, j'utilise github pour partager certains de mes projets (par exemple micro-optparse) où git est une exigence.
Florian Pilz
7
@Cawas true, -no-ffest rarement une bonne idée, mais peut toujours aider à conserver un historique interne aux fonctionnalités tout en enregistrant un seul commit sur la branche principale. Cela a du sens pour un long historique de fonctionnalités, lorsque vous fusionnez de temps en temps sa progression sur la branche principale.
VonC
12
Soit dit en passant, à votre question de "Est-ce que [une histoire de branche linéaire] ne semble pas professionnelle?". Il n'y a rien de non professionnel à utiliser un système de code source avec ses valeurs par défaut. Ce n'est pas une question de professionnalisme. Il s'agit de déterminer quelle philosophie de branchement vous vous abonnez. Par exemple, @VonC est lié à l'article de sandofsky où il défend l'utilisation de l'avance rapide comme approche supérieure. Pas bien ou mal, juste différentes philosophies pour différents contextes.
Marplesoft

Réponses:

718

La fusion à avance rapide a du sens pour les branches de courte durée, mais dans une histoire plus complexe , la fusion à avance rapide peut rendre l'historique plus facile à comprendre et faciliter la restauration d'un groupe de validations.

Avertissement : L' avance non rapide a également des effets secondaires potentiels. Veuillez consulter https://sandofsky.com/blog/git-workflow.html , éviter le 'no-ff' avec ses "checkpoint commits" qui brisent la bissecte ou le blâme, et examinez attentivement si ce devrait être votre approche par défaut master.

texte alternatif
(De nvie.com , Vincent Driessen , poste " Un modèle de branchement Git réussi ")

Intégration d'une fonctionnalité finie lors du développement

Les fonctionnalités terminées peuvent être fusionnées dans la branche de développement pour les ajouter à la prochaine version:

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff myfeature
Updating ea1b82a..05e9557
(Summary of changes)
$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).
$ git push origin develop

L' --no-ffindicateur fait que la fusion crée toujours un nouvel objet de validation, même si la fusion peut être effectuée avec une avance rapide. Cela évite de perdre des informations sur l'existence historique d'une branche de fonctionnalité et regroupe toutes les validations qui ont ajouté la fonctionnalité ensemble.

Jakub Narębski mentionne également la configmerge.ff :

Par défaut, Git ne crée pas de commit de fusion supplémentaire lors de la fusion d'un commit descendant du commit actuel. Au lieu de cela, la pointe de la branche actuelle est avancée rapidement.
Lorsqu'elle est définie sur false, cette variable indique à Git de créer un commit de fusion supplémentaire dans un tel cas (équivalent à donner l' --no-ffoption depuis la ligne de commande).
Lorsqu'il est défini sur ' only', seules ces fusions à avance rapide sont autorisées (équivalent à donner l' --ff-onlyoption à partir de la ligne de commande).


L'avance rapide est la valeur par défaut car:

  • les branches de courte durée sont très faciles à créer et à utiliser dans Git
  • les branches de courte durée isolent souvent de nombreux commits qui peuvent être réorganisés librement au sein de cette branche
  • ces commits font en fait partie de la branche principale: une fois réorganisée, la branche principale est rapidement avancée pour les inclure.

Mais si vous prévoyez un flux de travail itératif sur une branche de sujet / fonctionnalité (c'est-à-dire que je fusionne, je reviens à cette branche de fonctionnalité et ajoute d'autres validations), il est utile d'inclure uniquement la fusion dans la branche principale, plutôt que toutes les validations intermédiaires de la branche de fonctionnalité.

Dans ce cas, vous pouvez finir par définir ce type de fichier de configuration :

[branch "master"]
# This is the list of cmdline options that should be added to git-merge 
# when I merge commits into the master branch.

# The option --no-commit instructs git not to commit the merge
# by default. This allows me to do some final adjustment to the commit log
# message before it gets commited. I often use this to add extra info to
# the merge message or rewrite my local branch names in the commit message
# to branch names that are more understandable to the casual reader of the git log.

# Option --no-ff instructs git to always record a merge commit, even if
# the branch being merged into can be fast-forwarded. This is often the
# case when you create a short-lived topic branch which tracks master, do
# some changes on the topic branch and then merge the changes into the
# master which remained unchanged while you were doing your work on the
# topic branch. In this case the master branch can be fast-forwarded (that
# is the tip of the master branch can be updated to point to the tip of
# the topic branch) and this is what git does by default. With --no-ff
# option set, git creates a real merge commit which records the fact that
# another branch was merged. I find this easier to understand and read in
# the log.

mergeoptions = --no-commit --no-ff

Le PO ajoute dans les commentaires:

Je vois un certain sens dans l'avance rapide pour les branches [de courte durée], mais en faisant l'action par défaut signifie que git suppose que vous ... avez souvent des branches [de courte durée]. Raisonnable?

Jefromi répond:

Je pense que la durée de vie des succursales varie considérablement d'un utilisateur à l'autre. Cependant, parmi les utilisateurs expérimentés, il y a probablement une tendance à avoir beaucoup plus de branches de courte durée.

Pour moi, une branche de courte durée est une branche que je crée afin de faciliter une certaine opération (rebasage, correctif probable ou rapide et test), puis supprimée immédiatement une fois que j'ai terminé.
Cela signifie qu'il devrait probablement être absorbé dans la branche de sujet à partir de laquelle il est issu et que la branche de sujet sera fusionnée en une seule branche. Personne n'a besoin de savoir ce que j'ai fait en interne pour créer la série de validations implémentant cette fonctionnalité donnée.

Plus généralement, j'ajoute:

cela dépend vraiment de votre flux de travail de développement :

  • s'il est linéaire, une branche a du sens.
  • Si vous avez besoin d'isoler des fonctionnalités et de les travailler pendant une longue période et de les fusionner à plusieurs reprises, plusieurs branches ont du sens.

Voir " Quand devriez-vous créer une succursale? "

En fait, lorsque vous considérez le modèle de branche Mercurial, il s'agit essentiellement d' une branche par référentiel (même si vous pouvez créer des têtes anonymes, des signets et même des branches nommées )
Voir "Git et Mercurial - Comparer et contraster" .

Mercurial, par défaut, utilise des codelines légères anonymes, qui dans leur terminologie sont appelées "têtes".
Git utilise des branches nommées légères, avec un mappage injectif pour mapper les noms des branches du référentiel distant aux noms des branches de suivi à distance.
Git vous "force" à nommer des branches (enfin, à l'exception d'une seule branche sans nom, qui est une situation appelée " tête détachée "), mais je pense que cela fonctionne mieux avec des flux de travail lourds comme une branche de sujet, ce qui signifie plusieurs branches dans un seul paradigme de référentiel.

VonC
la source
Wow, c'était rapide. ;) Je connais l'option --no-ff, mais je ne découvre l'avance rapide qu'après avoir foiré ma fonctionnalité. Je vois un certain sens dans l'avance rapide pour les branches vivantes courtes, mais ce qui en fait l'action par défaut signifie que git suppose que vous avez le plus souvent de telles branches vivantes courtes. Raisonnable?
Florian Pilz
@Florian: Je pense que c'est une vue raisonnable du processus. J'ai ajouté un exemple de configuration qui configurera la façon dont vous souhaitez gérer la fusion vers le master.
VonC
Merci, le fichier de configuration devrait aider à éviter de tels accrochages. :) Seulement appliqué cette configuration localement avec "git config branchement.master.mergeoptions '--no-ff'"
Florian Pilz
2
@BehrangSaeedzadeh: le rebasage est un autre sujet (qui n'a pas été mentionné dans cette page): tant que vous n'avez pas poussé votre featurebranche vers un référentiel public, vous pouvez la rebaser au-dessus du masternombre de fois que vous le souhaitez. Voir stackoverflow.com/questions/5250817/…
VonC
4
@BehrangSaeedzadeh: un rebase en lui-même ne rendra pas l'histoire linéaire. C'est ainsi que vous intégrez les changements de votrefeature branche masterqui rend ladite histoire linéaire ou non. Une simple fusion rapide va le rendre linéaire. Ce qui est logique si vous avez nettoyé l'historique de cette featurebranche avant la fusion d'avance rapide, ne laissant que des validations importantes, comme mentionné dans stackoverflow.com/questions/7425541/… .
VonC
42

Permettez - moi d'un peu sur un VonC de réponse très complète :


Premièrement, si je me souviens bien, le fait que Git par défaut ne crée pas de commit de fusion dans le cas d' avance rapide est venu de la considération des "référentiels égaux" à une seule branche, où une traction mutuelle est utilisée pour synchroniser ces deux référentiels (un workflow que vous pouvez trouver comme premier exemple dans la plupart de la documentation utilisateur, y compris "Le manuel de l'utilisateur Git" et "Contrôle de version par exemple"). Dans ce cas, vous n'utilisez pas pull pour fusionner une branche entièrement réalisée, vous l'utilisez pour suivre d'autres travaux. Vous ne voulez pas avoir un fait éphémère et sans importance lorsque vous effectuez une synchronisation enregistrée et stockée dans un référentiel, enregistré pour l'avenir.

Notez que l'utilité des branches de fonctionnalités et d'avoir plusieurs branches dans un référentiel unique n'est venue que plus tard, avec une utilisation plus répandue de VCS avec une bonne prise en charge de la fusion et avec l'essai de divers workflows basés sur la fusion. C'est pourquoi, par exemple, Mercurial ne supportait à l'origine qu'une seule branche par référentiel (ainsi que des conseils anonymes pour le suivi des branches distantes), comme le montrent les anciennes versions de "Mercurial: The Definitive Guide".


Deuxièmement, lorsque vous suivez les meilleures pratiques d'utilisation des branches de fonctionnalités , à savoir que les branches de fonctionnalités doivent toutes commencer à partir d'une version stable (généralement de la dernière version), pour pouvoir sélectionner et sélectionner les fonctionnalités à inclure en sélectionnant les branches de fonctionnalités à fusionner, vous ne sont généralement pas en situation d'avance rapide ... ce qui rend ce problème théorique. Vous devez vous soucier de la création d'une véritable fusion et non d'une avance rapide lors de la fusion d'une toute première branche (en supposant que vous ne placez pas les modifications à validation unique directement sur 'master'); toutes les autres fusions ultérieures sont bien entendu en situation de non-avance rapide.

HTH

Jakub Narębski
la source
En ce qui concerne les branches de fonctionnalités des versions stables: que se passe-t-il si une fonctionnalité que je développe pour la prochaine version dépend des modifications apportées à une autre fonctionnalité que j'ai déjà développée et fusionnée dans master? Cela signifie-t-il que je dois créer cette deuxième branche de fonctionnalité à partir du maître?
dOxxx
1
@dOxxx: Oui, il y a des exceptions, comme par exemple lorsqu'une branche s'appuie sur l'autre (soit directement, soit après avoir fusionné le précédent dans le maître).
Jakub Narębski