top

Git

Allgemeines

Git ist ein version control system (VCS), welches 2005 von Linus Torvalds zur Entwicklung des Linux-Kernels ins Leben gerufen wurde. Es handelt sich dabei, im Gegensatz zu bspw. Apache Subversion, um ein verteiltes VCS. Es wird also nicht direkt an einem zentral gelagerten repository gearbeitet. Stattdessen wird vom remote repository eine lokale Kopie erstellt. Bearbeitet wird das local repository, das periodisch mit den Neuerungen im remote abgeglichen werden kann. Die lokalen Änderungen können auf das remote hochgeladen werden, wahlweise nachdem sie überprüft und gutgeheissen wurden.

Branches und commits

In aller Regel wird mit git in sog. branches gearbeitet. Diese repräsentieren einen gewissen, logischen Entwicklungsstrang. Ausserdem beinhalten sie eine Entwicklungsgeschichte, so dass sich Veränderungen inhaltlich und zeitlich zurückverfolgen lassen. Diese Veränderungen sind in sog. commits gebündelt, die Momentaufnahmen in Relation zum letzten commit darstellen. Branches sind hierarchisch geordnet und bilden einen Baum, an dessen Wurzel die ursprüngliche branch liegt, typischerweise - aber nicht zwingend - master oder main genannt. Es ist projektabhängig, wie viele branches existieren, welche Aufgaben sie haben, ob und wie sie fortgesetzt werden und in welchem Verhältnis sie zueinander stehen.

Gitflow

Es gibt verschiedene Methoden zur Organisierung und Verwaltung von branches. Diese spiegeln verschiedene Entwicklungsphilosophien, die Teamgrösse, die Anforderungen des Projekts und weitere Aspekte. Als Beispiel dient hier gitflow. Er war mal eine neuartige und sehr innovative Arbeitsweise, wurde in gewissen Kontexten aber von anderen abgelöst. Nichtsdestotrotz eignet sich der gitflow immer noch gut bei Projekten, in denen der code schon produktiv ist, unbedingt auch weiter funktionieren muss und deswegen eine strikte Kontrolle notwendig ist.

Im gitflow gibt es grundsätzlich zwei stetig fortlaufende branches:

  • master/main: Beinhaltet den produktiven code. Hier kommt nur rein, was wirklich bereit und geprüft ist. Gelangt etwas aus einer anderen branch in diese branch, stellt dies ein release und somit eine neue Version dar.
  • develop: Verläuft parallel zum master und beinhaltet den code, an dem laufend gearbeitet wird.

Typischerweise wird am develop branch nicht direkt gearbeitet, sondern in Form von feature branches. Diese «Unteräste» sollen spezifische Funktionalitäten implementieren und können ihrerseits wieder branches haben. Nach der Implementation sind sie abgeschlossen und gehen wieder ganz im develop branch auf. release branches dienen dazu, die Übernahme von code aus dem develop in den master vorzubereiten und zu testen. Funktioniert alles wie vorgesehen, fliessen die release branches schliesslich in den master.

Folgendes Bild illustriert den Arbeitsablauf (Quelle):

Eine vereinfachte Version davon ist der feature branch-Workflow. Hier werden die feature branches direkt vom main/master abgespalten, was für kleinere Projekte ausreicht.

 

GitHub und die Kommandozeile

