Git Branches
Inhalt
Ziele
- Ich kann mit Mergekonflikten umgehen.
- Ich kann ein Rebase eines Branches vornehmen.
- Ich kenne die zwei wichtigsten Flows und kann sie voneinander unterscheiden.
Im vorherigen Kapitel haben wir gelernt, dass man nur dann seine Änderungen pushen kann, wenn einem niemand anders zuvorkam. Um diesem Problem aus dem Weg zu gehen, kann ein Branch (engl. für Ast/Zweig) verwendet werden, welcher zu einem späteren Zeitpunkt wieder auf den Hauptpfad gemerged wird.
Im ersten Kapitel haben wir gelernt, dass Git mit jedem Commit ein Snapshot der Daten und eine Referenz auf den vorgehenden Commit speichert. Ein Branch ist nichts anderes als ein Zeiger auf einen spezifischen Snapshot.
Erzeugen wir mit git branch testing
einen neuen Branch namens “testing” so wird ein Zeiger erstellt, welcher auf denselben Commit zeigt, auf dem man sich im Moment befindet. Damit Git weiss, wo man sich im Moment gerade befindet gibt es einen speziellen Zeiger namens HEAD
:
|
|
Wie man auf der Skizze erkennen kann, wurde der neue Branch “testing” erstellt, wir befinden uns aber immer noch auf dem “master” Branch (HEAD). Um nun auf den neuen Branch zu wechseln können wir den Befehl git checkout testing
verwenden. Protipp: Um einen neuen Branch zu erstellen und gleich auf diesen zu wechseln, kann der Befehl git checkout -b <branchname>
verwendet werden.
Erstellen wir nun einen Commit auf dem “testing” Branch, dann zeigt sich folgendes Bild:
|
|
Beispiel für Branching, Merging und Rebasing
In diesem Abschnitt wird davon ausgegangen, dass es einen Fehler im aktuellen master
-Branch gibt, der unter testing
nicht behoben wurde, da er zur Entwicklung neuer Funktionen verwendet wird. Um den Fehler zu beheben, wird ein neuer Branch mit dem Namen bugfix
erstellt, der mit der gleichen Revision wie master
beginnt.
|
|
git commit
Nachdem der Branch bugfix
erstellt und ausgecheckt wurde, wird die Korrektur entwickelt und übertragen.
|
|
- Die erste Zeile in
first-file.txt
wurde geändert, indemwith bugfix
zur ersten Zeile hinzugefügt wurde.
Mit der neuen Übergabe an bugfix
fangen die Branches an auseinanderzulaufen.
|
|
Mergen der Branches
Nach der Fehlerbehebung ist es nun an der Zeit, sie wieder in den Master-Branch einzubinden, damit andere Benutzer sie ebenfalls verwenden können.
git merge
Merge ist eine Wiederholung der Änderungen eines benannten Commits (auch Branch genannt) in einen anderen Branch, da diese voneinander abwichen. Damit dies funktioniert, ändert man zuerst den Zielbranch. In diesem Fall ist das bugfix
. Die Änderungen sollen nach master
zurückgespielt werden. Da das Ziel master
ist, ist der erste Schritt, zu diesem Branch zu wechseln.
|
|
Wechsle zum Zielbranch (
master
).Bestätige, dass du dich im Zielbranch befindest. Dieser Schritt ist optional.
Führe die Änderungen von
bugfix
mitmaster
zusammen.
Nach der Zusammenführung zeigen bugfix
und master
auf dieselbe Revision.!
|
|
git branch -d
Es gibt keinen Grund mehr, den bugfix
-Branch beizubehalten, da die Änderungen nun in master
eingearbeitet wurden. Mit dem Befehl branch -d <branchname>
wird der Branch gelöscht.
|
|
Der Branch kann beim Auschecken nicht gelöscht werden. Der aktive Branch ist
master
, der für die Löschung vonbugfix
funktioniert.Der Branch wird gelöscht und die Ausgabe enthält den kurzen SHA1-Hash.
Die Überprüfung mit
git log
bestätigt, dassmaster
auf denselben Hash zeigt wiebugfix
.
Nachdem der Branch bugfix
gelöscht wurde, bleiben nur noch master
und testing
übrig.
Rebasing Branches
Nachdem die Fehlerkorrektur in den Branch master
eingebracht wurde, ist der nächste logische Schritt, die Änderungen in den Branch testing
einzubringen, um sicherzustellen, dass der nächste Release die korrigierte Version enthält. Wenn man mit mehreren Branches arbeitet, ist diese Operation notwendig, um nicht zu weit in den master
zurückzufallen und viele Merge-Konflikte zu vermeiden.
git rebase
Beim Rebase wird der Basis-Commit eines Branches verschoben und dessen Änderungen an den aktuellen Stand eines anderen Branches oder Commits angehängt, der als Argument in der Befehlszeile angegeben wird.
In unserem Beispielszenario entwickeln wir kurz vor den Ferien ein neues Feature. Dazu erstellen wir einen Branch, welcher sich vom Master abzweigt und commiten unsere Änderungen. Die Ausgangslage vor den Ferien würde also wie folgt aussehen:
Nun kommen wir zwei Wochen später - nach unseren Ferien - wieder ins Office und die Situation sieht wie folgt aus:
Wie wir sehen, hat sich einiges auf master
getan und unser Branch ist nicht mehr auf dem aktuellen Stand. Wir wollen, dass feature
aktuell ist, wollen aber nicht git merge
nutzen, da wir keinen Merge-Commit im Branch haben möchten, sondern jeden einzelnen Commit aus master
auch auf unserem Feature-Branch appliziert haben möchten. So sind alle Änderungen Schritt für Schritt nachvolliehbar. Deswegen entscheiden wir uns für einen rebase.
Als Erstes stellen wir also sicher, dass wir auf dem korrekten Branch sind und machen anschliessend ein git rebase master
. Hierbei sollte angemerkt werden, dass man auf master
niemals ein rebase machen sollte, da dies die History verändert mit zusätzlichen Commits und andere Entwickler:innen dadurch verwirrt werden könnten.
Wenn man master
updaten möchte, sollte dies über ein git merge
erfolgen.
|
|
Nun ist der letzte Commit auf Master die neue Base des Branches feature
und alle Änderungen wurden nachgespielt.
Die Situation sieht also wie folgt aus:
Hinweis
Die Durchführung eines rebase zwischen zwei Branches erfordert einen gemeinsamen Vorfahren im Tree.
Einfache Mergekonflikte
Im oben erwähnten Beispiel ist alles automatisch gegangen beim Mergen, es gibt jedoch Fälle, bei denen Git nicht mehr in der Lage ist automatisch die Dateien Zusammenzuführen, wenn zum Beispiel eine Änderung an der gleichen Stelle einer Datei in beiden Branches vorgenommen wird. Gehen wir vom Beispiel oben aus, die Story die man da umsetzt macht auch etwas mit dem Dashboard welches wir kurzum anpassen mussten:
|
|
Git konnte nicht automatisch mergen und hat somit keinen commit erstellt, wir müssen den Mergekonflikt von Hand lösen, bevor wir weiter arbeiten können. Weitere Infos liefert git status
:
|
|
Git fügt automatisch eine Markierung in die Dateien ein, welche gmerged werden müssen:
|
|
Das bedeutet, dass der HEAD (also der Masterbranch, weil auf den haben wir vor dem Mergen gewechselt) den oberen Teil (also immer alles auf Grün) und unsere neuen Änderungen den unteren Teil auf dieser Zeile hatten. Man kann den Konflikt nun lösen, indem man den ganzen Block, mit der gewünschten Änderung ersetzt. Ist der Konflikt gelöst, können wir die Datei ganz normal stagen und commiten. Gerade bei grösseren Mergekonflikten kann es praktisch sein mit tools zu Arbeiten, welche einem die Unterschiede zwischen den beiden Branches Grafisch darstellen, dafür gibt es den Befehl git mergetool
.
In der Regel können viele Merge-Konflikte verhindert oder minimiert werden, indem:
Regelmässige Kommunikation von Änderungen zwischen Teammitgliedern.
Regelmässige Rebases mit dem Merge-Zielbranch.
Erstellen kleiner und atomarer Commits.
Flows
Flows sind standardisierte Abläufe, wie Branches erzeugt und später Releases erstellt werden. Wir zeigen hier die zwei am weitesten verbreiteten Flows.
Feature Branch Flow
Der Feature Branch Flow besagt, dass man für jede neue Funktion oder Verbesserung (Feature) einen eigenen Branch erstellt. Auf diesem Branch kann die Funktion entwickelt werden, ohne den Main-Branch zu beeinflussen. Sobald die Arbeit abgeschlossen und getestet ist, wird der Feature-Branch wieder in den Hauptbranch (main
) integriert.
Vertiefende Informationen zum Feature Branch Flow können auf dieser Seite gelesen werden.
Gitflow
Der Gitflow unterscheidet sich primär vom Feature Branch Flow indem, dass zwei Hauptbranches verwendet werden: main
für den stabilen Code und development
für die laufende Entwicklung. So werden Feature-Branches nicht direkt vom main
-, sondern vom development
-Branch (Name kann abweichen) abzweigen. So werden fertiggestellte Änderungen auch nicht gleich in den Main integriert, sondern zurück in den Development-Branch.
Zudem wird ein release-Branch verwendet, auf welchen Änderungen vom Development-Branch gepusht werden, um dort vor einem Release getestet zu werden. Anschließend wird der release-Branch, und nur dieser, in den main gemerged.
Nebst Feature Branches können auch Branches für Releases und Hotfixes (hier nicht abgebildet) erstellt werden.
Der Flow ermöglicht eine strukturierte Vorgehensweise für die Entwicklung und Veröffentlichung von Software, indem Entwicklungs- und Produktionscode getrennt werden. Änderungen sind einfach und schnell nachvollziehbar und es ist jederzeit möglich, schnell auf eine releaste-Version zurückzugreifen.
Vertiefende Informationen zum Gitflow können auf dieser Seite gelesen werden.
Hands On
https://learngitbranching.js.org/