Gibt es einen Unterschied zwischen git rebase und git merge –ff-only

Lesezeit: 8 Minuten

Nach dem, was ich gelesen habe, helfen uns beide, eine lineare Geschichte zu bekommen.

Nach dem, was ich experimentiert habe, funktioniert Rebase die ganze Zeit. Aber merge –ff-only funktioniert nur in Szenarien, in denen es schnell weitergeleitet werden kann.

Mir ist auch aufgefallen, dass git merge einen Merge-Commit erstellt, aber wenn wir –ff-only verwenden, ergibt dies einen linearen Verlauf, der im Wesentlichen dem Git-Rebasing entspricht. Also –ff-only tötet den Zweck von git merge, richtig?

Was ist also der eigentliche Unterschied zwischen ihnen?

Benutzer-Avatar
Torek

Beachten Sie, dass git rebase hat eine andere Arbeit als git merge (mit oder ohne --ff-only). Was rebase ist es, vorhandene Commits zu nehmen und Kopieren Sie. Angenommen, Sie sind eingeschaltet branch1 und habe zwei Commits gemacht A und B:

...-o--o--A--B   <-- HEAD=branch1
        \
         o--C    <-- branch2

und Sie entscheiden, dass Sie diese beiden Commits lieber aktiviert haben möchten branch2 stattdessen. Du kannst:

  • erhalten Sie eine Liste der Änderungen, die Sie vorgenommen haben A (verschieden A gegen seinen Elternteil)
  • erhalten Sie eine Liste der Änderungen, die Sie vorgenommen haben B (verschieden B gegen A)
  • wechseln zu branch2
  • nehmen Sie die gleichen Änderungen vor, die Sie in vorgenommen haben A und übertrage sie, indem du deine Commit-Nachricht von kopierst A; nennen wir das Commit A'
  • und nehmen Sie dann die gleichen Änderungen vor, die Sie in vorgenommen haben B und übertrage sie, indem du deine Commit-Nachricht von kopierst B; nennen wir das B'.

Es gibt einen Git-Befehl, der dieses Diff-and-then-copy-and-commit für Sie erledigt: git cherry-pick. So:

git checkout branch2      # switch HEAD to branch2 (commit C)
git cherry-pick branch1^  # this copies A to A'
git cherry-pick branch1   # and this copies B to B'

Jetzt hast du das:

...-o--o--A--B         <-- branch1
        \
         o--C--A'-B'   <-- HEAD=branch2

Jetzt können Sie wieder zu wechseln branch1 und lösche dein Original A und Bverwenden git reset (Ich werde verwenden --hard Hier ist es bequemer, da es auch den Arbeitsbaum aufräumt):

git checkout branch1
git reset --hard HEAD~2

Dadurch wird das Original entfernt A und B,1 also jetzt hast du:

...-o--o               <-- HEAD=branch1
        \
         o--C--A'-B'   <-- branch2

Jetzt müssen Sie nur noch auschecken branch2 dort weiter zu arbeiten.

Das ist was git rebase tut: es “verschiebt” Commits (allerdings nicht, indem es sie tatsächlich verschiebt, weil es das nicht kann: in Git kann ein Commit niemals geändert werden, also erfordert selbst das Ändern der Eltern-ID das Kopieren in einen neuen und etwas anderen Commit).

Mit anderen Worten, während git cherry-pick ist ein automatisiertes Diff-and-Redo von eines verpflichten, git rebase ist ein automatisierter Prozess der Wiederherstellung mehrere Commits, plus am Ende das Verschieben von Labels, um die Originale zu “vergessen” oder zu verstecken.

Das Obige veranschaulicht das Verschieben von Commits von einem lokalen Zweig branch1 zu einer anderen Ortsgruppe branch2aber git verwendet die genau der gleiche Vorgang Commits zu verschieben, wenn Sie einen Remote-Tracking-Zweig haben, der einige neue Commits erwirbt, wenn Sie a git fetch (einschließlich der fetch das ist der erste schritt von git pull). Sie könnten damit beginnen, am Zweig zu arbeiten featuredas hat einen Upstream von origin/featureund machen Sie ein paar eigene Commits:

...-o        <-- origin/feature
     \
      A--B   <-- HEAD=feature

Aber dann entscheidest du, dass du sehen solltest, was flussaufwärts passiert ist, also rennst du git fetch,2 und, aha, jemand im Upstream hat einen Commit geschrieben C:

...-o--C     <-- origin/feature
     \
      A--B   <-- HEAD=feature

An dieser Stelle können Sie Ihre einfach umbasieren feature‘s A und B auf zu Cgeben:

...-o--C     <-- origin/feature
        \
         A'-B'  <-- HEAD=feature

Dies sind Kopien Ihres Originals A und Bwobei die Originale weggeworfen werden (aber siehe Fußnote 1), nachdem die Kopien fertig sind.


Manchmal gibt es nichts zu rebasen, dh keine Arbeit, die Sie selbst geleistet haben. Das heißt, die Grafik vor dem fetch sieht aus wie das:

...-o      <-- origin/feature
           `-- HEAD=feature

Wenn Sie dann git fetch und begehen C kommt, aber Sie sind links mit dein feature Verzweigung, die auf das alte Commit zeigt, während origin/feature ist weitergekommen:

...-o--C   <-- origin/feature
     `---- <-- HEAD=feature

Das ist wo git merge --ff-only kommt herein: wenn Sie darum bitten, Ihren aktuellen Zweig zusammenzuführen feature mit origin/featuregit sieht, dass es möglich ist, den Pfeil sozusagen einfach nach vorne zu schieben, damit feature Punkte direkt zu begehen C. Es ist keine tatsächliche Zusammenführung erforderlich.

Wenn Sie Ihre eigenen Commits hatten A und Bund Sie haben darum gebeten, diese mit zusammenzuführen Cgit würde eine echte Zusammenführung durchführen und einen neuen Merge-Commit erstellen M:

...-o--C        <-- origin/feature
     \   `-_
      A--B--M   <-- feature

Hier, --ff-only stoppt und gibt Ihnen einen Fehler. Rebase hingegen kann kopieren A und B zu A' und B' und dann das Original verstecken A und B.

Also, kurz gesagt (ok, zu spät 🙂 ), sie machen einfach verschiedene Dinge. Manchmal ist das Ergebnis dasselbe, manchmal nicht. Wenn es in Ordnung ist zu kopieren A und Bkönnen Sie verwenden git rebase; aber wenn es einen guten Grund gibt nicht Um sie zu kopieren, können Sie verwenden git mergevielleicht mit --ff-onlyum nach Bedarf zusammenzuführen oder fehlzuschlagen.


1Git bewahrt die Originale tatsächlich einige Zeit auf – in diesem Fall normalerweise einen Monat –, aber es versteckt sie. Der einfachste Weg, sie zu finden, ist mit gits “Reflogs”, die eine Historie darüber führen, wohin jeder Zweig zeigt und wohin HEAD wies darauf hin, vor jeder Änderung, die den Zweig aktualisiert bzw. aktualisiert hat HEAD.

Irgendwann laufen die Reflog-Verlaufseinträge ab, ab diesem Zeitpunkt kommen diese Commits in Frage Müllabfuhr.

2Oder Sie können wieder verwenden git pullbei dem es sich um ein praktisches Skript handelt, das mit der Ausführung beginnt git fetch. Sobald der Abruf abgeschlossen ist, wird das Convenience-Skript entweder ausgeführt git merge oder git rebaseje nachdem, wie Sie es konfigurieren und ausführen.

  • Gute Antwort. Vielen Dank.

    – Iwin

    26. Januar 2015 um 5:48 Uhr

  • Ich mag die Art und Weise, wie Sie es verwenden git cherry-pick erklären git rebase. Sieht großartig aus!

    – Kaihua

    29. Januar 2019 um 15:59 Uhr

  • Was ist die empfohlene Strategie, wenn Sie einen PR aus dem Entwicklungszweig mit dem Master (Prod) zusammenführen?

    – Shanika Ediriweera

    27. März 2019 um 0:56 Uhr

  • Das klingt nach einem Ort, an dem man eine Standardzusammenführung durchführen kann, wahrscheinlich mit --no-ff hier auch.

    – Torek

    27. März 2019 um 1:19 Uhr

  • @GViz: Der eigentliche Inhalt eines Commits sind nur die Metadaten: Der Quell-Snapshot ist a tree Eintrag in die Metadaten, und wenn dieser Baum ein Duplikat eines früheren Baums ist, werden die beiden Bäume tatsächlich buchstäblich gemeinsam genutzt. Da Bäume Unterbäume enthalten, funktioniert diese Art des Teilens auch dann, wenn einige Teile einiger Commits unterschiedlich sind (die gemeinsamen Unterbäume werden geteilt und die unterschiedlichen Unterbäume unterscheiden sich; wenn Unterschiede “aufsteigen”, wird der Baum der obersten Ebene in diesem Fall anders sein). .

    – Torek

    18. August um 7:34 Uhr

Ja, es gibt einen Unterschied. git merge --ff-only bricht ab, wenn es nicht schnell vorspulen kann, und nimmt ein Commit (normalerweise eine Verzweigung) zum Zusammenführen --ff-only).

git rebase schreibt den Verlauf des aktuellen Zweigs um oder kann verwendet werden, um einen vorhandenen Zweig auf einen vorhandenen Zweig umzubasieren. In diesem Fall wird kein Merge-Commit erstellt, da es sich um eine Rebasierung handelt, anstatt um eine Zusammenführung.

  • Das heißt, –ff-only prüft und führt keine Zusammenführung durch, wenn dies nicht möglich ist, ohne den Verlauf neu zu schreiben. Während Rebase es tut, indem es die Geschichte neu schreibt. Ist es?

    – Iwin

    25. Januar 2015 um 20:13 Uhr

  • Nicht ganz. Fusionen schreiben die Geschichte nicht um, und somit git merge schreibt niemals die Geschichte um (mit oder ohne --ff-only). --ff-only verhindert jede andere Zusammenführung als eine Fast-Forward-Zusammenführung (keine Zusammenführungen überschreiben den Verlauf). Bei anderen Zusammenführungen als Fast-Forward-Zusammenführungen werden im Allgemeinen einfach die Änderungssätze in der falschen Reihenfolge angewendet (das ist eine Vereinfachung, aber gut genug, um mitzumachen), und es werden Metadaten aufgezeichnet, die zeigen, dass ein Änderungssatz zusammengeführt wurde. kein Umschreiben der Geschichte erforderlich.

    – Licht

    25. Januar 2015 um 21:42 Uhr

Ja, --ff-only wird immer dort scheitern, wo eine Ebene git merge versagen würde und wo eine Ebene versagen könnte git merge gelingen würde. Das ist der Punkt – wenn Sie versuchen, einen linearen Verlauf zu führen, und die Zusammenführung auf diese Weise nicht möglich ist, Sie wollen es scheitern.

Eine Option, die Fehlerfälle zu einem Befehl hinzufügt, ist nicht nutzlos; Auf diese Weise wird eine Vorbedingung validiert. Wenn der aktuelle Zustand des Systems also nicht Ihren Erwartungen entspricht, verschlimmern Sie das Problem nicht.

  • Ist es also so, dass –ff-only verwendet wird, um das Szenario zu validieren und wenn möglich einen linearen Verlauf zu erstellen; während Rebase eher wäre, erstellen Sie unabhängig vom Szenario einen linearen Verlauf. Ist es?

    – Iwin

    25. Januar 2015 um 19:36 Uhr

  • Ja, und wenn Sie etwas rebasen, das nicht sauber genug war merge --ff-only Sie entfernen Commits von einer Stelle im Verlaufsbaum und fügen sie (mit Änderungen) an einer anderen Stelle wieder ein. Dies nennt man “Verlauf umschreiben” und es ist in Ordnung, wenn Sie sich in einem unveröffentlichten Zweig befinden, aber nachdem Sie Ihren Zweig veröffentlicht haben und andere Leute ihn geklont haben, verursacht das Umschreiben des Verlaufs Probleme für sie.

    Benutzer2404501

    25. Januar 2015 um 20:01 Uhr

1322510cookie-checkGibt es einen Unterschied zwischen git rebase und git merge –ff-only

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy