Wann verwenden Sie Git Rebase anstelle von Git Merge?

1549

Wann wird empfohlen, Git Rebase vs. Git Merge zu verwenden?

Muss ich nach einer erfolgreichen Rebase noch zusammenführen?

Coocoo4Cocoa
quelle
16
das ist gut: atlassian.com/git/tutorials/merging-vs-rebasing
stackexchanger
6
Ein Problem bei Leuten, die Rebase verwenden möchten, ist, dass es sie davon abhält, ihren Code regelmäßig zu pushen. Wenn sie also einen sauberen Verlauf wünschen, können sie ihren Code nicht weitergeben, was meiner Meinung nach wichtiger ist.
static_rtti
9
@static_rtti: Das stimmt einfach nicht. Sie verwenden einen Rebase-basierten Ablauf falsch, wenn Sie Ihre Änderungen nicht regelmäßig übertragen können.
Juzzlin
5
Es ist wirklich schade, dass Andrew Arnotts Antwort und Paces Antwort nicht früher veröffentlicht wurden, da sie diese Frage umfassender beantworten als frühere Antworten, bei denen bereits viele Stimmen gesammelt wurden.
Mark Booth

Antworten:

1136

Kurzfassung

  • Beim Zusammenführen werden alle Änderungen in einem Zweig übernommen und in einem Commit in einem anderen Zweig zusammengeführt.
  • Rebase sagt, ich möchte, dass der Punkt, an dem ich verzweigt habe, zu einem neuen Startpunkt wechselt

Wann verwenden Sie eine der beiden?

Verschmelzen

  • Angenommen, Sie haben einen Zweig erstellt, um ein einzelnes Feature zu entwickeln. Wenn Sie diese Änderungen wieder in den Master übernehmen möchten, möchten Sie wahrscheinlich zusammenführen (es ist Ihnen egal, ob Sie alle vorläufigen Commits beibehalten).

Rebase

  • Ein zweites Szenario wäre, wenn Sie mit der Entwicklung beginnen und dann ein anderer Entwickler eine nicht verwandte Änderung vornimmt. Sie möchten wahrscheinlich die Änderungen aus der aktuellen Version aus dem Repository abrufen und dann neu starten .
Rob Di Marco
quelle
105
@Rob erwähnte die Beibehaltung von Interim Commits beim Zusammenführen. Ich glaube, dass durch das Zusammenführen von Zweig B (einem Feature-Zweig, an dem Sie gearbeitet haben) mit Zweig M (dem Hauptzweig) standardmäßig ein Commit in M ​​für jedes Commit erstellt wird, das in B durchgeführt wurde, da die beiden auseinander gingen. Wenn Sie jedoch mit der Option --squash zusammenführen, werden alle in Zweig B vorgenommenen Festschreibungen "zusammengefasst" und als einzelne Festschreibung in Zweig M zusammengeführt, sodass das Protokoll in Ihrem Hauptzweig schön und sauber bleibt. Squashing ist wahrscheinlich das, was Sie wollen, wenn Sie zahlreiche Entwickler haben, die unabhängig voneinander arbeiten und wieder zum Master verschmelzen.
spaaarky21
19
Ich glaube, dass die Annahme von @ spaaarky21 über das Zusammenführen nicht korrekt ist. Wenn Sie einen Zweig B mit dem Master M zusammenführen, gibt es nur ein einziges Commit für M (auch wenn B mehrere Commits hat), unabhängig davon, ob Sie eine einfache oder --squash-Zusammenführung verwenden. --Squash entfernt den Verweis auf B als übergeordnetes Element. Eine gute Visualisierung ist hier: syntevo.com/smartgithg/howtos.html?page=workflows.merge
jpeskin
14
@jpeskin Das sehe ich nicht. Ich habe gerade einen kurzen Test durchgeführt, um dies zu überprüfen. Erstellen Sie ein Verzeichnis mit einer Textdatei, initeinem neuen Repo, addder Datei und commit. Auschecken eines neuen Feature-Zweigs ( checkout -b feature.) Ändern Sie die Textdatei, schreiben Sie sie fest und wiederholen Sie sie, sodass der Feature-Zweig zwei neue Commits enthält. Dann checkout masterund merge feature. In logsehe ich mein anfängliches Commit für den Master, gefolgt von den beiden, die aus dem Feature zusammengeführt wurden. Wenn Sie das merge --squash featureFeature in den Master einbinden, aber nicht festschreiben, ist das einzige neue Commit für den Master das, das Sie selbst vorgenommen haben.
spaaarky21
21
@ spaaarky21 Es sieht so aus, als hätten wir beide halb recht. Wenn eine Schnellvorlaufzusammenführung möglich ist (wie in Ihrem Beispiel), schließt git standardmäßig alle Festschreibungen in den Feature-Zweig B ein (oder, wie Sie vorschlagen, können Sie --squash verwenden, um eine einzige Festschreibung zu kombinieren). In dem Fall, in dem Sie zwei unterschiedliche Zweige M und B zusammenführen, enthält git nicht alle einzelnen Commits von Zweig B, wenn sie in M ​​zusammengeführt werden (unabhängig davon, ob Sie --squash verwenden oder nicht).
Jpeskin
6
Warum ist das "(Sie kümmern sich nicht darum, alle Zwischenverpflichtungen beizubehalten)" noch in dieser Antwort enthalten? Es machte '09 keinen Sinn und es macht jetzt keinen Sinn. Auch sicher würden Sie nur rebase wollen , wenn ein anderer Entwickler gemacht bezogene Änderungen , dass Sie benötigt - wenn sie nicht verwandten Änderungen vorgenommen haben , die Feature - Zweig leicht ohnehin ohne Konflikte zusammenführen sollte, und Ihre Geschichte würde maintened werden.
Mark Booth
372

Es ist einfach. Mit rebase sagen Sie, dass Sie einen anderen Zweig als neue Basis für Ihre Arbeit verwenden möchten .

Wenn Sie beispielsweise einen Zweig haben master, erstellen Sie einen Zweig, um ein neues Feature zu implementieren, und sagen, Sie benennen ihn cool-feature. Natürlich ist der Hauptzweig die Basis für Ihr neues Feature.

Ab einem bestimmten Punkt möchten Sie nun die neue Funktion hinzufügen, die Sie in der masterVerzweigung implementiert haben . Sie können einfach zu masterdem cool-featureZweig wechseln und ihn zusammenführen :

$ git checkout master
$ git merge cool-feature

Auf diese Weise wird jedoch ein neues Dummy-Commit hinzugefügt. Wenn Sie Spaghetti-Geschichte vermeiden möchten, können Sie neu gründen :

$ git checkout cool-feature
$ git rebase master

Und dann zusammenführen in master:

$ git checkout master
$ git merge cool-feature

Dieses Mal ist die Zusammenführung nur ein schneller Vorlauf, da der Themenzweig dieselben Commits von Master plus Commits mit der neuen Funktion hat.

Aldo 'xoen' Giambelluca
quelle
31
but this way a new dummy commit is added, if you want to avoid spaghetti-history- Wie ist es schlimm?
ス レ ッ ク
6
Auch das Zusammenführungsflag --no-ff ist sehr, sehr nützlich.
Aldo 'xoen' Giambelluca
3
@ ア レ ッ ク ス wie der Benutzer Sean Schofieldes in einem Kommentar formuliert: "Rebase ist auch nett, weil, sobald Sie Ihre Sachen wieder in Master zusammenführen (was trivial ist, wie bereits beschrieben), Sie es an der" Spitze "Ihres Commit-Verlaufs haben. Auf größer Bei Projekten, in denen Features möglicherweise geschrieben, aber einige Wochen später zusammengeführt werden, möchten Sie sie nicht einfach in den Master einbinden, da sie bereits in der Vergangenheit in den Master "gestopft" wurden. Persönlich mag ich es, Git-Logs zu erstellen und zu sehen Diese neue Funktion befindet sich ganz oben. Beachten Sie, dass die Festschreibungsdaten beibehalten werden - die Basis ändert diese Informationen nicht. "
Adrien Be
4
Ich denke , es trägt zu wiederholen hier - denken Sie daran , dass alle diese Begriffe ( merge, rebase, fast-forward, etc.) beziehen sich auf bestimmte Manipulationen eines gerichteten azyklischen Graphen. Es wird einfacher, über dieses mentale Modell nachzudenken.
Roy Tinker
10
@Aldo Es gibt nichts "Sauberes" oder "Ordentliches" an einer neu basierten Geschichte. Es ist im Allgemeinen schmutzig und meiner Meinung nach schrecklich, weil Sie keine Ahnung haben, was wirklich vor sich ging. Die "sauberste" Git-Geschichte ist die tatsächlich aufgetretene. :)
Marnen Laibow-Koser
269

Um meine von TSamper erwähnte eigene Antwort zu ergänzen ,

  • Eine Rebase ist häufig eine gute Idee vor einer Zusammenführung, da Sie Ydie Arbeit des Zweigs, Bauf dem Sie zusammenführen möchten, in Ihren Zweig integrieren .
    Vor dem Zusammenführen lösen Sie jedoch erneut Konflikte in Ihrem Zweig (dh: "Rebase", wie in "Wiederholen meiner Arbeit in meinem Zweig ab einem aktuellen Punkt aus dem Zweig" B).
    Wenn dies korrekt durchgeführt wurde, erfolgt die nachfolgende Zusammenführung von Ihrem Zweig zu Zweig Bkann schnell vorwärts sein.

  • Eine Zusammenführung wirkt sich direkt auf den Zielzweig aus B, was bedeutet, dass die Zusammenführungen besser trivial sind. Andernfalls Bkann es lange dauern, bis dieser Zweig wieder in einen stabilen Zustand zurückkehrt (Zeit für die Lösung aller Konflikte).


der Punkt der Verschmelzung nach einer Rebase?

In dem Fall, den ich beschreibe, stütze ich mich wieder Bauf meine Filiale, um die Möglichkeit zu haben, meine Arbeit von einem neueren Punkt aus abzuspielen B, aber während ich in meiner Filiale bleibe.
In diesem Fall ist noch eine Zusammenführung erforderlich, um meine "wiedergegebene" Arbeit zu übernehmen B.

Das andere Szenario ( zum Beispiel in Git Ready beschrieben ) besteht darin, Ihre Arbeit direkt Büber eine Rebase einzuspielen (wodurch all Ihre netten Commits erhalten bleiben oder Sie sogar die Möglichkeit haben, sie über eine interaktive Rebase neu zu bestellen).
In diesem Fall (wenn Sie in der B-Verzweigung neu gründen) haben Sie Recht: Es ist keine weitere Zusammenführung erforderlich:

Ein Git-Baum, der standardmäßig nicht zusammengeführt oder neu basiert wurde

rebase1

wir bekommen durch Umbasierung:

rebase3

In diesem zweiten Szenario geht es darum: Wie kann ich neue Funktionen wieder in den Master integrieren?

Mein Punkt, indem ich das erste Rebase-Szenario beschreibe, ist es, alle daran zu erinnern, dass eine Rebase auch als vorläufiger Schritt dazu verwendet werden kann (nämlich "Neue Funktion zurück in den Master bringen").
Sie können rebase verwenden, um den Master zuerst "in" den Zweig mit den neuen Funktionen zu bringen: Die Rebase spielt Commits mit neuen Funktionen aus dem HEAD masterZweig, aber immer noch im Zweig mit neuen Funktionen wieder, wodurch Ihr Zweigstartpunkt effektiv von einem alten Master-Commit auf verschoben wird HEAD-master.
Auf diese Weise können Sie Konflikte in Ihrem Zweig lösen (dh isoliert, während sich der Master parallel weiterentwickeln kann, wenn Ihre Konfliktlösungsphase zu lange dauert).
Dann können Sie zu Master wechseln und zusammenführen new-feature(oder neu aufbauen new-feature, masterwenn Sie die in Ihrem Programm vorgenommenen Commits beibehalten möchtennew-feature Ast).

Damit:

  • "Rebase vs. Merge" kann als zwei Möglichkeiten angesehen werden, eine Arbeit beispielsweise zu importieren master.
  • "Rebase then Merge" kann jedoch ein gültiger Workflow sein, um Konflikte zunächst isoliert zu lösen und dann Ihre Arbeit wiederherzustellen.
VonC
quelle
17
Das Zusammenführen nach dem erneuten Basieren ist ein trivialer schneller Vorlauf, ohne dass Konflikte gelöst werden müssen.
Obecalp
4
@obelcap: In der Tat ist dies eine Art Idee: Sie nehmen alle Problemkonflikte in Ihrer Umgebung auf (Rebase-Master in Ihrem Zweig mit neuen Funktionen) und führen dann Co-Master durch, führen neue Funktionen zusammen: 1 Pico-Sekunde (schnell-) vorwärts) wenn der Meister keine Entwicklungen hatte
VonC
27
Rebase ist auch nett, denn sobald Sie Ihre Inhalte wieder in Master zusammenführen (was, wie bereits beschrieben, trivial ist), befindet es sich an der "Spitze" Ihres Commit-Verlaufs. Bei größeren Projekten, bei denen Features möglicherweise geschrieben, aber einige Wochen später zusammengeführt werden, möchten Sie sie nicht einfach in den Master einbinden, da sie bereits in der Vergangenheit in den Master "gestopft" wurden. Persönlich mag ich es, Git-Log zu erstellen und diese aktuelle Funktion ganz oben zu sehen. Beachten Sie, dass die Festschreibungsdaten beibehalten werden. Durch die erneute Basis werden diese Informationen nicht geändert.
Sean Schofield
3
@Joe: Im Geiste sagst du "Wiederhole alle meine Änderungen (isoliert in meiner privaten Filiale) über dieser anderen Filiale, aber lass mich in meiner privaten Filiale, sobald die Rebase abgeschlossen ist". Dies ist eine gute Gelegenheit, um die lokale Geschichte zu bereinigen und "Checkpoint Commits", gebrochene Halbierungen und falsche Schuldzuweisungen zu vermeiden. Siehe "Git Workflow": sandofsky.com/blog/git-workflow.html
VonC
4
@scoarescoare Der Schlüssel ist zu sehen, wie Ihre lokalen Änderungen über den neuesten Upstream-Zweig kompatibel sind . Wenn einer Ihrer Commits einen Konflikt einführt, wird er sofort angezeigt. Bei einer Zusammenführung wird nur ein (zusammengeführtes) Commit eingeführt, das viele Konflikte auslösen kann, ohne dass auf einfache Weise festgestellt werden kann, welches unter Ihren lokalen Commits diesen Konflikt hinzugefügt hat. Zusätzlich zu einer saubereren Historie erhalten Sie eine genauere Ansicht der Änderungen, die Sie einführen, Commit by Commit (von der Rebase wiedergegeben), im Gegensatz zu allen Änderungen, die von der Upstream-Verzweigung eingeführt wurden (in einer einzigen Zusammenführung zusammengefasst).
VonC
229

TL; DR

Wenn Sie Zweifel haben, verwenden Sie Zusammenführen.

Kurze Antwort

Die einzigen Unterschiede zwischen einem Rebase und einem Merge sind:

  • Die resultierende Baumstruktur des Verlaufs (im Allgemeinen nur beim Betrachten eines Commit-Diagramms erkennbar) ist unterschiedlich (einer hat Zweige, der andere nicht).
  • Durch das Zusammenführen wird im Allgemeinen ein zusätzliches Commit erstellt (z. B. Knoten im Baum).
  • Durch Zusammenführen und erneutes Basieren werden Konflikte unterschiedlich behandelt. Rebase zeigt Konflikte nacheinander an, bei denen durch Zusammenführen alle gleichzeitig angezeigt werden.

Die kurze Antwort lautet also, Rebase oder Merge auszuwählen, je nachdem, wie Ihr Verlauf aussehen soll .

Lange Antwort

Es gibt einige Faktoren, die Sie bei der Auswahl des zu verwendenden Vorgangs berücksichtigen sollten.

Wird der Zweig, von dem Sie Änderungen erhalten, mit anderen Entwicklern außerhalb Ihres Teams geteilt (z. B. Open Source, öffentlich)?

Wenn ja, nicht neu gründen. Rebase zerstört den Zweig und diese Entwickler haben defekte / inkonsistente Repositorys, sofern sie diese nicht verwenden git pull --rebase. Dies ist ein guter Weg, um andere Entwickler schnell zu verärgern.

Wie kompetent ist Ihr Entwicklungsteam?

Rebase ist eine destruktive Operation. Das heißt, wenn Sie es nicht richtig anwenden, können Sie engagierte Arbeit verlieren und / oder die Konsistenz der Repositorys anderer Entwickler beeinträchtigen.

Ich habe in Teams gearbeitet, in denen die Entwickler alle aus einer Zeit stammten, in der sich Unternehmen engagierte Mitarbeiter für das Verzweigen und Zusammenführen leisten konnten. Diese Entwickler wissen nicht viel über Git und wollen nicht viel wissen. In diesen Teams würde ich nicht riskieren, aus irgendeinem Grund eine Neugründung zu empfehlen.

Stellt der Zweig selbst nützliche Informationen dar?

Einige Teams verwenden das Branch-per-Feature-Modell, bei dem jeder Branch ein Feature (oder einen Bugfix oder ein Sub-Feature usw.) darstellt. In diesem Modell hilft der Branch dabei, Sätze verwandter Commits zu identifizieren. Zum Beispiel kann man ein Feature schnell zurücksetzen, indem man die Zusammenführung dieses Zweigs zurücksetzt (um fair zu sein, ist dies eine seltene Operation). Oder unterscheiden Sie ein Merkmal, indem Sie zwei Zweige vergleichen (häufiger). Rebase würde den Zweig zerstören und dies wäre nicht einfach.

Ich habe auch in Teams gearbeitet, die das Branch-per-Developer-Modell verwendet haben (wir waren alle dort). In diesem Fall übermittelt der Zweig selbst keine zusätzlichen Informationen (das Commit hat bereits den Autor). Es würde nicht schaden, neu zu gründen.

Möchten Sie die Zusammenführung aus irgendeinem Grund rückgängig machen?

Das Zurücksetzen (wie beim Rückgängigmachen) einer Rebase ist im Vergleich zum Zurücksetzen einer Zusammenführung erheblich schwierig und / oder unmöglich (wenn die Rebase Konflikte hatte). Wenn Sie der Meinung sind, dass die Möglichkeit besteht, dass Sie zurückkehren möchten, verwenden Sie Merge.

Arbeitest du in einem team Wenn ja, sind Sie bereit, in diesem Bereich alles oder nichts zu tun?

Rebase-Operationen müssen mit einem entsprechenden gezogen werden git pull --rebase. Wenn Sie alleine arbeiten, können Sie sich möglicherweise merken, welche Sie zum richtigen Zeitpunkt verwenden sollten. Wenn Sie in einem Team arbeiten, ist dies sehr schwer zu koordinieren. Aus diesem Grund empfehlen die meisten Rebase-Workflows die Verwendung von Rebase für alle Zusammenführungen (und git pull --rebasefür alle Pulls).

Verbreitete Mythen

Zusammenführen zerstört die Geschichte (Squashes Commits)

Angenommen, Sie haben die folgende Zusammenführung:

    B -- C
   /      \
  A--------D

Einige Leute werden angeben, dass die Zusammenführung den Festschreibungsverlauf "zerstört", da Sie die wichtigen Festschreibungsnachrichten in B und C verpassen würden, wenn Sie nur das Protokoll des Hauptzweigs (A - D) betrachten würden.

Wenn dies wahr wäre, hätten wir solche Fragen nicht . Grundsätzlich werden B und C angezeigt, sofern Sie nicht ausdrücklich darum bitten, sie nicht zu sehen (mit --first-parent). Dies ist sehr einfach selbst zu versuchen.

Rebase ermöglicht sicherere / einfachere Zusammenführungen

Die beiden Ansätze werden unterschiedlich zusammengeführt, aber es ist nicht klar, dass einer immer besser als der andere ist und möglicherweise vom Entwickler-Workflow abhängt. Wenn ein Entwickler beispielsweise dazu neigt, sich regelmäßig zu verpflichten (z. B. zweimal am Tag, wenn er von der Arbeit nach Hause wechselt), kann es für eine bestimmte Niederlassung viele Festschreibungen geben. Viele dieser Commits sehen möglicherweise nicht wie das Endprodukt aus (ich neige dazu, meinen Ansatz ein- oder zweimal pro Feature umzugestalten). Wenn jemand anderes an einem verwandten Codebereich arbeitete und versuchte, meine Änderungen neu zu definieren, könnte dies eine ziemlich mühsame Operation sein.

Rebase ist cooler / sexier / professioneller

Wenn Sie einen Alias rmfür rm -rf"Zeit sparen" möchten, ist Rebase möglicherweise das Richtige für Sie.

Meine zwei Cent

Ich denke immer, dass ich eines Tages auf ein Szenario stoßen werde, in dem Git Rebase das großartige Tool ist, das das Problem löst. Ähnlich wie ich denke, werde ich auf ein Szenario stoßen, in dem Git Reflog ein großartiges Tool ist, das mein Problem löst. Ich arbeite jetzt seit über fünf Jahren mit Git. Es ist nicht passiert.

Unordentliche Geschichten waren für mich nie wirklich ein Problem. Ich lese meine Commit-Geschichte nicht immer nur wie einen aufregenden Roman. Die meiste Zeit, in der ich eine Geschichte brauche, werde ich sowieso Git-Schuld oder Git-Halbierung verwenden. In diesem Fall ist es für mich tatsächlich nützlich, die Zusammenführung festzuschreiben, denn wenn die Zusammenführung das Problem verursacht hat, sind dies für mich aussagekräftige Informationen.

Update (4/2017)

