it-swarm.com.de

Wann verwenden Sie git rebase anstelle von git merge?

Wann wird die Verwendung von git rebase vs. git merge empfohlen?

Muss ich nach einer erfolgreichen Rebase noch zusammenführen?

1334
Coocoo4Cocoa

Kurze Version

  • Merge übernimmt alle Änderungen in einem Zweig und führt sie in einem Commit in einen anderen Zweig ein.
  • Rebase sagt, ich möchte, dass der Punkt, an dem ich verzweigt wurde, zu einem neuen Ausgangspunkt wechselt

Wann verwendest du eines?

Verschmelzen

  • Angenommen, Sie haben einen Zweig erstellt, um ein einzelnes Feature zu entwickeln. Wenn Sie diese Änderungen wieder in den Master-Zustand bringen möchten, möchten Sie wahrscheinlich merge (es ist Ihnen egal, ob Sie alle Interim-Commits beibehalten). 

Rebase

  • Ein zweites Szenario wäre, wenn Sie mit der Entwicklung beginnen und ein anderer Entwickler eine nicht zusammenhängende Änderung vorgenommen hat. Wahrscheinlich möchten Sie ziehen und dann rebase verwenden, um Ihre Änderungen von der aktuellen Version aus dem Repo zu stützen.
1020
Rob Di Marco

Mit rebase ist es einfach, einen anderen Zweig als neuen base für Ihre Arbeit zu verwenden.

Wenn Sie beispielsweise über einen Zweig master verfügen und einen Zweig erstellen, um ein neues Feature zu implementieren, nennen Sie es cool-feature.

Nun möchten Sie an einem bestimmten Punkt die neue Funktion hinzufügen, die Sie im Zweig master implementiert haben. Sie könnten einfach zu master wechseln und den cool-feature-Zweig zusammenführen:

$ git checkout master
$ git merge cool-feature

auf diese Weise wird jedoch ein neues Dummy-Commit hinzugefügt. Wenn Sie die Spaghetti-Geschichte vermeiden möchten, können Sie rebase :

$ git checkout cool-feature
$ git rebase master

und dann in master zusammenführen:

$ git checkout master
$ git merge cool-feature

Da diesmal der Themenzweig die gleichen Commits von master und die Commits mit der neuen Funktion hat, ist die Zusammenführung nur ein schneller Vorlauf.

meine eigene Antwort ergänzen erwähnt von TSamper

  • eine Rebase ist oft eine gute Idee, bevor Sie eine Zusammenführung durchführen, da die Idee darin besteht, dass Sie in Ihrem Zweig Y die Arbeit des Zweigs B integrieren, mit dem Sie zusammenführen.
    Aber bevor Sie zusammenführen, lösen Sie jeden Konflikt in Ihrem branch (d. H .: "rebase", wie in "geben Sie meine Arbeit in meinem Zweig wieder ab einem jüngsten Punkt aus dem Zweig B).
    Wenn dies korrekt ausgeführt wird, kann die nachfolgende Zusammenführung von Ihrem Zweig zu Zweig B ein schneller Vorlauf sein.

  • ein Zusammenführen wirkt sich direkt auf den Zielzweig B aus. Dies bedeutet, dass die Zusammenführungen besser trivial sind. Andernfalls kann der Zweig B lange dauern, um wieder in einen stabilen Zustand zu gelangen (Zeit, für die Sie alle Konflikte lösen).


der Punkt der Verschmelzung nach einer Rebase? 

In dem Fall, den ich beschreibe, stütze ich B auf meinen Zweig ab, nur um die Möglichkeit zu haben, meine Arbeit von einem neueren Punkt aus B abzuspielen, während ich in meinem Zweig bleibe.
In diesem Fall ist noch eine Zusammenführung erforderlich, um meine "wiedergegebene" Arbeit auf B zu bringen.

Das andere Szenario ( in Git Ready beschrieben zum Beispiel) besteht darin, Ihre Arbeit direkt in B durch eine Rebase zu bringen (die all Ihre Nice-Commits konserviert oder Ihnen sogar die Möglichkeit gibt, sie durch ein interaktives Programm neu zu ordnen.) rebase).
In diesem Fall (wenn Sie sich im B-Zweig aufhalten) haben Sie Recht: Es ist keine weitere Zusammenführung erforderlich:

