Best Practice-Tipps für Git: Rebases nutzen anstatt nur zu Mergen

Ich erlebe es als Software-Entwickler leider sehr oft, dass Entwickler wenig bis keine Ahnung von git haben und es gerade so hinkriegen in einer GUI (sei es Sourcetree oder eine IDE wie IntelliJ) die Branches ineinander zu mergen. Dabei entstehen sehr schwierig zu verstehende git-Historien. Das ist nicht optimal, doch mit meinen wenigen Tipps lernt ihr git hoffentlich besser zu verstehen und warum ihr nicht immer mergen solltet. Bei Git geht es nicht nur darum stets den aktuellsten Stand zu haben, sondern es geht vor allem um Nachvollziehbarkeit! Hier ein kleines Beispiel: Die linke Seite ist „gut“, die rechte Seite ist „schlecht“/unübersichtlich:

Symbolbild: Gute Git-Historie (links) vs schlechte/unübersichtliche Historie (rechts)

Symbolbild: Gute Git-Historie (links) vs schlechte/unübersichtliche Historie (rechts)

Die Sache ist, es funktioniert auch auf die hässliche Art und man hat irgendwie den aktuellsten Stand der Software. Aber wenn man dann mal sinnvoll zurückverfolgen will wie und wo Änderungen in die Software gekommen sind, dann sollte man spätestens da überlegen wie sinnvoll es ist alle Branches kreuz und quer durcheinander zu mergen. Ich bin kein geprüfter Git-Experte, sondern möchte lediglich einige Tipps geben die sich aus meiner Erfahrung und logischen Grundlagen ergeben.

Erklärung zu den obigen zwei Git-Verläufen

Links seht ihr wie Änderungen von dev auf Master gemerged werden und Änderungen vom Feature-Branch in den dev-Branch gemerged werden. Alle Änderungen lassen sich gut verfolgen und merges (neue Features) einfach rückgängig machen. Rechts hingegen ist ein größeres Hin und Her-Gemerge zu sehen, beispielsweise wurde dev in den Feature-Branch gemerged, und der Feature-Branch dann wieder in Dev. Don’t do this (on the right)!

Master-Branch, Dev-Branch, Feature-Branches

Es gibt in meiner Philosophie idealerweise einen Master-Branch, auf dem lediglich stabile Versionen laufen. Dieser Branch ist von der laufenden Entwicklung, dem dev-Branch, ausgekoppelt. Auf Master und Dev ist Force-Pushen verboten. Auf Master wird nie commited, immer nur von dev reingemerged. Für Features deren Entwicklung länger dauert gibt es eigene Feature-Branches. Kleinere Sachen können direkt auf dev commited werden. Wir wollen Feature-Branches bei größeren Arbeiten verwenden, damit die Git-Historie nicht eine abwechselnde Folge von commits verschiedener Teile der Software ist. Eine Folge von Commits in der Git-Historie sollte immer zusammengehörig sein bzw. anders gesagt: Eine Folge von Commits zu einem bestimmten Feature sollte nicht von anderen Commits unterbrochen werden.

Merging und Rebasing

Feature-Branches müssen regelmäßig auf dev gerebased werden. Das bedeutet, dass wir den aktuellen Stand vom dev-Branch nehmen und als neue Basis des Feature-Branches nehmen. Nehmen wir als Beispiel eine Anwendung, die von Angular 4 auf Angular 5 geupdatet werden muss. Da muss man beispielweise alle <template>-Tags durch <ng-template> ersetzen. Macht man das auf seinem Feature-Branch und ein Entwickler hat in der Zeit aber eine Komponente auf dev hinzugefügt, so ist diese dem Feature-Branch nicht bekannt. Wir wollen Features im Idealfall als „ganze Einheiten“ in dev reinmergen, ohne den Feature-Branch mehrfach aufzumachen/zu nutzen. Daher rebasen wir (z.B.)[git checkout dev && git pull && git checkout my-feature-branch &&] git rebase dev. Dabei treten eventuell Konflikte auf, die gelöst werden müssen. Das geht aber einfach auf der Kommandozeile, alternativ aber auch insbesondere sehr einfach in IntelliJ IDE. Wurde unser Feature-Branch aber bereits einmal auf den Remote-Server gepusht brauchen wir nach dem Rebase (den wir lokal vorgenommen haben) einen Force-Push, da sich die Commit-IDs geändert haben, da wir die Historie geändert haben.

Ist ein Feature-Branch gerebased können wir ihn auch ohne Merge-Konflikt in dev reinmergen. Branches immer mit --no-ff (no-fast-forward) reinmergen! Dabei werden zwar grundsätzlich immer Merge-Commits erzeugt statt die Commits „einfach mitzunehmen und auf den Branch zu setzen“, man hat aber eine ganz klare Sicht wann welches Feature reingekommen ist und man kann es mit einem revert einfach wieder rausnehmen ohne zu schauen ab welchem Commit genau die Entwicklung des neuen Features begann.

Die Git Historie (git hist, alias für git log --graph --oneline --decorate [-all], so wie die Git-Branches-Übersicht in IntelliJ unten) ist wie folgt aufgebaut: Links der Master-Branch, „rechts“ davon der dev-Branch, rechts davon die Feature-Branches. Gemerged wird nur von „rechts nach links“. Wenn man „von links nach rechts“ merged, also zum Beispiel von dev auf einen Feature-Branch, dann ist das nicht schön. Das seht ihr oben im Bild in der rechten Hälfte.

Wenn mehrere Entwickler auf dem Dev-Branch arbeiten wird es passieren, dass man nicht pushen kann, weil man Commits vom Remote-Branch noch nicht hat. In dem Fall bitte git pull --rebase (bzw. in IntelliJ auf „Rebase“ klicken) ausführen, denn sonst wird dev in dev gemerged und das ist nicht schön.

Nutzt Ammend-Commits

Oft hat man dann den Fall, dass man etwas commited und danach merkt es fehlte doch noch eine Stelle oder irgendwo ist ein Typo drin. Nutzt in den Fällen doch bitte git commit --amend (bzw. Klickt den Schalter oben Rechts im IntelliJ-Commit-Fenster). So wird die Änderung noch an den letzten Commit angehängt. So kann man die Gesamtzahl der Commits kleiner halten, was zu mehr Übersichtlichkeit führt, da keine unnötigen Mini-Commits mehr entstehen. Hier gilt allerdings: Hat man vor einem Ammend-Commit bereits gepusht so muss ein Force-Push folgen, da die Commit-ID geändert wird.

Fazit

Es lohnt sich wirklich, wenn ihr euch mal ein paar Minuten Zeit nehmt und wenigstens ein paar Grundlagen von git lernt und dabei stets im Hinterkopf habt: „Ich muss ein Feature jederzeit einfach reverten können! Die Entwicklungshistorie muss nachvollziehbar sein!“ Rebases sind ein wichtiges Feature für saubere Git-Historien. Rebases bereiten außerdem Merges von beispielsweise großen Feature-Branches vor, da das Mergen von großen Features dann ohne Merge-Konflikte funktioniert! Bitte hört auf Git auschließlich in Sourcetree und eurer IDE zu benutzen und übt git auf der Kommandozeile. Dadurch lernt ihr git sehr gut verstehen! 🙂

Ich freue mich über eure Fragen, Anregungen und Kommentare! Ich kann auch noch viel lernen! Habt ihr tolle Git-Tipps und Best-Practices? Dann her damit!

Weiterführende Links

Philipp Schuster

Ich bin Philipp, studiere Informatik an der TU Dresden und arbeite als Werkstudent im Bereich Software-Entwicklung bei T-Systems MMS. Ich bin 20 Jahre alt und beschäftige mich in meiner Freizeit gerne mit meinem Blog, Programierung, Technik, aber auch mit Joggen und vielen anderen Dingen. Get To Know Me oder schreibt mich an!

Das könnte Dich auch interessieren...

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.