Wann wird empfohlen, Git Rebase vs. Git Merge zu verwenden?
Muss ich nach einer erfolgreichen Rebase noch zusammenführen?
Wann wird empfohlen, Git Rebase vs. Git Merge zu verwenden?
Muss ich nach einer erfolgreichen Rebase noch zusammenführen?
Antworten:
Wann verwenden Sie eine der beiden?
init
einem neuen Repo, add
der 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 master
und merge feature
. In log
sehe 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 feature
Feature in den Master einbinden, aber nicht festschreiben, ist das einzige neue Commit für den Master das, das Sie selbst vorgenommen haben.
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 master
Verzweigung implementiert haben . Sie können einfach zu master
dem cool-feature
Zweig 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.
but this way a new dummy commit is added, if you want to avoid spaghetti-history
- Wie ist es schlimm?
Sean Schofield
es 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. "
merge
, rebase
, fast-forward
, etc.) beziehen sich auf bestimmte Manipulationen eines gerichteten azyklischen Graphen. Es wird einfacher, über dieses mentale Modell nachzudenken.
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 Y
die Arbeit des Zweigs, B
auf 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 B
kann 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 B
kann 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 B
auf 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
wir bekommen durch Umbasierung:
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 master
Zweig, 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
, master
wenn Sie die in Ihrem Programm vorgenommenen Commits beibehalten möchtennew-feature
Ast).
Damit:
master
.Wenn Sie Zweifel haben, verwenden Sie Zusammenführen.
Die einzigen Unterschiede zwischen einem Rebase und einem Merge sind:
Die kurze Antwort lautet also, Rebase oder Merge auszuwählen, je nachdem, wie Ihr Verlauf aussehen soll .
Es gibt einige Faktoren, die Sie bei der Auswahl des zu verwendenden Vorgangs berücksichtigen sollten.
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.
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.
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.
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.
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 --rebase
für alle Pulls).
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.
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.
Wenn Sie einen Alias rm
für rm -rf
"Zeit sparen" möchten, ist Rebase möglicherweise das Richtige für Sie.
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.
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.
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:
Im Gegensatz dazu führt das Zusammenführen eines Themenzweigs zum Master Folgendes zusammen:
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.
git merge
unterstützt die --no-ff
Option, die es zwingt, ein Merge-Commit durchzuführen .
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:
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.
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.
In vielen Fällen haben sich jedoch beide Zweige einzeln weiterentwickelt.
Um eine Integration vorzunehmen, muss Git ein neues Commit erstellen, das die Unterschiede zwischen ihnen enthält - das Merge-Commit.
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.
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.
Wir werden dies in drei Schritten tun
git rebase branch-A // Synchronises the history with branch-A
git checkout branch-A // Change the current branch to branch-A
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.
Als nächstes werden die Commits aus Zweig B angewendet, die wir integrieren möchten. Zu diesem Zeitpunkt sehen beide Zweige genau gleich aus.
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.
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 rebase
ist auch eine gute Lektüre im selben Beitrag.
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 .
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 .
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 merge
und git merge --no-ff
zuerst (Sie möchten ihn normalerweise verwenden, git merge --no-ff
da 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 rebase
ist, es niemals in öffentlichen Zweigen zu verwenden.
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 merge
vs git rebase
gilt also fast nur für die Feature-Zweige (in den folgenden Beispielen --no-ff
wurde 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 rebase
da dadurch ein schönerer Verlaufsbaum erstellt wird :)
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
develop
zu einem Feature-Zweiggit 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
git cherry-pick
Wenn Sie nur ein bestimmtes Commit benötigen, git cherry-pick
ist dies eine -x
gute 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 --rebase
anstelle 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.
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.
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.
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
Es wurde oft erklärt, was Rebase und was Merge ist, aber wann sollten Sie was verwenden?
Wann sollten Sie Rebase verwenden?
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 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).
Wann benutze ich git rebase
? Fast nie, weil es die Geschichte neu schreibt. git merge
ist fast immer die bevorzugte Wahl, da sie berücksichtigt, was tatsächlich in Ihrem Projekt passiert ist.