Ein Git-Baum bei Standardeinstellung, wenn wir nicht zusammengeführt oder umbasiert haben

rebase1

wir bekommen durch Umbasierung:

rebase3

In diesem zweiten Szenario dreht sich alles um: Wie bekomme ich ein neues Feature in Master?.

Wenn ich das erste Rebase-Szenario beschreibe, möchte ich alle daran erinnern, dass eine Rebase auch als vorbereitender Schritt dazu verwendet werden kann ("das neue Feature zurück in den Master bringen").
Sie können rebase verwenden, um master zuerst in den Zweig mit neuen Features zu bringen: Die rebase gibt die neuen Feature-Commits aus dem HEAD master wieder, aber immer noch im Zweig mit dem neuen Feature, wodurch der Startpunkt Ihrer Verzweigung effektiv von einem alten verschoben wird Master-Commit für HEAD-master.
Damit können Sie Konflikte in Ihrem branch auflösen (dh isoliert, während Master sich parallel weiterentwickelt, wenn die Konfliktlösung zu lange dauert).
Dann können Sie zum Master wechseln und new-feature zusammenführen (oder new-feature in master umbinden, wenn Sie die in Ihrem new-feature-Zweig durchgeführten Commits beibehalten möchten).

So: 

  • "rebase vs. merge" kann als zwei Möglichkeiten zum Importieren einer Arbeit betrachtet werden, zum Beispiel master
  • "Rebase Then Merge" kann jedoch ein gültiger Arbeitsablauf sein, um Konflikte zunächst isoliert zu lösen und dann Ihre Arbeit wiederherzustellen.
259
VonC

TL; DR

Wenn Sie irgendwelche Zweifel haben, verwenden Sie Zusammenführen.

Kurze Antwort

Die einzigen Unterschiede zwischen einem Rebase und einem Merge sind:

  • Die resultierende Baumstruktur des Verlaufs (in der Regel nur bei Betrachtung eines Festschreibungsdiagramms erkennbar) ist unterschiedlich (einer hat Zweige, der andere nicht).
  • Durch das Zusammenführen wird im Allgemeinen ein zusätzliches Commit erstellt (z. B. ein Knoten in der Struktur).
  • Durch Zusammenführen und erneutes Basieren werden Konflikte unterschiedlich behandelt. Durch einen Neustart werden Konflikte jeweils nach einem Commit angezeigt, und durch einen Zusammenschluss werden alle Konflikte gleichzeitig angezeigt.

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

Lange Antwort

Es gibt einige Faktoren, die Sie bei der Auswahl der zu verwendenden Operation 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 rebase. Durch einen Neustart wird der Zweig zerstört, und diese Entwickler haben defekte/inkonsistente Repositorys, sofern sie nicht git pull --rebase Verwenden. 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 korrekt anwenden , können Sie die festgelegte 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 die Abwicklung von Verzweigungen und Zusammenschlüssen leisten konnten. Diese Entwickler wissen nicht viel über Git und wollen auch nicht viel wissen. In diesen Teams würde ich nicht riskieren, aus irgendeinem Grund eine Neuinstallation 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 Unterfeature usw.) darstellt. In diesem Modell hilft der Branch bei der Identifizierung von Gruppen verwandter Commits. Zum Beispiel kann ein Feature schnell zurückgesetzt werden, indem die Zusammenführung dieses Zweigs rückgängig gemacht wird (um fair zu sein, dies ist eine seltene Operation). Oder unterscheiden Sie ein Feature, indem Sie zwei Zweige vergleichen (häufiger). Ein Rebase würde den Zweig zerstören und das wäre nicht einfach.

Ich habe auch an Teams gearbeitet, die das Branch-per-Developer-Modell verwendeten (wir waren alle dabei). In diesem Fall übermittelt die Filiale selbst keine zusätzlichen Informationen (das Commit hat bereits den Autor). Ein Umbasieren würde keinen Schaden anrichten.

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

Das Zurücksetzen (wie beim Rückgängigmachen) einer Basis ist im Vergleich zum Zurücksetzen einer Zusammenführung erheblich schwieriger und/oder unmöglich (wenn bei der Basis Konflikte aufgetreten sind). Wenn Sie glauben, dass die Möglichkeit besteht, dass Sie zurücksetzen möchten, verwenden Sie die Option Zusammenführen.

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

Rebase-Operationen müssen mit einem entsprechenden git pull --rebase Abgerufen werden. 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 schwierig zu koordinieren. Aus diesem Grund empfehlen die meisten Rebase-Workflows die Verwendung von Rebase für alle Zusammenführungen (und git pull --rebase Fü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 sich nur das Protokoll des Hauptzweigs (A - D) ansehen würden.

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

Durch erneutes Löschen können Zusammenführungen sicherer und einfacher durchgeführt werden

Die beiden Ansätze werden unterschiedlich zusammengeführt, aber es ist nicht klar, dass einer immer besser als der andere ist und dies möglicherweise vom Entwicklerworkflow abhängt. Wenn ein Entwickler beispielsweise dazu neigt, regelmäßig Commits durchzuführen (z. B. zweimal am Tag, wenn er von der Arbeit nach Hause wechselt), gibt es möglicherweise viele Commits für eine bestimmte Branche. 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 zurückzusetzen, könnte dies eine ziemlich mühsame Operation sein.

Rebase ist cooler/sexier/professioneller

Wenn Sie rm mit rm -rf Aliasen möchten, um "Zeit zu sparen", dann ist Rebase vielleicht etwas 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, stoße ich auf ein Szenario, 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.

Chaotische Geschichten waren für mich nie wirklich ein Problem. Ich lese meine Commit-Geschichte nicht nur wie einen spannenden Roman. In den meisten Fällen, in denen ich eine Vorgeschichte benötige, werde ich sowieso git blame oder git bisect verwenden. In diesem Fall ist das Festschreiben der Zusammenführung für mich sehr nützlich, denn wenn die Zusammenführung das Problem verursacht, sind dies für mich aussagekräftige Informationen.

Aktualisierung (4/2017)

Ich fühle mich verpflichtet zu erwähnen, dass ich persönlich weniger auf Rebase gesetzt habe, obwohl meine allgemeinen Ratschläge immer noch gültig sind. In letzter Zeit habe ich viel mit dem Angular 2 Material -Projekt interagiert. Sie haben Rebase verwendet, um einen sehr sauberen Commit-Verlauf zu führen. Auf diese Weise konnte ich sehr leicht feststellen, durch welches Commit ein bestimmter Fehler behoben wurde und ob dieses Commit in einer Version enthalten war. Es ist ein großartiges Beispiel für die korrekte Verwendung von Rebase.

181
Pace

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

Durch das Zusammenführen werden Ihre Commits nicht gelöscht. Merge bewahrt die Geschichte! (siehe gitk) Rebase schreibt den Verlauf neu, was eine schlechte Sache ist, nachdem Sie ihn gedrückt haben .

Verwenden Sie "Merge" - und nicht "Rebase" , wenn Sie bereits einen Push ausgeführt haben.

Hier ist Linus '(Autor von Git) übernehmen . Es ist eine wirklich gute Lektüre. Oder Sie können meine eigene Version der gleichen Idee weiter unten lesen.

Neustarten eines Zweigs auf dem Master:

  • liefert eine falsche Vorstellung davon, wie Commits erstellt wurden
  • verschmutzt den Master mit einer Reihe von Zwischen-Commits, die möglicherweise nicht gut getestet wurden
  • es könnte tatsächlich zu Build-Unterbrechungen bei diesen Zwischen-Commits kommen, da Änderungen vorgenommen wurden, die zwischen dem Zeitpunkt der Erstellung des ursprünglichen Zweigthemas und dem Zeitpunkt seiner Neugründung vorgenommen wurden.
  • erschwert die Suche nach guten Plätzen im Master zum Auschecken.
  • Bewirkt, dass die Zeitstempel von Commits nicht mit ihrer chronologischen Reihenfolge in der Struktur übereinstimmen. Sie sehen also, dass Commit A vor Commit B in Master steht, Commit B jedoch zuerst erstellt wurde. (Was?!)
  • Produziert mehr Konflikte, da einzelne Festschreibungen in der Zweigstelle Zusammenführungskonflikte beinhalten können, die einzeln gelöst werden müssen (Weitere historische Informationen zu den Ereignissen in jeder Festschreibung).
  • ist eine Umschreibung der Geschichte. Wenn der neu zu gründende Zweig an eine andere Stelle verschoben wurde (und nicht an Sie selbst weitergegeben wurde), haben Sie alle anderen, die diesen Zweig haben, durcheinander gebracht, seit Sie den Verlauf umgeschrieben haben.

Im Gegensatz dazu führt das Zusammenführen eines Zweigthemas zum Master Folgendes durch:

  • der Verlauf der Erstellung von Zweigzweigen wird beibehalten, einschließlich aller Zusammenführungen vom Master zum Zweigzweig, um ihn auf dem neuesten Stand zu halten. Sie bekommen wirklich eine genaue Vorstellung davon, mit welchem ​​Code der Entwickler bei der Erstellung gearbeitet hat.
  • master ist ein Zweig, der hauptsächlich aus Zusammenführungen besteht, und jeder dieser Zusammenführungs-Commits ist normalerweise ein guter Punkt in der Geschichte, den man sicher auschecken kann, da dort der Zweig für die Integration vorbereitet war.
  • alle einzelnen Commits des Zweigthemas bleiben erhalten, einschließlich der Tatsache, dass sie sich in einem Zweigthema befanden. Daher ist es natürlich, diese Änderungen zu isolieren, und Sie können bei Bedarf einen Drill-In durchführen.
  • zusammenführungskonflikte müssen nur einmal (zum Zeitpunkt der Zusammenführung) gelöst werden, damit Änderungen, die im Zweig "Topic" vorgenommen wurden, nicht unabhängig voneinander gelöst werden müssen.
  • kann mehrmals reibungslos durchgeführt werden. Wenn Sie Ihren Zweig regelmäßig in master integrieren, können die Benutzer weiterhin auf dem Zweig aufbauen und ihn unabhängig zusammenführen.
171
Andrew Arnott

Zusammenführen bedeutet: Erstellen Sie ein einzelnes neues Commit, das meine Änderungen in das Ziel einfügt.

Neubasis bedeutet: Erstellen Sie eine ganze Reihe von Commits, wobei Sie meine aktuellen Commits als Hinweise verwenden. Mit anderen Worten: Berechnen Sie, wie meine Änderungen ausgesehen hätten, wenn ich angefangen hätte, sie von dem Punkt an zu machen, an dem ich mich neu orientiere. Nach dem Rebase-Test müssen Sie daher möglicherweise die Änderungen erneut testen, und während des Rebasis-Tests treten möglicherweise einige Konflikte auf. 

Warum sollten Sie sich angesichts dieser Tatsache zurückhalten? Nur um die Entwicklungsgeschichte klar zu halten. Nehmen wir an, Sie arbeiten an Feature X, und wenn Sie fertig sind, führen Sie Ihre Änderungen in zusammen. Das Ziel enthält jetzt eine einzige Festschreibung, die etwas in Richtung "Hinzugefügtes Feature X" aussagt. Anstatt zu verschmelzen, würde die Zielentwicklungshistorie, wenn Sie neu basierten und dann zusammengeführt wurden, alle einzelnen Commits in einer einzigen logischen Abfolge enthalten. Das macht es später einfacher, Änderungen zu überprüfen. Stellen Sie sich vor, wie schwer es wäre, den Entwicklungsverlauf zu überprüfen, wenn 50 Entwickler ständig verschiedene Funktionen zusammenführen. 

Das heißt, wenn Sie den Zweig, an dem Sie arbeiten, bereits in den Upstream-Bereich verschoben haben, sollten Sie nicht rebasieren, sondern stattdessen zusammenführen. Für Zweige, die nicht in den Upstream-Modus verschoben wurden, müssen Sie die Basis neu testen, testen und zusammenführen. 

Ein anderes Mal möchten Sie möglicherweise rebasieren, wenn Sie Commits aus Ihrem Zweig entfernen möchten, bevor Sie in den Upstream-Modus gelangen. Zum Beispiel: Commits, die frühzeitig Debugging-Code einführen, und andere Commits, die diesen Code bereinigen. Die einzige Möglichkeit, dies zu tun, ist die Durchführung einer interaktiven Basis: git rebase -i <branch/commit/tag>

UPDATE: Sie möchten rebase auch verwenden, wenn Sie Git als Schnittstelle zu einem Versionskontrollsystem verwenden, das nicht-lineare Historie unterstützt (z. B. Subversion). Wenn Sie die git-svn-Bridge verwenden, ist es sehr wichtig, dass die Änderungen, die Sie wieder in Subversion zusammenführen, eine sequentielle Liste der Änderungen über den letzten Änderungen in Trunk sind. Dazu gibt es nur zwei Möglichkeiten: (1) Erstellen Sie die Änderungen manuell neu, und (2) verwenden Sie den Befehl rebase, der viel schneller ist.

UPDATE2: Eine weitere Möglichkeit, sich eine Rebase vorzustellen, besteht darin, dass sie eine Art Zuordnung von Ihrem Entwicklungsstil zu dem im Repository akzeptierten Stil ermöglicht, für den Sie sich engagieren. Nehmen wir an, Sie begehen gerne kleine, winzige Brocken. Sie haben ein Commit, um einen Tippfehler zu beheben, ein Commit, um nicht verwendeten Code zu entfernen, und so weiter. Wenn Sie alles erledigt haben, was Sie tun müssen, haben Sie eine lange Reihe von Commits. Nehmen wir an, das Repository, für das Sie sich verpflichten, zu großen Commits ermutigt. Für Ihre Arbeit würden Sie also ein oder zwei Commits erwarten. Wie nehmen Sie Ihre Commits und komprimieren sie auf das, was erwartet wird? Sie würden eine interaktive Rebase verwenden und Ihre winzigen Commits in weniger große Blöcke zerquetschen. Das Gleiche gilt, wenn das Gegenteil nötig war - wenn Ihr Stil aus ein paar großen Commits bestand, der Repo jedoch lange Strings aus kleinen Commits forderte. Sie würden auch eine Rebase verwenden, um das zu tun. Wenn Sie stattdessen zusammengeführt hatten, haben Sie jetzt Ihren Commit-Stil in das Haupt-Repository übernommen. Wenn es viele Entwickler gibt, können Sie sich vorstellen, wie schwer es wäre, nach einiger Zeit eine Historie mit verschiedenen Commit-Styles zu verfolgen.

UPDATE3: Does one still need to merge after a successful rebase? Ja. Der Grund ist, dass eine Rebase im Wesentlichen ein "Verschieben" von Commits beinhaltet. Wie ich bereits gesagt habe, werden diese Commits berechnet. Wenn Sie jedoch 14 Commits vom Verzweigungspunkt aus gesehen haben und dann vorausgesetzt, dass mit Ihrer Rebase nichts schief geht, sind Sie 14 Commits voraus (des Punktes, auf den Sie sich basieren) die rebase ist vollbracht. Sie hatten vor einer Rebase einen Zweig. Sie haben danach einen Zweig der gleichen Länge. Sie müssen noch zusammenführen, bevor Sie Ihre Änderungen veröffentlichen. Mit anderen Worten, so viele Male wie Sie möchten (auch nur, wenn Sie Ihre Änderungen nicht in den Upstream verschoben haben). Mischen Sie erst, nachdem Sie die Datenbank neu erstellt haben.

66
Carl

vor dem Zusammenführen/Ersetzen:

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

nach git merge master:

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

nach git rebase master:

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

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

dieses beispiel und viel besser illustrierte informationen über git finden sie hier: http://excess.org/article/2008/07/07/ogre-git-tutorial/

57
guybrush

Das Zusammenführen ist definitiv die einfachste und häufigste Möglichkeit, Änderungen zu integrieren, ist aber nicht die einzige: Rebase ist ein alternatives Integrationsmittel.

Verständnis etwas besser zusammenfügen

Wenn Git eine Zusammenführung durchführt, werden drei Commits gesucht:

  • (1) Gemeinsames Vorfahren-Commit Wenn Sie die Historie zweier Zweige in einem Projekt verfolgen, haben sie immer mindestens ein Commit: zu diesem Zeitpunkt hatten beide Zweige den gleichen Inhalt und entwickelten sich dann unterschiedlich.
  • (2) + (3) Endpunkte jedes Zweigs Das Ziel einer Integration besteht darin, die aktuellen Zustände zweier Zweige zusammenzufassen. Daher sind ihre jeweiligen neuesten Überarbeitungen von besonderem Interesse. Die Kombination dieser drei Zusagen führt zu der von uns angestrebten Integration.

Fast-Forward oder Merge Commit

In sehr einfachen Fällen hat eine der beiden Verzweigungen keine neuen Commits, da die Verzweigung stattgefunden hat - ihr letztes Commit ist immer noch der gemeinsame Vorfahr.

 enter image description here

In diesem Fall ist das Durchführen der Integration ein Kinderspiel: Git kann nur die Commits des anderen Zweigs über das Common-Vorfahren-Commit hinzufügen. In Git wird diese einfachste Form der Integration als "Fast-Forward" -Mischung bezeichnet. Beide Zweige haben dann die gleiche Geschichte.

 enter image description here

In vielen Fällen sind jedoch beide Zweige einzeln vorgerückt .  enter image description here

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

 enter image description here

Human Commits & Merge Commits

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

Ein Merge-Commit ist etwas anders: Statt von einem Entwickler erstellt zu werden, wird es automatisch von Git erstellt. Anstatt einen Satz verwandter Änderungen einzuwickeln, besteht der Zweck darin, zwei Zweige wie einen Knoten zu verbinden. Wenn Sie eine Zusammenführungsoperation später verstehen möchten, müssen Sie sich die Historie beider Zweige und das entsprechende Commit-Diagramm ansehen.

Integration mit Rebase

Einige Leute ziehen es vor, auf solche automatischen Merge-Commits zu verzichten. Stattdessen möchten sie, dass die Projekthistorie so aussieht, als ob sie sich in einer einzigen, geraden Linie entwickelt hätte. Es gibt keine Hinweise darauf, dass es in mehrere Zweige aufgeteilt wurde Irgendwann.

 enter image description here

Gehen wir Schritt für Schritt durch eine Rebase-Operation. Das Szenario ist das gleiche wie in den vorherigen Beispielen: Wir möchten die Änderungen von Branch-B in Branch-A integrieren, jetzt aber mit Rebase.

 enter image description here

Wir werden dies in drei Schritten tun

  1. git rebase branch-A // syncs 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 wird Git alle Commits auf Branch-A "rückgängig machen", nachdem die Zeilen verzweigt wurden (nach dem Common-Vorfahren-Commit). Allerdings werden sie natürlich nicht verworfen. Stattdessen können Sie sich diese Commits als "vorübergehend gespeichert" vorstellen.

 enter image description here

Als Nächstes werden die Commits aus Branch-B übernommen, die wir integrieren möchten. Zu diesem Zeitpunkt sehen beide Zweige genau gleich aus.

 enter image description here

Im letzten Schritt werden die neuen Commits auf Branch-A nun erneut angewendet - aber auf einer neuen Position, zusätzlich zu den integrierten Commits von Branch-B (sie basieren auf re-based) .Das Ergebnis sieht nach Entwicklung aus war in einer geraden Linie passiert. Anstelle eines Merge-Commits, das alle kombinierten Änderungen enthält, wurde die ursprüngliche Commit-Struktur beibehalten.

 enter image description here

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

Hinweis: Aus dem awesome post von git-tower . Die Nachteile von rebase ist auch eine gute Lektüre in demselben Beitrag.

40
Abdullah Khan

Dieser Satz bekommt es:

Im Allgemeinen können Sie das Beste aus beiden Welten erzielen, indem Sie local Änderungen, die Sie vorgenommen haben, aber noch nicht freigegeben wurden, bevor Sie sie eingeben Um Ihre Geschichte aufzuräumen, stützen Sie jedoch niemals etwas ab, was Sie schon getan haben irgendwo.

Quelle: http://www.git-scm.com/book/de/v2/Git-Branching-Rebasing#Rebase-vs.-Merge

26

Das Pro git-Buch als wirklich gute Erklärung auf der umbasierenden Seite .

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

Eine Rebase geht zum gemeinsamen Vorfahren auf der 2 und wendet die Änderungen schrittweise übereinander an. Dies führt zu einer "saubereren" und lineareren Geschichte. 

Aber wenn Sie sich zurückhalten, geben Sie frühere Commits auf und erstellen neue. Daher sollten Sie ein Repo, das öffentlich ist, niemals neu abstimmen. Die anderen Leute, die am Repo arbeiten, werden dich hassen.

Allein deshalb verschmelze ich fast ausschließlich. In 99% der Fälle unterscheiden sich meine Zweigstellen nicht so stark. Wenn es Konflikte gibt, dann nur an ein oder zwei Stellen.

15
xero

Diese Antwort ist weitgehend auf Git Flow ausgerichtet. Die Tabellen wurden mit dem Nice ASCII - Tabellengenerator und die Historienbäume mit diesem wunderbaren Befehl ( Alias ​​ as git lg) erstellt:

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)'

Tabellen sind in umgekehrter chronologischer Reihenfolge, um mehr mit den Historienbäumen übereinzustimmen. Siehe auch den Unterschied zwischen git merge und git merge --no-ff zuerst (normalerweise möchten Sie git merge --no-ff verwenden, da dies dazu führt, dass Ihre Geschichte der Realität näher kommt):

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üge Features immer zusammen und entwickle nie, entwickle nie Features von Features. Dies ist eine Folge der Goldenen Regel der Neueinstellung :

Die goldene Regel von git rebase ist, es niemals in public -Zweigen zu verwenden.

Mit anderen Worten :

Niemals etwas widerlegen, was Sie irgendwo geschoben haben.

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

Die Frage von git merge vs git rebase gilt also fast nur für die Feature-Zweige (in den folgenden Beispielen wurde --no-ff immer beim Zusammenführen verwendet). Da ich nicht sicher bin, ob es eine bessere Lösung gibt ( eine Debatte existiert ), werde ich nur angeben, wie sich beide Befehle verhalten. In meinem Fall bevorzuge ich git rebase, da dies einen schöneren Historienbaum erzeugt :)

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 develop zu einem Funktionszweig

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 development
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 development
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

Seitennotizen

git cherry-pick

Wenn Sie nur ein bestimmtes Commit benötigen, ist git cherry-pick eine Nice-Lösung (die -x-Option fügt eine Zeile mit der Zeichenfolge "(Kirsche aus Commit ...)" an den ursprünglichen Nachrichtentext der Commit-Nachricht an gute Idee, es zu benutzen - git log <commit_sha1>, um es zu sehen):

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

