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?
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 B
verwenden 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 branch2
aber 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 feature
das hat einen Upstream von origin/feature
und 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 C
geben:
...-o--C <-- origin/feature
\
A'-B' <-- HEAD=feature
Dies sind Kopien Ihres Originals A
und B
wobei 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/feature
git 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 B
und Sie haben darum gebeten, diese mit zusammenzuführen C
git 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 B
können Sie verwenden git rebase
; aber wenn es einen guten Grund gibt nicht Um sie zu kopieren, können Sie verwenden git merge
vielleicht mit --ff-only
um 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 pull
bei 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 rebase
je nachdem, wie Sie es konfigurieren und ausführen.
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.
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.