Ich fühle mich verpflichtet zu erwähnen, dass ich persönlich die Verwendung von Rebase abgeschwächt habe, obwohl mein allgemeiner Rat immer noch besteht. Ich habe in letzter Zeit viel mit dem Angular 2 Material- Projekt interagiert . Sie haben Rebase verwendet, um einen sehr sauberen Commit-Verlauf zu führen. Dadurch konnte ich sehr leicht erkennen, durch welches Commit ein bestimmter Fehler behoben wurde und ob dieses Commit in einer Version enthalten war oder nicht. Es ist ein gutes Beispiel für die korrekte Verwendung von Rebase.

Tempo
quelle
5
Sollte die validierte Antwort sein.
Mik378
Dies ist sicherlich die beste Antwort. Besonders mit der klargestellten Bemerkung im letzten Update. Es hilft dabei, den Git-Verlauf sauber und klar zu halten, aber verwenden Sie ihn sicher.
Zquintana
5
Ich liebe diese Antwort am meisten. Aber: Rebase macht keine "saubere" Geschichte. Es macht eine linearere Geschichte, aber das ist überhaupt nicht dasselbe, denn wer weiß jetzt viel "Dreck", den jedes Commit verbirgt? Der sauberste und klarste Git-Verlauf ist derjenige, der die Verzweigungs- und Festschreibungsintegrität beibehält.
Marnen Laibow-Koser
3
"Allgemeiner Mythos, Sie sehen Commits B und C": Nicht unbedingt !! Sie sehen B und C tatsächlich nur, wenn es sich bei der Zusammenführung um eine Schnellvorlaufzusammenführung handelt, und dies ist nur möglich, wenn keine Konflikte aufgetreten sind. Wenn es Konflikte gibt, erhalten Sie ein einziges Commit! Allerdings: Sie können den Master zuerst mit dem Feature zusammenführen und dort Konflikte lösen. Wenn Sie dann das Feature mit dem Master zusammenführen, erhalten Sie die Commits B und C und ein einzelnes Commit X aus der (ersten) Zusammenführung vom Master mit dem Feature in Ihrem Verlauf.
Jeremy Benks
sehr gute Erklärung! Es muss mehr gestimmt werden!
Dimmits
185

Viele Antworten hier besagen, dass durch das Zusammenführen alle Ihre Commits zu einem werden. Daher wird empfohlen, Rebase zu verwenden, um Ihre Commits beizubehalten. Das ist falsch. Und eine schlechte Idee, wenn Sie Ihre Commits bereits vorangetrieben haben .

Durch das Zusammenführen werden Ihre Commits nicht ausgelöscht. Zusammenführen bewahrt die Geschichte! (Schauen Sie sich nur gitk an.) Rebase schreibt den Verlauf neu, was eine schlechte Sache ist, nachdem Sie ihn gepusht haben.

Verwenden Sie Merge - nicht Rebase, wenn Sie bereits gepusht haben.

Hier ist die Einstellung von Linus (Autor von Git) (jetzt auf meinem eigenen Blog gehostet, wie von der Wayback-Maschine wiederhergestellt ). Es ist eine wirklich gute Lektüre.

Oder Sie können meine eigene Version der gleichen Idee unten lesen.

Wiederherstellen eines Zweigs auf dem Master:

  • liefert eine falsche Vorstellung davon, wie Commits erstellt wurden
  • verschmutzt den Meister mit einer Reihe von Zwischenverpflichtungen, die möglicherweise nicht gut getestet wurden
  • könnte tatsächlich Build-Unterbrechungen für diese Zwischen-Commits einführen, da Änderungen vorgenommen wurden, die zwischen dem Zeitpunkt der Erstellung des ursprünglichen Themenzweigs und dem Zeitpunkt der Neubasierung zu meistern waren.
  • macht es schwierig, gute Plätze im Master zum Auschecken zu finden.
  • Bewirkt, dass die Zeitstempel für Commits nicht mit ihrer chronologischen Reihenfolge im Baum übereinstimmen. Sie würden also sehen, dass Commit A im Master vor Commit B steht, Commit B jedoch zuerst erstellt wurde. (Was?!)
  • Erzeugt mehr Konflikte, da einzelne Commits im Themenbereich jeweils Zusammenführungskonflikte beinhalten können, die einzeln gelöst werden müssen (was in der Geschichte weiter darüber liegt, was bei jedem Commit passiert ist).
  • ist eine Neufassung der Geschichte. Wenn der Zweig, der neu basiert, an eine andere Stelle verschoben wurde (für andere als Sie selbst freigegeben), haben Sie alle anderen, die diesen Zweig haben, vermasselt, seit Sie den Verlauf neu geschrieben haben.

Im Gegensatz dazu führt das Zusammenführen eines Themenzweigs zum Master Folgendes zusammen:

  • Bewahrt den Verlauf der Erstellung von Themenzweigen, einschließlich aller Zusammenführungen vom Master zum Themenzweig, um ihn auf dem neuesten Stand zu halten. Sie erhalten wirklich eine genaue Vorstellung davon, mit welchem ​​Code der Entwickler beim Erstellen gearbeitet hat.
  • master ist ein Zweig, der hauptsächlich aus Zusammenführungen besteht, und jeder dieser Zusammenführungs-Commits sind in der Regel „gute Punkte“ in der Geschichte, die sicher überprüft werden können, da dort der Themenzweig zur Integration bereit war.
  • Alle einzelnen Commits des Themenzweigs bleiben erhalten, einschließlich der Tatsache, dass sie sich in einem Themenzweig befanden. Daher ist es selbstverständlich, diese Änderungen zu isolieren, und Sie können bei Bedarf einen Drilldown durchführen.
  • Zusammenführungskonflikte müssen nur einmal (zum Zeitpunkt der Zusammenführung) gelöst werden, sodass zwischenzeitliche Festschreibungsänderungen, die im Themenzweig vorgenommen wurden, nicht unabhängig voneinander gelöst werden müssen.
  • kann mehrfach reibungslos durchgeführt werden. Wenn Sie Ihren Themenzweig regelmäßig integrieren, um ihn zu meistern, können die Benutzer weiterhin auf dem Themenzweig aufbauen und er kann weiterhin unabhängig zusammengeführt werden.
Andrew Arnott
quelle
3
Außerdem verfügt Git Merge über die Option "--no-ff" (kein schneller Vorlauf), mit der Sie alle durch eine bestimmte Zusammenführung eingeführten Änderungen ganz einfach rückgängig machen können.
Tiago
3
Um es klarer zu machen: Sie beziehen sich auf die Situation "wann immer Sie bereits gepusht haben" - dies sollte fett sein. Der Link zu Linus Post ist großartig, übrigens, verdeutlicht es.
Honzajde
2
Aber ist es nicht empfehlenswert, vom Master in Ihren Themenzweig zu "aktualisieren", bevor Sie den Themenzweig über PR in den Master zusammenführen (um Konflikte in Ihrem Zweig zu lösen, nicht im Master)? Wir machen das so, also haben die meisten Themenzweige als letztes Commit "Zweigmaster in Thema zusammenführen ...", aber hier wird dies als "Merkmal" der Neugründung aufgeführt und niemand erwähnt es zum Zusammenführen ...?
Probleme von
2
@AndrewArnott "Die meisten Themenzweige sollten konfliktfrei in ihren Zielzweigen zusammengeführt werden können." Wie sollte das möglich sein, wenn 20 Entwickler an 30 Zweigen arbeiten? Während Sie an Ihrem arbeiten, wird es Zusammenführungen geben. Natürlich müssen Sie Ihren Themenzweig vom Ziel aus aktualisieren, bevor Sie eine PR erstellen ... nein?
ProblemsOfSumit
3
Normalerweise nicht @Sumit. Git kann beide Richtungen problemlos zusammenführen, obwohl Änderungen an einem oder beiden Zweigen vorgenommen wurden. Nur wenn dieselben Codezeilen (oder sehr nahe) in zwei Zweigen geändert werden, treten Konflikte auf. Wenn dies in einem Team häufig vorkommt, sollte das Team überdenken, wie es die Arbeit verteilt, da die Lösung von Konflikten eine Steuer darstellt und sie verlangsamt.
Andrew Arnott
76