Nicht sicher, ob ich es besser erklären kann als Derek Gourlay ... Im Allgemeinen verwenden Sie git pull --rebase anstelle von git pull :) Was in diesem Artikel jedoch fehlt, ist, dass Sie es standardmäßig aktivieren können :

git config --global pull.rebase true

git rerere

Wieder schön erklärt hier . Einfach ausgedrückt: Wenn Sie diese Option aktivieren, müssen Sie denselben Konflikt nicht mehrmals lösen.

15
sp00m

Git rebase wird verwendet, um die Verzweigungspfade in der History-Cleaner- und Repository-Struktur linear zu machen. 

Es wird auch verwendet, um die von Ihnen erstellten Verzweigungen privat zu halten, da nach dem Umbasieren und Übertragen der Änderungen auf den Server beim Löschen Ihrer Verzweigung kein Hinweis auf eine Verzweigung vorliegt, an der Sie gearbeitet haben. So ist Ihre Niederlassung jetzt Ihr lokales Anliegen.

Nachdem wir rebase gemacht haben, entfernen wir auch ein zusätzliches Commit, das wir bei normaler Zusammenführung überprüft haben. 

Und ja, man muss nach einem erfolgreichen Rebase-Vorgang immer noch zusammenführen, da der Rebebankommando Ihre Arbeit nur auf den Zweig setzt, den Sie während des Rebebase-Befehls erwähnt haben, beispielsweise master und das erste Commit Ihres Zweiges als direkter Nachkomme des Master-Zweigs. Dies bedeutet, dass wir jetzt einen Schnellvorlauf durchführen können, um Änderungen von diesem Zweig zum Hauptzweig zu bringen.

