Was ist der Unterschied zwischen "Git Merge" und "Git Merge --no-ff"?

Antworten:

1080

Das --no-ffFlag verhindert git merge, dass ein "schneller Vorlauf" ausgeführt wird, wenn festgestellt wird, dass Ihre aktuelle HEADDatei ein Vorfahr des Commits ist, das Sie zusammenführen möchten. Ein schneller Vorlauf ist, wenn git anstelle eines Zusammenführungs-Commits nur Ihren Verzweigungszeiger so bewegt, dass er auf das eingehende Commit zeigt. Dies tritt normalerweise auf, wenn a git pullohne lokale Änderungen ausgeführt wird.

Gelegentlich möchten Sie jedoch verhindern, dass dieses Verhalten auftritt, normalerweise, weil Sie eine bestimmte Zweigstellentopologie beibehalten möchten (z. B. wenn Sie in einem Themenzweig zusammenführen und sicherstellen möchten, dass dies beim Lesen des Verlaufs so aussieht). Zu diesem Zweck können Sie das --no-ffFlag übergeben und git mergeerstellen immer eine Zusammenführung anstelle eines schnellen Vorlaufs.

In ähnlicher Weise können Sie das Flag verwenden , wenn Sie a ausführen git pulloder verwenden möchten, um einen git mergeexpliziten Schnellvorlauf durchzuführen, und wenn Sie aussteigen möchten, wenn der Schnellvorlauf nicht möglich ist --ff-only. Auf diese Weise können Sie regelmäßig so etwas wie git pull --ff-onlyohne nachzudenken tun. Wenn es dann zu Fehlern kommt, können Sie zurückgehen und entscheiden, ob Sie zusammenführen oder neu aufbauen möchten.

Lily Ballard
quelle
87
Um mehr direkt die Frage des OP zu beantworten: sie sind nicht immer anders, aber wenn doch, dann ist es klar , gitkoder git log --graphdass das Vorspulen merge nicht eine Zusammenführung schaffen verpflichten, während die Nicht-vorspulen tat.
Cascabel
11
Es wäre schön, den Grund für die Vermeidung von ff zu erläutern: Der vom Autor erwähnte "spezifische Branchentopologie" bedeutet, dass im Fall --no-ff ein zusätzliches Merge-Commit als Marker für das Merge dient. Der Profi ist ein expliziter Merge-Marker mit den Namen des Autors und der Fusion. Die Nachteile sind nichtlineare Geschichte, die wie eine Reihe von konvergierenden Eisenbahnschienen aussieht. Ein möglicher psychologischer Nebeneffekt der Fusion ist jedoch, dass Mitwirkende aufgrund eines längeren Überprüfungsprozesses das Interesse verlieren: blog.spreedly.com/2014/06/24/…
Vlad
6
Wäre es fair zu sagen, dass --no-ffdas Entwickeln eines Features oder das Entwickeln zum Master dem Zusammenführen einer Pull-Anforderung ähnelt?
Merlinpatt
9
@merlinpatt Sicher. Wenn Sie eine Pull-Anfrage in GitHub zusammenführen, entspricht dies --no-ff.
Lily Ballard
1
Dies ist eine gute Erklärung. Mann, es gibt einfach nicht genug gute Git-Erklärungen, damit die Leute verstehen, warum saubere Git-Geschichte wirklich, wirklich wichtig ist (ich eingeschlossen!)
Dudewad
1037

Grafische Antwort auf diese Frage

Hier ist eine Seite mit einer klaren Erklärung und grafischen Darstellung der Verwendung git merge --no-ff:

Unterschied zwischen Git Merge --no-ff und Git Merge

Bis ich das sah, war ich völlig mit Git verloren. Mithilfe von --no-ffkann jemand, der den Verlauf überprüft, den Zweig, an dem Sie gearbeitet haben , deutlich sehen . (Dieser Link verweist auf Githubs "Netzwerk" -Visualisierungstool.) Und hier ist eine weitere großartige Referenz mit Abbildungen. Diese Referenz ergänzt die erste sehr gut und konzentriert sich mehr auf diejenigen, die mit Git weniger vertraut sind.