TLDR: Es kommt darauf an, was am wichtigsten ist - eine ordentliche Geschichte oder eine wahre Darstellung der Entwicklungssequenz

Wenn ein aufgeräumter Verlauf am wichtigsten ist, würden Sie zuerst die Basis neu erstellen und dann Ihre Änderungen zusammenführen, damit genau klar ist, um welchen neuen Code es sich handelt. Wenn Sie Ihren Zweig bereits gepusht haben, sollten Sie ihn nicht neu gründen, es sei denn, Sie können mit den Konsequenzen umgehen.

Wenn die wahre Darstellung der Sequenz am wichtigsten ist, würden Sie ohne erneute Basierung zusammenführen.

Zusammenführen bedeutet: Erstellen Sie ein einzelnes neues Commit, das meine Änderungen mit dem Ziel zusammenführt. Hinweis: Dieses neue Commit hat zwei übergeordnete Elemente - das letzte Commit aus Ihrer Commit-Zeichenfolge und das letzte Commit des anderen Zweigs, den Sie zusammenführen.

Rebase bedeutet: Erstellen Sie eine ganz neue Reihe von Commits, wobei Sie meine aktuellen Commits als Hinweise verwenden. Mit anderen Worten, berechnen Sie, wie meine Änderungen ausgesehen hätten, wenn ich sie von dem Punkt an vorgenommen hätte, an dem ich sie neu erstellt habe. Nach dem Rebase müssen Sie möglicherweise Ihre Änderungen erneut testen, und während des Rebases treten möglicherweise einige Konflikte auf.

Warum sollten Sie vor diesem Hintergrund eine Rebase erstellen? Nur um die Entwicklungsgeschichte klar zu halten. Angenommen, Sie arbeiten an Feature X, und wenn Sie fertig sind, führen Sie Ihre Änderungen in zusammen. Das Ziel verfügt nun über ein einzelnes Commit, das etwas in Anlehnung an "Feature X hinzugefügt" aussagt. Anstatt zu verschmelzen, würde der Zielentwicklungsverlauf jetzt alle einzelnen Commits in einer einzigen logischen Abfolge enthalten, wenn Sie neu aufbauen und dann zusammenführen. Dies erleichtert das spätere Überprüfen von Änderungen erheblich. Stellen Sie sich vor, wie schwierig es wäre, den Entwicklungsverlauf zu überprüfen, wenn 50 Entwickler ständig verschiedene Funktionen zusammenführen würden.

Das heißt, wenn Sie den Zweig, an dem Sie arbeiten, bereits in den Upstream verschoben haben, sollten Sie nicht neu starten, sondern stattdessen zusammenführen. Für Zweige, die nicht vorgelagert wurden, neu starten, testen und zusammenführen.

Eine andere Möglichkeit, die Sie möglicherweise neu festlegen möchten, besteht darin, Commits aus Ihrem Zweig zu entfernen, bevor Sie sie in den Upstream verschieben. Beispiel: Commits, die frühzeitig Debugging-Code einführen, und andere Commits, die diesen Code weiter bereinigen. Die einzige Möglichkeit, dies zu tun, besteht darin, eine interaktive Rebase durchzuführen:git rebase -i <branch/commit/tag>

UPDATE: Sie möchten Rebase auch verwenden, wenn Sie Git als Schnittstelle zu einem Versionskontrollsystem verwenden, das keinen nichtlinearen Verlauf unterstützt ( z. B. Subversion ). Bei Verwendung der git-svn-Brücke ist es sehr wichtig, dass die Änderungen, die Sie wieder in Subversion zusammenführen, eine sequenzielle Liste von Änderungen sind, die zusätzlich zu den letzten Änderungen im Trunk angezeigt werden. Es gibt nur zwei Möglichkeiten, dies zu tun: (1) Erstellen Sie die Änderungen manuell neu und (2) Verwenden Sie den Befehl rebase, der viel schneller ist.

UPDATE 2: Eine weitere Möglichkeit, sich eine Rebase vorzustellen, besteht darin, eine Art Zuordnung von Ihrem Entwicklungsstil zu dem Stil zu ermöglichen, der in dem Repository akzeptiert wird, für das Sie sich verpflichten. Nehmen wir an, Sie möchten sich in kleinen Stücken engagieren. Sie haben ein Commit, um einen Tippfehler zu beheben, ein Commit, um nicht verwendeten Code zu entfernen und so weiter. Bis Sie fertig sind, was Sie tun müssen, haben Sie eine lange Reihe von Commits. Nehmen wir nun an, das Repository, für das Sie sich verpflichten, fördert große Commits. Für die von Ihnen ausgeführte Arbeit würde man also ein oder vielleicht zwei Commits erwarten. Wie nehmen Sie Ihre Commit-Zeichenfolge und komprimieren sie auf das, was erwartet wird? Sie würden eine interaktive Rebase verwenden und Ihre winzigen Commits in weniger größere Teile zerlegen. Das gleiche gilt, wenn das Gegenteil erforderlich war - wenn Ihr Stil ein paar große Commits war, Das Repository verlangte jedoch lange Reihen kleiner Commits. Sie würden auch eine Rebase verwenden, um dies zu tun. Wenn Sie stattdessen zusammengeführt haben, haben Sie jetzt Ihren Commit-Stil auf das Haupt-Repository übertragen. Wenn es viele Entwickler gibt, können Sie sich vorstellen, wie schwierig es wäre, nach einiger Zeit einem Verlauf mit mehreren verschiedenen Festschreibungsstilen zu folgen.

UPDATE 3: Does one still need to merge after a successful rebase?Ja, das tust du. Der Grund ist, dass eine Rebase im Wesentlichen eine "Verschiebung" von Commits beinhaltet. Wie ich oben gesagt habe, werden diese Commits berechnet. Wenn Sie jedoch 14 Commits ab dem Zeitpunkt der Verzweigung hatten und davon ausgehen, dass bei Ihrer Rebase nichts schief geht, sind Sie 14 Commits voraus (von dem Punkt, auf den Sie zurückgreifen) Der Rebase ist fertig. Sie hatten eine Niederlassung vor einer Rebase. Sie haben danach einen Zweig gleicher Länge. Sie müssen noch zusammenführen, bevor Sie Ihre Änderungen veröffentlichen. Mit anderen Worten, starten Sie die Basis so oft neu, wie Sie möchten (erneut nur, wenn Sie Ihre Änderungen nicht in den Upstream verschoben haben). Zusammenführen erst nach dem Rebase.

Carl
quelle
1
Eine Zusammenführung mit dem Master kann zu einem schnellen Vorlauf führen. In einem Feature-Zweig kann es einige Commits geben, die kleinere Fehler aufweisen oder nicht einmal kompiliert werden. Wenn Sie nur Unit-Tests in einem Feature-Zweig durchführen, werden einige Fehler bei der Integration behoben. Vor dem Zusammenführen mit dem Master sind Integrationstests erforderlich, die einige Fehler aufzeigen können. Wenn diese behoben sind, könnte die Funktion integriert werden. Da Sie keinen fehlerhaften Code an den Master übergeben möchten, scheint eine Neubasis erforderlich zu sein, um einen schnellen Vorlauf mit allen Festschreibungen zu verhindern.
mbx
1
@mbx git mergeunterstützt die --no-ffOption, die es zwingt, ein Merge-Commit durchzuführen .
Gavin S. Yancey
63