Dienstleistungen wie GitHub oder GitLab sowie Editoren und IDEs bieten GUIs für die Verwendung von Git. Oftmals ist es aber notwendig, git in der Kommandozeile zu verwenden, weswegen man damit zumindest ein wenig vertraut sein sollte. Es folgen in Grundzügen die wichtigsten Befehle, welche wir verwendet haben:

  • git clone <repository>: Erstellt ein lokales Abbild des repositorys im aktuellen Verzeichnis. Dabei werden nicht nur die Dateien heruntergeladen, sondern auch Informationen über die aktuellen branches, die commit history usw.
  • git checkout (-b) <branch>: Ändert die branch, auf der aktuell gearbeitet wird, zur angegebenen branch. Mit dem Argument -b kann eine neue branch erstellt werden, die sich von der aktuellen branch abspaltet.
  • git commit (-a) <commit>: Hiermit wird in der aktuellen branch ein neuer commit, also ein neuer Punkt in der Geschichte der branch erstellt, der die gemachten Änderungen aufzeichnet. Dateien müssen dafür gestaget werden, mit dem Argument -a wird automatisch jede veränderte Datei gestaget und commited (ausgenommen neue Dateien, über die git nicht informiert wurde). Der commit sollte ausserdem einen sprechenden Namen erhalten.
  • git merge (--squash) <branch>: Schliesst die aktuelle branch ab und lässt sie in die angegebene branch einfliessen, z.B. wenn ein feature branch abgeschlossen ist. Es gibt verschieden Arten von merges, auf die später eingegangen wird.
  • git push (-u) origin <branch>: Lädt die lokalen commits der angegebenen branch auf das remote repository (origin), alle Änderungen vor dem push bleiben lokal! 

merge != merge

Bei einem merge wird eine branch abgeschlossen und fliesst wieder in die Mutterbranch. Wie oben angetönt, gibt es verschiedene Arten von merges, die sich in verschiedenen Situationen anbieten. Hier werden drei Typen kurz vorgestellt:

  • commit merge: Wird bei merge ohne besondere Argumente durchgeführt. Die Änderungen der Tochterbranch werden zusammen mit all ihren commits in die Mutterbranch übertragen und ein sog. merge commit erstellt, wo beide zusammenfliessen; die ganze Versionsgeschichte der branch landet ebenfalls darin. Ein Spezialfall kann eintreten, wenn die Mutterbranch seit dem Abspalten der Tochterbranch nicht verändert wurde: Dann wird einfach die Tochterbranch als Mutterbranch weitergeführt, da dies gleichwertig ist und nichts kopiert oder verändert werden muss. Dann wird auch kein merge commit erstellt - dies wird fast forwarding genannt.
  • squash merge: Wird merge mit der Option --squash ausgeführt, wird die Tochterbranch verflacht, d.h. alle commits und die Versionsgeschichte werden in einen einzigen commit zusammengefasst und dann per merge commit an die Mutterbranch angereiht. Zwar können Veränderungen dadurch schwierig nachzuvollziehen oder zu datieren sein, das hat aber gerade bei reger Arbeit (viele Tochterbranches) den Vorteil, dass der log der Mutterbranch dadurch kürzer und übersichtlicher wird.
  • rebase: Der rebase ist praktisch, wenn z.B. ein einzelner commit (etwa ein hotfix) eingespielt werden muss. Bei einem rebase werden die commits einer branch direkt an die Zielbranch vorne angehängt. Dabei wird also kein merge commit an die Mutterbranch angefügt, sondern ein normaler commit bzw. mehrere normale commits. Das Endergebnis aus Codeperspektive ist gleichwertig, wie wenn ein regulärer merge verwendet wird (bei einem merge commit mit fast-forwarding ist der Prozess ganz identisch). Es hat aber den Vorteil, dass der branching-Prozess aus dem log bereinigt wird, da diese für gewisse Änderungen nicht notwendig ist und bei vielen branches unübersichtlich werden kann. Auch rebase lässt sich mit der Option --squash verwenden, um mehrere commits der zu mergenden branch zu verflachen.

Kompetenzen

  • Arbeitstechniken: Git ist ein Werkzeug, um Code zu verwalten, zu entwickeln und um im Team zusammenzuarbeiten. Es lässt sich für fast beliebig grosse oder kleine Projekte verwenden.
  • Lernstrategien: Ich habe verschiedene Quellen zum Thema Workflows, Kommandozeilenbefehle, Arten von merges/rebase befragt sowie die offizielle Dokumentation und das offizielle Lehrmittel von git konsultiert. Damit habe ich mir in dieser Zusammenfassung selbstständig eine Übersicht verschafft.

ToDo:

  • Handlungskompetenzen mehr ausführen, besser eklären was Git (nicht GitHub) ist
  • Tags hinzufügen
  • aufsplitten in Git-Teil und Deployment-Teil?