Quelle est la différence entre `git merge` et` git merge --no-ff`?

Réponses:

1080

L' --no-ffindicateur empêche git merged'exécuter une "avance rapide" s'il détecte que votre courant HEADest un ancêtre de la validation que vous essayez de fusionner. Une avance rapide est lorsque, au lieu de construire une validation de fusion, git déplace simplement votre pointeur de branche pour pointer vers la validation entrante. Cela se produit généralement lorsque vous effectuez un git pullsans aucun changement local.

Cependant, vous souhaitez parfois empêcher ce problème de se produire, généralement parce que vous souhaitez conserver une topologie de branche spécifique (par exemple, vous fusionnez dans une branche de rubrique et vous voulez vous assurer qu'elle ressemble à cela lors de la lecture de l'historique). Pour ce faire, vous pouvez passer le --no-ffdrapeau et git mergesera toujours construire une fusion au lieu de l' avance rapide.

De même, si vous souhaitez exécuter un git pullou utiliser git mergeafin d'avancer explicitement rapidement, et que vous souhaitez renflouer s'il ne peut pas avancer rapidement, vous pouvez utiliser l' --ff-onlyindicateur. De cette façon, vous pouvez régulièrement faire quelque chose comme git pull --ff-onlysans réfléchir, puis en cas d'erreur, vous pouvez revenir en arrière et décider si vous souhaitez fusionner ou rebaser.

Lily Ballard
la source
87
Pour répondre plus directement à la question de l'OP: ils ne sont pas toujours différents, mais s'ils le sont, il est clair gitkou git log --graphque la fusion à avance rapide n'a pas créé de validation de fusion, tandis que celle à avance rapide ne l'a pas fait.
Cascabel
11
Il serait intéressant de développer la raison pour laquelle ff est évité: l'auteur a mentionné que "topologie de branche spécifique" signifie qu'en cas --no-ff, un commit de fusion supplémentaire sert de marqueur de la fusion. L'avantage est un marqueur de fusion explicite avec les noms de l'auteur et de la fusion. Le contre est une histoire non linéaire qui ressemble à un ensemble de voies ferrées convergentes. Un effet secondaire psychologique possible de la fusion est que les contributeurs perdent leur intérêt en raison d'un processus d'examen plus long: blog.spreedly.com/2014/06/24/…
Vlad
6
Serait-il juste de dire que --no-ffd'une fonctionnalité à développer ou développer à maîtriser est similaire à la fusion d'une demande de pull?
merlinpatt
9
@merlinpatt Bien sûr. Si vous fusionnez une demande d'extraction dans GitHub, cela fait l'équivalent de --no-ff.
Lily Ballard
1
Ceci est une bonne explication. Mec, il n'y a tout simplement pas assez de bonnes explications sur le git pour que les gens comprennent pourquoi l'histoire propre du git est vraiment, vraiment importante (moi inclus!)
dudewad
1037

Réponse graphique à cette question

Voici un site avec une explication claire et une illustration graphique de l'utilisation git merge --no-ff:

différence entre git merge --no-ff et git merge

Jusqu'à ce que je voie cela, j'étais complètement perdu avec Git. L'utilisation --no-ffpermet à une personne qui examine l'historique de voir clairement la branche sur laquelle vous avez travaillé. (ce lien pointe vers l'outil de visualisation "réseau" de github) Et voici une autre grande référence avec des illustrations. Cette référence complète bien la première avec plus de concentration sur ceux qui connaissent moins git.


Infos de base pour les newbs comme moi

Si vous êtes comme moi et pas un Git-gourou, ma réponse décrit ici la gestion de la suppression de fichiers du suivi de git sans les supprimer du système de fichiers local, ce qui semble mal documenté mais souvent survenu. Une autre situation nouvelle consiste à obtenir le code actuel , qui parvient toujours à m'échapper.


Exemple de workflow

J'ai mis à jour un package sur mon site Web et j'ai dû revenir à mes notes pour voir mon flux de travail; J'ai pensé qu'il était utile d'ajouter un exemple à cette réponse.

Mon workflow de commandes git:

git checkout -b contact-form
(do your work on "contact-form")
git status
git commit -am  "updated form in contact module"
git checkout master
git merge --no-ff contact-form
git branch -d contact-form
git push origin master

Ci-dessous: utilisation réelle, y compris les explications.
Remarque: la sortie ci-dessous est coupée; git est assez bavard.

$ git status
# On branch master
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   ecc/Desktop.php
#       modified:   ecc/Mobile.php
#       deleted:    ecc/ecc-config.php
#       modified:   ecc/readme.txt
#       modified:   ecc/test.php
#       deleted:    passthru-adapter.igs
#       deleted:    shop/mickey/index.php
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       ecc/upgrade.php
#       ecc/webgility-config.php
#       ecc/webgility-config.php.bak
#       ecc/webgility-magento.php

Remarquez 3 choses ci-dessus:
1) Dans la sortie, vous pouvez voir les modifications de la mise à niveau du package ECC, y compris l'ajout de nouveaux fichiers.
2) Notez également qu'il y a deux fichiers (pas dans le /eccdossier) que j'ai supprimés indépendamment de ce changement. Au lieu de confondre ces suppressions de fichiers ecc, je créerai une cleanupbranche différente plus tard pour refléter la suppression de ces fichiers.
3) Je n'ai pas suivi mon workflow! J'ai oublié git pendant que j'essayais de faire fonctionner ecc à nouveau.

Ci-dessous: plutôt que de faire le tout compris, git commit -am "updated ecc package"je le ferais normalement, je voulais seulement ajouter les fichiers dans le /eccdossier. Ces fichiers supprimés ne faisaient pas spécifiquement partie des miens git add, mais comme ils étaient déjà suivis dans git, je dois les supprimer du commit de cette branche:

$ git checkout -b ecc
$ git add ecc/*
$ git reset HEAD passthru-adapter.igs
$ git reset HEAD shop/mickey/index.php
Unstaged changes after reset:
M       passthru-adapter.igs
M       shop/mickey/index.php

$ git commit -m "Webgility ecc desktop connector files; integrates with Quickbooks"

$ git checkout master
D       passthru-adapter.igs
D       shop/mickey/index.php
Switched to branch 'master'
$ git merge --no-ff ecc
$ git branch -d ecc
Deleted branch ecc (was 98269a2).
$ git push origin master
Counting objects: 22, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (14/14), done.
Writing objects: 100% (14/14), 59.00 KiB, done.
Total 14 (delta 10), reused 0 (delta 0)
To [email protected]:me/mywebsite.git
   8a0d9ec..333eff5  master -> master



Script pour automatiser ce qui précède

Ayant utilisé ce processus plus de 10 fois par jour, je me suis mis à écrire des scripts batch pour exécuter les commandes, j'ai donc fait un git_update.sh <branch> <"commit message">script presque approprié pour effectuer les étapes ci-dessus. Voici la source Gist pour ce script.

Au lieu de git commit -amsélectionner des fichiers dans la liste "modifiée" produite via git statuspuis les coller dans ce script. Cela est dû au fait que j'ai effectué des dizaines de modifications mais que je voulais des noms de branche variés pour aider à regrouper les changements.

Chris K
la source
11
Pouvez-vous toujours supprimer une branche en toute sécurité après une fusion avec l' --no-ffoption?
Andy Fleming
17
@DesignerGuy oui, vous pouvez supprimer en toute sécurité l'ancienne branche. Considérez les branches comme de simples pointeurs vers un commit spécifique.
Zyphrax
2
J'ai trouvé ce texte de la page liée utile: sans --no-ff "il est impossible de voir dans l'historique Git quels objets de commit ont implémenté une fonctionnalité ensemble - vous devriez lire manuellement tous les messages du journal."
Lorne Laliberte
une image vaut toujours mieux que mille mots!
rupps
1
Le graphique ne montre pas la branche des fonctionnalités dans la deuxième image, pourquoi pas? Il existe toujours, non?
ADJenks
269

Stratégies de fusion

Fusion explicite : crée une nouvelle validation de fusion. (C'est ce que vous obtiendrez si vous l'utilisiez --no-ff.)

entrez la description de l'image ici

Fast Forward Merge: avancer rapidement, sans créer de nouveau commit:

entrez la description de l'image ici

Rebase : Établir un nouveau niveau de base:

entrez la description de l'image ici

Courge: écraser ou presser (quelque chose) avec force pour qu'elle devienne plate:

entrez la description de l'image ici

Premraj
la source
11
Graphique intéressant mais il ne montre pas vraiment le cas --no-ff et ne répond donc pas tout à fait à la question ici
Vib
22
La première, la «fusion explicite», intitulée ici «fusion», est une fusion non ff.
bkribbs
3
que les graphismes m'ont vraiment aidé beaucoup
exexzian
2
ne répond pas à la question, mais ce graphique est étonnant, UPVOTE!
SovietFrontier
3
Alors, quelle est la différence entre Rebase et fusion rapide?
ThanosFisherman
217

L' --no-ffoption garantit qu'aucune fusion d'avance rapide ne se produira et qu'un nouvel objet de validation sera toujours créé . Cela peut être souhaitable si vous souhaitez que git conserve un historique des branches de fonctionnalités.             git merge --no-ff vs git merge Dans l'image ci-dessus, le côté gauche est un exemple de l'historique git après utilisation git merge --no-ffet le côté droit est un exemple d'utilisation git mergeoù une fusion ff était possible.

EDIT : une version précédente de cette image indiquait un seul parent pour la validation de fusion. Les validations de fusion ont plusieurs validations parentales que git utilise pour conserver un historique de la "branche de fonctionnalité" et de la branche d'origine. Les multiples liens parents sont surlignés en vert.

Daniel Smith
la source
3
Qu'est-ce que la flèche verte indique par rapport à la flèche noire?
rtconner du
11
la flèche verte indique un lien entre une validation et un parent où la validation a plusieurs parents. Les validations normales (noires) n'ont qu'un seul parent.
Daniel Smith
9
@DanielSmith Comment dessinez-vous ce graphique élégant?
chancyWu
4
@chancyWu avec Adobe Illustrator
Daniel Smith
Il semble que dans mon entreprise, toutes les demandes d'extraction soient fusionnées avec l'option --no-ff. Dans ces circonstances, est-il toujours judicieux de rebaser avant de créer une demande de tirage?
Nickpick
37

C'est une vieille question, et elle est quelque peu subtilement mentionnée dans les autres articles, mais l'explication qui a fait ce déclic pour moi est que les fusions non rapides nécessiteront un commit séparé .

Parris Varney
la source
Pourriez-vous s'il vous plaît revoir le flux de travail dans ma réponse ci-dessus: cela signifie-t-il que j'ai besoin de validations supplémentaires au-delà de ce que j'ai maintenant? Merci.
Chris K
3
@ChrisK, git merge --no-ff eccvous aurez simplement un commit de fusion supplémentaire dans le git logmaître de branche. Cela n'est techniquement pas nécessaire dans le cas où maître pointe vers un ancêtre direct de la validation ecc est activé, mais en spécifiant l'option --no-ff, vous forcez la création de cette validation de fusion. Il aura le titre:Merge branch 'ecc'
Ankur Agarwal
Excellent résumé! :)
Eduard
4

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 regroupe tous les validations qui ont ajouté la fonctionnalité ensemble.

jsina
la source