Während das Zusammenführen definitiv die einfachste und häufigste Art ist, Änderungen zu integrieren, ist es nicht die einzige: Rebase ist ein alternatives Integrationsmittel.

Merge ein bisschen besser verstehen

Wenn Git eine Zusammenführung durchführt, sucht es nach drei Commits:

  • (1) Commit für gemeinsame Vorfahren. Wenn Sie die Historie zweier Zweige in einem Projekt verfolgen, haben diese immer mindestens ein Commit gemeinsam: Zu diesem Zeitpunkt hatten beide Zweige den gleichen Inhalt und entwickelten sich dann unterschiedlich.
  • (2) + (3) Endpunkte jedes Zweigs. Ziel einer Integration ist es, die aktuellen Zustände zweier Branchen zu kombinieren. Daher sind ihre jeweiligen letzten Überarbeitungen von besonderem Interesse. Die Kombination dieser drei Commits führt zu der Integration, die wir anstreben.

Fast-Forward oder Merge Commit

In sehr einfachen Fällen hat einer der beiden Zweige seit dem Verzweigen keine neuen Commits mehr - sein letzter Commit ist immer noch der gemeinsame Vorfahr.

Geben Sie hier die Bildbeschreibung ein

In diesem Fall ist die Durchführung der Integration ganz einfach: Git kann einfach alle Commits des anderen Zweigs zusätzlich zum Commit des gemeinsamen Vorfahren hinzufügen. In Git wird diese einfachste Form der Integration als "Schnellvorlauf" -Zusammenführung bezeichnet. Beide Zweige teilen dann genau die gleiche Geschichte.

Geben Sie hier die Bildbeschreibung ein

In vielen Fällen haben sich jedoch beide Zweige einzeln weiterentwickelt.

Geben Sie hier die Bildbeschreibung ein

Um eine Integration vorzunehmen, muss Git ein neues Commit erstellen, das die Unterschiede zwischen ihnen enthält - das Merge-Commit.

Geben Sie hier die Bildbeschreibung ein

Human Commits & Merge Commits

Normalerweise wird ein Commit von einem Menschen sorgfältig erstellt. Es ist eine sinnvolle Einheit, die nur verwandte Änderungen umschließt und sie mit einem Kommentar kommentiert.

Ein Merge-Commit ist etwas anders: Anstatt von einem Entwickler erstellt zu werden, wird es automatisch von Git erstellt. Und anstatt eine Reihe verwandter Änderungen zu verpacken, besteht der Zweck darin, zwei Zweige wie einen Knoten zu verbinden. Wenn Sie einen Zusammenführungsvorgang später verstehen möchten, müssen Sie sich den Verlauf beider Zweige und das entsprechende Festschreibungsdiagramm ansehen.

Integration in Rebase

Einige Leute verzichten lieber auf solche automatischen Zusammenführungs-Commits. Stattdessen möchten sie, dass die Geschichte des Projekts so aussieht, als hätte sie sich in einer einzigen geraden Linie entwickelt. Es gibt keinen Hinweis darauf, dass es irgendwann in mehrere Zweige aufgeteilt wurde.

Geben Sie hier die Bildbeschreibung ein

Lassen Sie uns Schritt für Schritt durch eine Rebase-Operation gehen. Das Szenario ist das gleiche wie in den vorherigen Beispielen: Wir möchten die Änderungen von Zweig B in Zweig A integrieren, aber jetzt Rebase verwenden.

Geben Sie hier die Bildbeschreibung ein

Wir werden dies in drei Schritten tun

  1. git rebase branch-A // Synchronises the history with branch-A
  2. git checkout branch-A // Change the current branch to branch-A
  3. git merge branch-B // Merge/take the changes from branch-B to branch-A

Zuerst "macht" Git alle Commits in Zweig A rückgängig, die aufgetreten sind, nachdem die Zeilen verzweigt wurden (nach dem Commit des gemeinsamen Vorfahren). Natürlich werden sie nicht verworfen. Stattdessen können Sie sich diese Commits als "vorübergehend gespeichert" vorstellen.

Geben Sie hier die Bildbeschreibung ein

Als nächstes werden die Commits aus Zweig B angewendet, die wir integrieren möchten. Zu diesem Zeitpunkt sehen beide Zweige genau gleich aus.

Geben Sie hier die Bildbeschreibung ein

Im letzten Schritt werden die neuen Commits für Zweig A nun erneut angewendet - jedoch an einer neuen Position zusätzlich zu den integrierten Commits von Zweig B (sie werden neu basiert).

Das Ergebnis sieht so aus, als ob die Entwicklung in einer geraden Linie stattgefunden hätte. Anstelle eines Zusammenführungs-Commits, das alle kombinierten Änderungen enthält, wurde die ursprüngliche Commit-Struktur beibehalten.

Geben Sie hier die Bildbeschreibung ein

Schließlich erhalten Sie einen sauberen Zweig Zweig-A ohne unerwünschte und automatisch generierte Commits.

Hinweis: Aus dem fantastischen Beitrag von git-tower. Die Nachteile von rebaseist auch eine gute Lektüre im selben Beitrag.

Abdullah Khan
quelle
+1 für sehr coole Diagramme. Ich wollte schon immer in der Lage sein, ein Git-Flow-Beispiel auf ähnliche Weise ohne Glück zu veranschaulichen.
Mikayil Abdullayev
60

Vor dem Zusammenführen / Wiederherstellen:

A <- B <- C    [master]
^
 \
  D <- E       [branch]

Nachher git merge master:

A <- B <- C
^         ^
 \         \
  D <- E <- F

Nachher git rebase master:

A <- B <- C <- D' <- E'

(A, B, C, D, E und F sind Commits)

Dieses Beispiel und viele weitere gut illustrierte Informationen zu Git finden Sie im Git The Basics Tutorial .

Guybrush
quelle
30

Dieser Satz versteht es:

Im Allgemeinen besteht der Weg, um das Beste aus beiden Welten zu erhalten, darin, lokale Änderungen, die Sie vorgenommen, aber noch nicht geteilt haben, neu zu begründen, bevor Sie sie veröffentlichen, um Ihre Geschichte zu bereinigen, aber niemals etwas neu zu begründen, das Sie irgendwo verschoben haben .

Quelle: 3.6 Git Branching - Rebasing, Rebase vs. Merge

Joaquin Sargiotto
quelle
25

Diese Antwort orientiert sich weitgehend an Git Flow . Die Tabellen wurden mit dem netten ASCII-Tabellengenerator und die Verlaufsbäume mit diesem wunderbaren Befehl ( Alias als git lg) generiert :

git log --graph --abbrev-commit --decorate --date=format:'%Y-%m-%d %H:%M:%S' --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%ad%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'

Die Tabellen sind in umgekehrter chronologischer Reihenfolge angeordnet, um mit den Verlaufsbäumen besser übereinzustimmen. Siehe auch den Unterschied zwischen git mergeund git merge --no-ffzuerst (Sie möchten ihn normalerweise verwenden, git merge --no-ffda Ihre Geschichte dadurch näher an der Realität erscheint):