4
cvibha

Einige praktische Beispiele, die etwas mit der Entwicklung im großen Maßstab zu tun haben, bei der Gerrit für die Überprüfung und Bereitstellung der Integration verwendet wird.

Ich verschmelze, wenn ich meinen Feature-Zweig zu einem neuen Remote-Master hochsteige. Dies führt zu minimalem Uplift-Aufwand und es ist einfach, die Entwicklung der Feature-Entwicklung in beispielsweise 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 verschmelze, wenn ich ein Lieferverpflichtung vorbereite.

git fetch
git checkout Origin/master
git merge --squash Origin/my_feature
git commit
git Push Origin HEAD:refs/for/master

Ich baue nach, wenn mein Bereitstellungs-Commit aus irgendeinem Grund die Integration fehlschlägt und ich ihn auf einen neuen Remote-Master aktualisieren muss.

git fetch
git fetch <gerrit link>
git checkout FETCH_HEAD
git rebase Origin/master
git Push Origin HEAD:refs/for/master
2
Martin G

Schauen wir uns ein Beispiel an. Während der Arbeit an einem Zweig mit dem Namen login, der auf dem Zweig master basiert, hat eines Ihrer Teammitglieder einige Änderungen an master vorgenommen. Sie benötigen diese Änderungen, um die Funktion login in Ihrem Zweig zu beenden.

