web-dev-qa-db-fra.com

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

Venant de Mercurial, j'utilise des branches pour organiser les fonctionnalités. Naturellement, je souhaite également voir ce travail se dérouler dans mon histoire.

J'ai commencé mon nouveau projet en utilisant git et fini mon premier long métrage. Lors de la fusion de la fonctionnalité, j’ai réalisé que git utilisait l’avance rapide, c’est-à-dire qu’il applique mes modifications directement à la branche principale si possible et qu'il 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 aboutira à une branche maîtresse géante. Personne ne sait que j'ai utilisé une branche distincte pour chaque fonction, car je n'aurai finalement que cette branche maîtresse géante. Cela n'aura-t-il pas l'air peu 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 qu'il y a de si bien?

630
Florian Pilz

La fusion rapide est logique pour les branches éphémères, mais dans un plus historique complexe , une fusion non rapide peut rendre l'historique plus facile à comprendre et faciliter l'annulation d'un groupe de commits. .

Warning : Le non-transfert rapide a également des effets secondaires potentiels. Veuillez examiner https://sandofsky.com/blog/git-workflow.html , évitez le 'no-ff' avec son "checkpoint commits" qui divise ou blâme, et examinez avec soin si cela devrait être votre approche par défaut pour master.

alt text
(De nvie.com , Vincent Driessen , post " Un modèle de branchement Git réussi " )

Incorporant une fonctionnalité terminée sur develop

Les fonctionnalités terminées peuvent être fusionnées dans la branche 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'indicateur _--no-ff_ 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 de regrouper tous les commits qui ont ajouté la fonctionnalité.

Jakub Narębski aussi mentions le config merge.ff :

Par défaut, Git ne crée pas de validation de fusion supplémentaire lors de la fusion d'une validation qui est un descendant de la validation actuelle. Au lieu de cela, la pointe de la branche actuelle est transférée rapidement.
Lorsqu'elle est définie sur false, cette variable indique à Git de créer une validation de fusion supplémentaire dans un tel cas (équivalent à l'option _--no-ff_ à partir de la ligne de commande).
Lorsqu'il est défini sur 'only', seules les fusions rapides sont autorisées (ce qui revient à donner l'option _--ff-only_ à partir de la ligne de commande).


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

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

Mais si vous anticipez un flux de travail itératif sur une branche de sujet/fonctionnalité (c’est-à-dire que je fusionne, puis je reviens sur cette branche de fonctionnalité et ajoute quelques modifications supplémentaires), il est utile d’inclure uniquement la fusion dans la branche principale, plutôt que tous les commits intermédiaires de la branche de fonctionnalité.

Dans ce cas, vous pouvez configurer 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 sens en avance rapide pour les branches [de courte durée], mais en faire 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 branches varie énormément d'un utilisateur à l'autre. Parmi les utilisateurs expérimentés, cependant, il y a probablement une tendance à avoir des branches beaucoup plus courtes.

Pour moi, , une branche éphémère est celle que je crée afin de faciliter une certaine opération (rebaser, probable ou mettre à jour rapidement), puis immédiatement supprimer une fois J'ai fini.
Cela signifie probablement devrait être intégré à la branche de sujet à partir de laquelle il a créé , et 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 commits mettant en œuvre cette fonctionnalité donnée.

Plus généralement, j'ajoute:

cela dépend vraiment de votre workflow de développement :

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

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

En fait, lorsque vous considérez le modèle de branche Mercurial, il se base essentiellement sur ne [=] branche par référentiel (même si vous pouvez créer têtes anonymes, signets et même nommés). branches )
Voir "Git et Mercurial - Comparaison et contraste" .

Mercurial utilise par défaut des lignes de code anonymes et légères, appelées dans leur terminologie "têtes".
Git utilise des branches nommées légères, avec un mappage par injection pour mapper les noms des branches du référentiel distant aux noms des branches de suivi à distance.
Git vous "oblige" à nommer les branches (enfin, à l’exception d’une branche unique non nommée, qui est une situation appelée " HEAD détaché "), mais je pense que cela fonctionne mieux. avec des flux de travail lourds en branches, tels que le flux de travail de branche de sujet, ce qui signifie plusieurs branches dans un même paradigme de référentiel.

709
VonC

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


Premièrement, si je me souviens bien, le fait que Git par défaut ne crée pas fusions validées dans le cas fast-forward est dû à la prise en compte de "référentiels égaux à une branche" ", où une extraction mutuelle est utilisée pour synchroniser ces deux référentiels (un flux de travail que vous pouvez trouver comme premier exemple dans la documentation de la plupart des utilisateurs, y compris" The Git User's Manual "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 le même travail. 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ée pour le futur.

Notez que l'utilité des branches de fonctionnalités et de la présence de plusieurs branches dans un même référentiel n'est apparue que plus tard, avec une utilisation plus répandue de VCS avec une bonne prise en charge de la fusion et avec différents flux de travail basés sur la fusion. C’est pourquoi, par exemple, Mercurial ne prenait initialement en charge qu’une branche par référentiel (ainsi que des astuces anonymes pour le suivi des branches distantes), comme le montrent les anciennes versions de "Mercurial: Le Guide définitif".


Deuxièmement, lorsque vous suivez les meilleures pratiques consistant à utiliser des branches de fonctionnalité , à savoir cette fonctionnalité les branches doivent toutes commencer à partir de la 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 à fusionner -forward situation ... ce qui rend ce problème sans objet. Vous devez vous préoccuper 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 n'imposez pas les modifications à validation unique directement sur 'maître'); toutes les autres fusions ultérieures ne sont bien sûr pas dans une situation d'avance rapide.

HTH

41
Jakub Narębski