git merge

Befehle:

Time          Branch "develop"             Branch "features/foo"
------- ------------------------------ -------------------------------
15:04   git merge features/foo
15:03                                  git commit -m "Third commit"
15:02                                  git commit -m "Second commit"
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Ergebnis:

* 142a74a - YYYY-MM-DD 15:03:00 (XX minutes ago) (HEAD -> develop, features/foo)
|           Third commit - Christophe
* 00d848c - YYYY-MM-DD 15:02:00 (XX minutes ago)
|           Second commit - Christophe
* 298e9c5 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git merge --no-ff

Befehle:

Time           Branch "develop"              Branch "features/foo"
------- -------------------------------- -------------------------------
15:04   git merge --no-ff features/foo
15:03                                    git commit -m "Third commit"
15:02                                    git commit -m "Second commit"
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Ergebnis:

*   1140d8c - YYYY-MM-DD 15:04:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/foo' - Christophe
| * 69f4a7a - YYYY-MM-DD 15:03:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 2973183 - YYYY-MM-DD 15:02:00 (XX minutes ago)
|/            Second commit - Christophe
* c173472 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git merge vs. git rebase

Erster Punkt: Führen Sie Features immer zu Entwickeln zusammen, und stützen Sie die Entwicklung niemals auf Features . Dies ist eine Folge der Goldenen Regel der Wiederaufnahme :

Die goldene Regel git rebaseist, es niemals in öffentlichen Zweigen zu verwenden.

Mit anderen Worten :

Stellen Sie niemals etwas zurück, das Sie irgendwohin geschoben haben.

Ich würde persönlich hinzufügen: Es sei denn, es handelt sich um einen Feature-Zweig UND Sie und Ihr Team sind sich der Konsequenzen bewusst .

Die Frage von git mergevs git rebasegilt also fast nur für die Feature-Zweige (in den folgenden Beispielen --no-ffwurde immer beim Zusammenführen verwendet). Da ich nicht sicher bin, ob es eine bessere Lösung gibt (es gibt eine Debatte ), werde ich nur angeben, wie sich beide Befehle verhalten. In meinem Fall bevorzuge ich die Verwendung, git rebaseda dadurch ein schönerer Verlaufsbaum erstellt wird :)

Zwischen Feature-Zweigen

git merge

Befehle:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- --------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Ergebnis:

*   c0a3b89 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 37e933e - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| *   eb5e657 - YYYY-MM-DD 15:07:00 (XX minutes ago)
| |\            Merge branch 'features/foo' into features/bar - Christophe
| * | 2e4086f - YYYY-MM-DD 15:06:00 (XX minutes ago)
| | |           Fifth commit - Christophe
| * | 31e3a60 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| | |           Fourth commit - Christophe
* | |   98b439f - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \ \            Merge branch 'features/foo' - Christophe
| |/ /
|/| /
| |/
| * 6579c9c - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 3f41d96 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* 14edc68 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git rebase

Befehle:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git rebase features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Ergebnis:

*   7a99663 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 708347a - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * 949ae73 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * 108b4c7 - YYYY-MM-DD 15:05:00 (XX minutes ago)
| |           Fourth commit - Christophe
* |   189de99 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| |/
| * 26835a0 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * a61dd08 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* ae6f5fc - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

Von developzu einem Feature-Zweig

git merge

Befehle:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09                                                                    git commit -m "Sixth commit"
15:08                                                                    git merge --no-ff develop
15:07   git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Ergebnis:

*   9e6311a - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 3ce9128 - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| *   d0cd244 - YYYY-MM-DD 15:08:00 (XX minutes ago)
| |\            Merge branch 'develop' into features/bar - Christophe
| |/
|/|
* |   5bd5f70 - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| * | 4ef3853 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| | |           Third commit - Christophe
| * | 3227253 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/ /            Second commit - Christophe
| * b5543a2 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * 5e84b79 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/            Fourth commit - Christophe
* 2da6d8d - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git rebase

Befehle:

Time           Branch "develop"              Branch "features/foo"           Branch "features/bar"
------- -------------------------------- ------------------------------- -------------------------------
15:10   git merge --no-ff features/bar
15:09                                                                    git commit -m "Sixth commit"
15:08                                                                    git rebase develop
15:07   git merge --no-ff features/foo
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Ergebnis:

*   b0f6752 - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 621ad5b - YYYY-MM-DD 15:09:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * 9cb1a16 - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * b8ddd19 - YYYY-MM-DD 15:05:00 (XX minutes ago)
|/            Fourth commit - Christophe
*   856433e - YYYY-MM-DD 15:07:00 (XX minutes ago)
|\            Merge branch 'features/foo' - Christophe
| * 694ac81 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * 5fd94d3 - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* d01d589 - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

Randnotizen

git cherry-pick

Wenn Sie nur ein bestimmtes Commit benötigen, git cherry-pickist dies eine -xgute Lösung (die Option hängt eine Zeile mit der Aufschrift " (Kirsche aus Commit ...) " an den ursprünglichen Commit-Nachrichtentext an. Daher ist es normalerweise eine gute Idee, sie zu verwenden - um git log <commit_sha1>zu sehen es):

Befehle:

Time           Branch "develop"              Branch "features/foo"                Branch "features/bar"
------- -------------------------------- ------------------------------- -----------------------------------------
15:10   git merge --no-ff features/bar
15:09   git merge --no-ff features/foo
15:08                                                                    git commit -m "Sixth commit"
15:07                                                                    git cherry-pick -x <second_commit_sha1>
15:06                                                                    git commit -m "Fifth commit"
15:05                                                                    git commit -m "Fourth commit"
15:04                                    git commit -m "Third commit"
15:03                                    git commit -m "Second commit"
15:02   git checkout -b features/bar
15:01   git checkout -b features/foo
15:00   git commit -m "First commit"

Ergebnis:

*   50839cd - YYYY-MM-DD 15:10:00 (XX minutes ago) (HEAD -> develop)
|\            Merge branch 'features/bar' - Christophe
| * 0cda99f - YYYY-MM-DD 15:08:00 (XX minutes ago) (features/bar)
| |           Sixth commit - Christophe
| * f7d6c47 - YYYY-MM-DD 15:03:00 (XX minutes ago)
| |           Second commit - Christophe
| * dd7d05a - YYYY-MM-DD 15:06:00 (XX minutes ago)
| |           Fifth commit - Christophe
| * d0d759b - YYYY-MM-DD 15:05:00 (XX minutes ago)
| |           Fourth commit - Christophe
* |   1a397c5 - YYYY-MM-DD 15:09:00 (XX minutes ago)
|\ \            Merge branch 'features/foo' - Christophe
| |/
|/|
| * 0600a72 - YYYY-MM-DD 15:04:00 (XX minutes ago) (features/foo)
| |           Third commit - Christophe
| * f4c127a - YYYY-MM-DD 15:03:00 (XX minutes ago)
|/            Second commit - Christophe
* 0cf894c - YYYY-MM-DD 15:00:00 (XX minutes ago)
            First commit - Christophe

git pull --rebase

Ich bin mir nicht sicher, ob ich es besser erklären kann als Derek Gourlay ... Grundsätzlich git pull --rebaseanstelle von git pull:) Was jedoch im Artikel fehlt, ist, dass Sie es standardmäßig aktivieren können :

git config --global pull.rebase true

git rerere