enter image description here Abbildung 1. Die neuen Commits im Zweig master (E und F) werden benötigt, um die Arbeit im Zweig login abzuschließen.

Das Zusammenführen des Zweigs master in Ihren würde zu einem Zusammenführungs-Commit führen, das die Änderungen zwischen beiden Zweigen enthält und vorhanden ist, um anzuzeigen, wo eine Zusammenführung erfolgt aufgetreten.

enter image description here Abbildung 2. Das Zusammenführen der beiden Zweige führt zu einem Merge-Commit.

Wir werden nicht wissen müssen, wann wir die master in der login -Niederlassung zusammengeführt haben. Stattdessen möchten wir vorgeben, dass alle Festschreibungen für den Zweig login auf der Grundlage des neuen Zustands des Zweigs master vorgenommen wurden.

Gits Rebase -Befehl spult die Commits in Ihrem aktuellen Zweig vorübergehend zurück, zieht die Commits aus dem anderen Zweig ein und wendet die zurückgespulten Commits wieder oben an. Durch Umschalten des Stroms Hiermit wird der Stromzweig auf den anderen Zweig gelegt.

enter image description here

Abbildung 3. Beim erneuten Starten werden die Festschreibungen aus dem Zweig login über dem Zweig master angewendet.

Die Quelle ist hier

