web-dev-qa-db-fra.com

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

En utilisant gitk log, je ne pouvais pas distinguer les deux. Comment puis-je observer la différence (avec une commande git ou un outil)?

828
user1162226

L'indicateur --no-ff empêche git merge d'exécuter une "avance rapide" s'il détecte que votre HEAD actuel est un ancêtre du commit que vous essayez de fusionner. Une avance rapide a lieu lorsque, au lieu de construire une validation de fusion, git déplace simplement le pointeur de votre branche sur la validation entrante. Cela se produit généralement lorsque vous faites un git pull sans aucune modification locale.

Toutefois, 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 sujet et vous souhaitez vous assurer que cela ressemble de cette manière lors de la lecture de l'historique). Afin de faire cela, vous pouvez passer le drapeau --no-ff et git merge va toujours construire une fusion au lieu de l’avance rapide.

De même, si vous voulez exécuter un git pull ou utiliser git merge afin d’avancer explicitement, et vous voulez renoncer s’il ne peut pas avancer rapidement, vous pouvez utiliser le --ff-only drapeau. De cette façon, vous pouvez régulièrement faire quelque chose comme git pull --ff-only sans y penser, et si cela se produit, vous pouvez revenir en arrière et décider si vous souhaitez fusionner ou rebaser.

935
Lily Ballard

Réponse graphique à cette question

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

difference between git merge --no-ff and git merge

Jusqu'à ce que je voie cela, j'étais complètement perdu avec git. En utilisant --no-ff permet à quelqu'un qui passe en revue l'historique de clairement voir la branche que vous avez extraite sur lequel travailler. (ce lien pointe vers l'outil de visualisation "réseau" de github) Et voici ne autre excellente référence avec des illustrations. Cette référence complète bien la première en mettant davantage l’accent sur ceux qui sont moins familiarisés avec git.


Informations de base pour newbs comme moi

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


Exemple de flux de travail

J'ai mis à jour un paquet sur mon site Web et je devais revenir à mes notes pour voir mon flux de travail. J'ai pensé qu'il serait utile d'ajouter un exemple à cette réponse.

Mon flux de travail des 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 extraite; git est assez prolixe.

$ 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

Notez 3 choses d'en haut:
1) Dans le résultat, vous pouvez voir les modifications apportées par 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 dossier /ecc) que j'ai supprimés indépendamment de cette modification. Au lieu de confondre ces suppressions de fichiers avec ecc, je créerai ultérieurement une autre branche cleanup pour refléter la suppression de ces fichiers.
3) Je n'ai pas suivi mon flux de travail! J'ai oublié git pendant que j'essayais de faire fonctionner la ecc à nouveau.

Ci-dessous: plutôt que de faire _ tout-inclus git commit -am "updated ecc package" comme d'habitude, je voulais uniquement ajouter les fichiers dans le dossier /ecc. Ces fichiers supprimés ne faisaient pas spécifiquement partie de mon git add, mais comme ils ont déjà été 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, j'ai commencé à écrire des scripts batch pour exécuter les commandes. J'ai donc créé un script presque correct git_update.sh <branch> <"commit message"> pour effectuer les étapes ci-dessus. Voici la source Gist pour ce script.

Au lieu de git commit -am, je sélectionne des fichiers dans la liste "modifiée" générée via git status, puis je les colle dans ce script. Cela est dû au fait que j'ai effectué des dizaines de modifications, mais que je voulais divers noms de branches pour aider à regrouper les modifications.

897
Chris K

L'option --no-ff garantit qu'une fusion rapide ne se produira pas et que n nouvel objet de validation sera toujours créé. Cela peut être souhaitable si vous voulez que git maintienne 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 de git après l'utilisation de git merge --no-ff et le côté droit est un exemple d'utilisation de git merge dans lequel une fusion ff était possible.

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

191
Daniel Smith

Stratégies de fusion

Fusion explicite : crée un nouveau commit de fusion (c'est ce que vous obtiendrez si vous utilisiez --no-ff.)

enter image description here

Fusion rapide: Transférez rapidement, sans créer de nouveau commit:

enter image description here

Rebase : Établit un nouveau niveau de base:

enter image description here

Squash: Écrasez ou écrasez (quelque chose) avec force pour qu'il devienne plat:

enter image description here

127
Premraj

C'est une vieille question, et cela est mentionné de manière assez subtile dans les autres articles, mais l'explication qui a fait ce clic pour moi est que les fusions sans avance rapide nécessiteront un commit séparé.

34
Parris Varney

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 ayant ajouté la fonctionnalité.

1
jsina