Wieder schön hier erklärt . Einfach ausgedrückt, wenn Sie es aktivieren, müssen Sie denselben Konflikt nicht mehr mehrmals lösen.

sp00m
quelle
15

Das Pro Git- Buch hat eine wirklich gute Erklärung auf der Rebasing-Seite .

Grundsätzlich werden bei einer Zusammenführung zwei Commits ausgeführt und kombiniert.

Eine Rebase geht an den gemeinsamen Vorfahren der beiden und wendet die Änderungen schrittweise übereinander an. Dies sorgt für eine "sauberere" und linearere Geschichte.

Wenn Sie jedoch die Basis neu festlegen, geben Sie frühere Commits auf und erstellen neue. Sie sollten also niemals ein öffentliches Repository neu gründen. Die anderen Leute, die am Repository arbeiten, werden dich hassen.

Allein aus diesem Grund verschmelze ich fast ausschließlich. In 99% der Fälle unterscheiden sich meine Filialen nicht so stark. Wenn es also zu Konflikten kommt, gibt es nur ein oder zwei Stellen.

xero
quelle
1
Zusammenführungen kombinieren keine Commits - das würde die Geschichte neu schreiben. Rebase macht das.
Kellyfj
4

Git Rebase wird verwendet, um die Verzweigungspfade in der Verlaufsreihenfolge und der Repository-Struktur linear zu gestalten.

Es wird auch verwendet, um die von Ihnen erstellten Zweige privat zu halten, da nach dem erneuten Basieren und Übertragen der Änderungen auf den Server beim Löschen Ihres Zweigs keine Hinweise auf einen Zweig angezeigt werden, an dem Sie gearbeitet haben. Ihre Niederlassung ist jetzt Ihr lokales Anliegen.

Nach dem Rebase wird auch ein zusätzliches Commit entfernt, mit dem wir überprüft haben, ob wir eine normale Zusammenführung durchführen.

Und ja, nach einer erfolgreichen Rebase muss noch eine Zusammenführung durchgeführt werden, da der Rebase-Befehl Ihre Arbeit nur auf den Zweig legt, den Sie während der Rebase erwähnt haben, z. B. Master, und das erste Commit Ihres Zweigs als direkter Nachkomme des Master-Zweigs ausführt . Dies bedeutet, dass wir jetzt einen schnellen Vorlauf zusammenführen können, um Änderungen von diesem Zweig in den Hauptzweig zu bringen.

cvibha
quelle
4

Wenn Sie nur ein Entwickler sind, können Sie Rebase anstelle von Merge verwenden , um einen eindeutigen Verlauf zu erhalten

Geben Sie hier die Bildbeschreibung ein

yoAlex5
quelle
3

Einige praktische Beispiele, die etwas mit der groß angelegten Entwicklung zu tun haben, wo Gerrit für die Überprüfung und die Integration der Bereitstellung verwendet wird:

Ich füge zusammen, wenn ich meinen Feature-Zweig zu einem neuen Remote-Master erhebe. Dies gibt nur minimale Auftriebsarbeit und es ist einfach, den Verlauf der Feature-Entwicklung beispielsweise in gitk zu verfolgen .

git fetch
git checkout origin/my_feature
git merge origin/master
git commit
git push origin HEAD:refs/for/my_feature

Ich füge zusammen, wenn ich ein Liefer-Commit vorbereite.

git fetch
git checkout origin/master
git merge --squash origin/my_feature
git commit
git push origin HEAD:refs/for/master

Ich stütze mich, wenn mein Delivery Commit aus irgendeinem Grund nicht integriert werden kann, und muss es auf einen neuen Remote-Master aktualisieren.

git fetch
git fetch <gerrit link>
git checkout FETCH_HEAD
git rebase origin/master
git push origin HEAD:refs/for/master
Martin G.
quelle
3

Es wurde oft erklärt, was Rebase und was Merge ist, aber wann sollten Sie was verwenden?

Wann sollten Sie Rebase verwenden?

  • Wenn Sie den Zweig nicht gedrückt haben / niemand anderes daran arbeitet
  • Sie wollen die ganze Geschichte
  • Sie möchten alle automatisch generierten "zusammengeführten" Festschreibungsnachrichten vermeiden

Als Git Rebase ändert sich der Verlauf. Daher sollten Sie es nicht verwenden, wenn jemand anderes am selben Zweig arbeitet / wenn Sie es gepusht haben. Wenn Sie jedoch einen lokalen Zweig haben, können Sie einen Merge-Rebase-Master durchführen, bevor Sie Ihren Zweig wieder mit dem Master zusammenführen, um einen saubereren Verlauf zu erhalten. Wenn Sie dies tun, wird nach dem Zusammenführen in den Hauptzweig nicht sichtbar, dass Sie einen Zweig im Hauptzweig verwendet haben. Der Verlauf ist "sauberer", da Sie nicht automatisch "Zusammengeführt ..." generiert haben, aber immer noch den vollständige Historie in Ihrer Hauptniederlassung, ohne automatisch "zusammengeführte .." Commits generiert zu haben.

Stellen Sie jedoch sicher, dass Sie verwenden git merge feature-branch --ff-only, um sicherzustellen, dass keine Konflikte beim Erstellen eines einzelnen Commits auftreten, wenn Sie Ihre Funktion wieder mit main zusammenführen. Dies ist interessant, wenn Sie für jede Aufgabe, an der Sie arbeiten, Feature-Zweige verwenden, während Sie den Verlauf des Feature-Zweigs abrufen, jedoch kein "zusammengeführtes" Commit

Ein zweites Szenario wäre, wenn Sie von einem Zweig verzweigen und wissen möchten, was sich im Hauptzweig geändert hat. Rebase gibt Ihnen die Informationen, da sie jedes einzelne Commit enthalten.

Wann sollten Sie Merge verwenden?

  • Wenn Sie den Zweig gedrückt haben, arbeiten auch andere daran
  • Sie brauchen nicht die vollständige Geschichte
  • Einfach zusammenführen ist gut genug für Sie

Wenn Sie nicht den gesamten Verlauf eines Feature-Zweigs in Ihrem Hauptzweig benötigen oder möchten oder wenn andere an demselben Zweig arbeiten / Sie ihn verschoben haben. Wenn Sie den Verlauf weiterhin haben möchten, führen Sie einfach den Master in den Feature-Zweig ein, bevor Sie den Feature-Zweig in den Master zusammenführen. Dies führt zu einer Schnellvorlaufzusammenführung, bei der Sie den Verlauf des Feature-Zweigs in Ihrem Master haben (einschließlich des Zusammenführungs-Commits, das sich in Ihrem Feature-Zweig befand, weil Sie den Master darin zusammengeführt haben).

Jeremy Benks
quelle
-4

Wann benutze ich git rebase? Fast nie, weil es die Geschichte neu schreibt. git mergeist fast immer die bevorzugte Wahl, da sie berücksichtigt, was tatsächlich in Ihrem Projekt passiert ist.

Marnen Laibow-Koser
quelle
1
@benjaminhull Danke! - außer ich hoffe, meine Antwort basiert auf Fakten. IMHO Meinung hat wenig Platz in dieser Art von Dingen: Es ist eine Tatsache, dass der Verlust Ihrer tatsächlichen Geschichte das Leben später schwieriger macht.
Marnen Laibow-Koser
1
Zustimmen. Das Zusammenführen wird niemals zu einer beschädigten Historie usw. führen (wenn Sie Ihre Push-Commits neu begründen)
Surfrider