0
yoAlex5

Es wurde viele Male erklärt, was Rebase und welche Verschmelzung sind, aber wann was verwenden?

Wann verwenden Sie Rebase?

  • wenn Sie den Zweig noch nicht geschoben haben/niemand sonst daran arbeitet
  • sie wollen die volle Geschichte
  • sie möchten alle automatisch generierten Commit-Nachrichten "zusammengeführt .." vermeiden

Als git rebase ändert sich die Historie. Daher sollten Sie es nicht verwenden, wenn eine andere Person in demselben Zweig arbeitet oder wenn Sie es geschoben haben. Wenn Sie jedoch eine Zweigstelle vor Ort haben, können Sie einen Rebase-Master zusammenführen, bevor Sie Ihre Zweigstelle wieder zu einem Master zusammenführen, um eine sauberere Historie zu erhalten. Nach dem Zusammenführen in den Master-Zweig ist es nicht sichtbar, dass Sie einen Zweig im Master-Zweig verwendet haben. Die Historie ist "sauberer", da Sie nicht automatisch "Zusammengeführt .." erstellt haben, aber immer noch den vollen Inhalt haben Geschichte in Ihrem Master-Zweig, ohne automatisch "zusammengeführt .." Commits erstellt zu haben. Stellen Sie jedoch sicher, dass Sie git merge feature-branch --ff-only verwenden, um sicherzustellen, dass keine Konflikte beim Erstellen eines einzelnen Commits auftreten, wenn Sie Ihr Feature wieder mit dem Hauptelement zusammenführen.

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

Wann wird die Zusammenführung verwendet?

  • wenn Sie den Zweig geschoben haben, arbeiten auch andere daran
  • sie brauchen nicht die volle Geschichte
  • einfach zu verschmelzen ist gut genug für dich

Wenn Sie nicht die gesamte Historie eines Feature-Zweigs in Ihrem Master-Zweig benötigen oder möchten, oder wenn andere in demselben Zweig arbeiten/Sie haben ihn gepusht. Wenn Sie immer noch die Historie haben möchten, fügen Sie einfach den Master mit dem Feature-Zweig zusammen, bevor Sie den Feature-Zweig mit dem Master zusammenführen. Dies führt zu einer schnellen Zusammenführung, bei der sich der Verlauf des Feature-Zweigs in Ihrem Master befindet (einschließlich der Merge-Commit-Funktion, die sich in Ihrem Feature-Zweig befand, da Sie den Master mit ihm zusammengeführt haben).

0
Jeremy Benks