Grundlegende Informationen für Neulinge wie mich

Wenn Sie wie ich und kein Git-Guru sind, beschreibt meine Antwort hier, wie Sie das Löschen von Dateien aus dem Tracking von git behandeln, ohne sie aus dem lokalen Dateisystem zu löschen, was schlecht dokumentiert zu sein scheint, aber häufig vorkommt. Eine andere neue Situation ist das Abrufen des aktuellen Codes , der mir immer noch entgeht .


Beispiel für einen Workflow

Ich habe ein Paket auf meiner Website aktualisiert und musste zu meinen Notizen zurückkehren, um meinen Workflow anzuzeigen. Ich fand es nützlich, dieser Antwort ein Beispiel hinzuzufügen.

Mein Workflow von Git-Befehlen:

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

Unten: tatsächliche Verwendung, einschließlich Erläuterungen.
Hinweis: Die folgende Ausgabe wird abgeschnitten. Git ist ziemlich ausführlich.

$ 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

Beachten Sie 3 Dinge von oben:
1) In der Ausgabe sehen Sie die Änderungen aus dem Upgrade des ECC-Pakets, einschließlich des Hinzufügens neuer Dateien.
2) Beachten Sie auch, dass es zwei Dateien gibt (nicht im /eccOrdner), die ich unabhängig von dieser Änderung gelöscht habe. Anstatt diese Dateilöschungen mit zu verwechseln ecc, werde ich cleanupspäter einen anderen Zweig erstellen, um die Löschung dieser Dateien widerzuspiegeln.
3) Ich bin meinem Workflow nicht gefolgt! Ich habe git vergessen, als ich versuchte, ecc wieder zum Laufen zu bringen.

Unten: Anstatt das All-Inclusive zu machen, das git commit -am "updated ecc package"ich normalerweise machen würde, wollte ich nur die Dateien in den /eccOrdner einfügen. Diese gelöschten Dateien waren nicht speziell Teil von mir git add, aber da sie bereits in git verfolgt wurden, muss ich sie aus dem Commit dieses Zweigs entfernen:

$ 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



Skript zur Automatisierung der oben genannten

Nachdem ich diesen Prozess mehr als 10 Mal am Tag verwendet habe, habe ich Batch-Skripte geschrieben, um die Befehle auszuführen. Daher habe ich ein fast richtiges git_update.sh <branch> <"commit message">Skript für die obigen Schritte erstellt. Hier ist die Hauptquelle für dieses Skript.

Anstatt git commit -amich Dateien aus der "geänderten" Liste auszuwählen, die über erstellt wurden, git statusund diese dann in dieses Skript einzufügen. Dies geschah, weil ich Dutzende von Änderungen vorgenommen hatte, aber verschiedene Zweignamen wollte, um die Änderungen zu gruppieren.

Chris K.
quelle
11
Können Sie einen Zweig nach dem Zusammenführen mit der --no-ffOption immer noch sicher löschen ?
Andy Fleming
17
@DesignerGuy ja du kannst den alten Zweig sicher löschen. Stellen Sie sich Zweige nur als Zeiger auf ein bestimmtes Commit vor.
Zyphrax
2
Ich fand diesen Text von der verlinkten Seite hilfreich: Ohne --no-ff "ist aus dem Git-Verlauf nicht ersichtlich, welche der Festschreibungsobjekte zusammen eine Funktion implementiert haben - Sie müssten alle Protokollnachrichten manuell lesen."
Lorne Laliberte
Ein Bild immer besser als tausend Worte!
Rupps
1
Die Grafik zeigt nicht den Feature-Zweig im zweiten Bild. Warum nicht? Es existiert immer noch, nicht wahr?
ADJenks
269

Strategien zusammenführen

Explizite Zusammenführung : Erstellt ein neues Zusammenführungs-Commit. (Dies ist, was Sie erhalten, wenn Sie verwendet --no-ff.)

Geben Sie hier die Bildbeschreibung ein

Schnellvorlauf-Zusammenführung: Schnellvorlauf ohne Erstellung eines neuen Commits:

Geben Sie hier die Bildbeschreibung ein

Rebase : Richten Sie eine neue Basisebene ein:

Geben Sie hier die Bildbeschreibung ein

Squash: Crush oder Squeeze (etwas) mit Gewalt, so dass es flach wird:

Geben Sie hier die Bildbeschreibung ein

Premraj
quelle
11
Interessante Grafik, aber sie zeigt nicht wirklich den Fall --no-ff und beantwortet daher die Frage hier nicht ganz
Vib
22
Die erste, die "explizite Zusammenführung", die hier als "Zusammenführung" bezeichnet wird, ist eine Zusammenführung ohne ff.
Bkribbs
3
Diese Grafik hat mir wirklich sehr geholfen
exexzian
2
beantwortet die Frage nicht per say, aber diese Grafik ist erstaunlich, UPVOTE!
Sowjetische Frontier
3
Was ist dann der Unterschied zwischen Rebase und Fast-Forward-Merge?
ThanosFisherman
217

Die --no-ffOption stellt sicher, dass keine Schnellvorlaufzusammenführung stattfindet und dass immer ein neues Festschreibungsobjekt erstellt wird . Dies kann wünschenswert sein, wenn git einen Verlauf von Feature-Zweigen verwalten soll.             git merge --no-ff vs git merge Im obigen Bild ist die linke Seite ein Beispiel für den Git-Verlauf nach der Verwendung git merge --no-ffund die rechte Seite ist ein Beispiel für die Verwendung, git mergebei der eine Zusammenführung möglich war.

BEARBEITEN : In einer früheren Version dieses Bildes wurde nur ein übergeordnetes Element für das Zusammenführungs-Commit angegeben. Merge-Commits haben mehrere übergeordnete Commits, mit denen git den Verlauf des "Feature-Zweigs" und des ursprünglichen Zweigs verwaltet. Die mehreren übergeordneten Links werden grün hervorgehoben.

Daniel Smith
quelle
3
Was zeigt der grüne Pfeil gegenüber dem schwarzen Pfeil an?
RTconner
11
Der grüne Pfeil zeigt eine Verknüpfung zwischen einem Commit und einem übergeordneten Element an, bei dem das Commit mehr als ein übergeordnetes Element hat. Normale Commits (schwarz) haben nur einen Elternteil.
Daniel Smith
9
@ DanielSmith Wie zeichnest du diese elegante Grafik?
chancyWu
4
@chancyWu mit Adobe Illustrator
Daniel Smith
In meiner Firma scheinen alle Pull-Anfragen mit der Option --no-ff zusammengeführt zu sein. Ist es unter diesen Umständen immer noch sinnvoll, die Basis neu zu erstellen, bevor ich eine Pull-Anforderung erstelle?
Nickpick
37

Dies ist eine alte Frage, und dies wird in den anderen Beiträgen etwas subtil erwähnt, aber die Erklärung, die diesen Klick für mich gemacht hat, ist, dass nicht schnelle Vorlaufzusammenführungen ein separates Commit erfordern .

Parris Varney
quelle
Würden Sie bitte den Workflow in meiner obigen Antwort überprüfen: Bedeutet dies, dass ich zusätzliche Commits benötige, die über das hinausgehen, was ich jetzt habe? Vielen Dank.
Chris K
3
@ChrisK git merge --no-ff eccSie haben einfach ein zusätzliches Merge-Commit im git logfor-Branch-Master. Dies ist technisch nicht erforderlich, wenn der Master auf einen direkten Vorfahren des Commits verweist. Ecc ist aktiviert. Wenn Sie jedoch die Option --no-ff angeben, erzwingen Sie die Erstellung dieses Merge-Commits. Es wird den Titel haben:Merge branch 'ecc'
Ankur Agarwal
Tolle Zusammenfassung! :)
Eduard
4

Das Flag --no-ff bewirkt, dass bei der Zusammenführung immer ein neues Festschreibungsobjekt erstellt wird, auch wenn die Zusammenführung mit einem schnellen Vorlauf durchgeführt werden könnte. Dadurch wird vermieden, dass Informationen über die historische Existenz eines Feature-Zweigs verloren gehen und alle Commits zusammengefasst werden, die das Feature hinzugefügt haben

jsina
quelle