Question Pourquoi git fast-forward fusionne-t-il par défaut?


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

J'ai commencé mon nouveau projet en utilisant git et j'ai fini 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'elle applique mes modifications directement à la branche master 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 aboutirait à une branche maîtresse géante. Personne ne sait que j'ai utilisé une branche distincte pour chaque fonctionnalité, parce qu'à la fin je n'aurai que cette branche maîtresse géante. Est-ce que ça ne va pas être 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'y a-t-il de si bon?


608
2018-05-17 15:26


origine


Réponses:


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

Attention: La transmission sans avance rapide a également des effets secondaires potentiels. S'il-vous-plaît évaluez https://sandofsky.com/blog/git-workflow.html, évitez le "no-ff" avec ses "validations de points de contrôle" qui rompent la coupure ou le blâme, et réfléchissez soigneusement à la question de savoir si cela devrait être votre approche par défaut pour master.

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

Incorporer une fonctionnalité finie sur développer

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

le --no-ff flag provoque la fusion pour toujours créer un nouvel objet commit, 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 aussi mentions la config merge.ff:

Par défaut, Git ne crée pas de validation de fusion supplémentaire lors de la fusion d'un commit qui est un descendant de la validation en cours. Au lieu de cela, la pointe de la branche actuelle est avancée rapidement.
  Lorsque réglé sur false, cette variable indique à Git de créer un commit de fusion supplémentaire dans un tel cas (équivalent à donner le --no-ff option depuis la ligne de commande).
  Lorsque réglé sur 'only", seules les fusions rapides sont autorisées (ce qui équivaut à --ff-only option depuis la ligne de commande).


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

  • 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 dans cette branche
  • ces commits font en réalité partie de la branche principale: une fois réorganisée, la branche principale est rapidement redirigée pour les inclure.

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

Dans ce cas, vous pouvez terminer le réglage ce genre 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

L'OP ajoute dans les commentaires:

Je vois un peu de sens dans l'avance rapide pour les branches [éphémères], mais en faire l'action par défaut signifie que git vous suppose ... ont souvent des branches [éphémères]. 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 succursales de courte durée.

Pour moi, une branche éphémère est celle que je crée pour faciliter une certaine opération (rebasage, probable, ou correctifs rapides et tests), puis supprimez immédiatement une fois que j'ai terminé.
  Cela signifie probablement devrait être absorbé dans la branche de sujet, il fourchu à partir deet 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 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 vous branchez?"

En fait, quand vous considérez le modèle de la branche Mercurial, il est à la base un branche par référentiel (même si vous pouvez créer têtes anonymes, signets et même branches nommées)
Voir "Git et Mercurial - Comparer et contraster".

Mercurial, par défaut, utilise des codages légers anonymes, qui dans sa terminologie sont appelés "têtes".
  Git utilise des branches nommées légères, avec un mappage injectif pour mapper les noms des branches dans le 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 "TETE détachée"), mais je pense que cela fonctionne mieux avec des flux de travail lourds tels que le flux de travail de branche de sujet, ce qui signifie plusieurs branches dans un seul paradigme de référentiel.


685
2018-05-17 15:31



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


Tout d'abord, si je me souviens bien, le fait que Git par défaut ne crée pas fusionner commet dans le avance rapide l'affaire est venue de l'examen des "dépôts égaux" à succursales traction mutuelle est utilisée pour synchroniser ces deux référentiels (un workflow que vous pouvez trouver comme premier exemple dans la plupart des documentations de l'utilisateur, y compris "The Git User's Manual" et "Version Control by Example"). Dans ce cas, vous n'utilisez pas le pull pour fusionner la 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 quand vous faites une synchronisation sauvegardée et stockée dans le dépôt, sauvegardée pour le futur.

Notez que l'utilité des branches de fonctionnalité et d'avoir plusieurs branches dans le référentiel unique n'est venue que plus tard, avec une utilisation plus répandue de VCS avec un bon support de fusion, et avec l'essai de plusieurs flux de travail basés sur la fusion. C'est pourquoi par exemple, Mercurial ne supportait à l'origine qu'une seule branche par référentiel (plus des astuces anonymes pour le suivi des branches distantes), comme on le voit dans les révisions plus anciennes de "Mercurial: The Definitive Guide".


Deuxièmement, en suivant les meilleures pratiques d'utilisation branches de fonctionnalité, à savoir que les branches de fonctionnalité doivent toutes partir d'une version stable (généralement de la dernière version), pour pouvoir choisir et sélectionner les fonctionnalités à inclure en sélectionnant les branches à fusionner, vous n'êtes généralement pas en situation d'avance rapide... ce qui rend ce problème discutable. Vous devez vous soucier de créer une véritable fusion et non d'avancer rapidement lors de la fusion d'une toute première branche (en supposant que vous ne mettiez pas directement les modifications de commit sur 'master'); toutes les autres fusions ultérieures sont bien entendu en situation non-rapide.

HTH


40
2018-05-31 13:30