Git Workflow für Embedded Software Entwicklung

In letzter Zeit hatte ich zunehmend Anfragen für Schulungen bzw. die Erstellung eines kundenspezifischen Git Workflows (s.a. Git-Handbuch) von Unternehmen aus dem Embedded Software Bereich. Dabei treten einige Eigenheiten im Vergleich zur Verwaltung des Sourcecodes einer klassischen Desktop Anwendung, Mobile App oder Webpage auf. Da ich diese nun schon mehrere Male am Telefon erläutert und Rückfragen beantwortet habe, schreibe ich einfach einige Gedanken dazu nieder.

Die offensichtlichste Abgrenzung zu den meisten anderen Software Bereichen ist, fast immer, die fehlende Möglichkeit Hotfixes für bereits erfolgte Releases durchzuführen. Die Seilwinde, die vielleicht in Russland in Betrieb ist, kann üblicherweise genau so wenig sicher erreicht und upgedatet werden, wie die Motorsteuerung des Laubbläsers meines Nachbarn, den ich gerade durchs Bürofenster höre 🙂

Eine weitere häufig anzutreffende Gemeinsamkeit bei Embedded Software ist, die Verwendung von Modulen oder Komponenten in vielen verschiedenen Produkten.

Die Frage nach dem „richtigen“ (d.h. für mich auch möglichst einfachen!) Git Workflow ist natürlich immer individuell zu beantworten, aber es gibt einige einfache Strategien um gute Entscheidungen zu treffen und nicht direkt in die falsche Richtung loszurennen.

Der wohl bekannteste Workflow ist Gitflow nach  Vincent Driessen. Viele, wenn nicht die meisten, Unternehmen verwenden Gitflow oder ein daran angelehntes Workflow Modell. Da bei der Embedded Software Entwicklung immer wieder Releases entstehen die entkoppelt sind und nicht ohne weiteres upgedatet werden können, macht es in vielen Fällen Sinn, statt eines Master Branches Release Branches für jedes erfolgte bzw. geplante Release anzulegen und so lange zu behalten wie diese Version im Einsatz ist.

Das Branching Modell eines typischen Repositories kann dann wie folgt aussehen:

Abb. 1: Branching Modell für ein Software Produkt mit vielen parallel existierenden Releases

Für ein Produkt ist das noch relativ einfach zu managen. Interessant wird es, wenn eine Firma mehrere Produkte hat. Diese bestehen häufig aus einem oder mehreren selbst entwickelten Modulen plus einen mehr oder weniger großen Anteil an produktspezifischen Sourcecode – manchmal sind das auch nur eine oder mehrere Konfigurationsdateien die das Verhalten der Module steuern. Als Beispiel nehmen wir an unsere Firma würde Produkte rund um die Motorsteuerung anbieten. Diese können natürlich in ganz unterschiedlichen Geräten verbaut sein. Exemplarisch wie in Abb. 2:

Abb. 2: 3 Produkte die jeweils ein gemeinsames Modul als Basis einbinden

Theoretisch kann man nun die Module einfach in ein Projekt kopieren und diese dann mit einchecken. Dann ist das entsprechende Modul in der richtigen Version vorhanden, allerdings hat man weder eine Historie des Modul Sourcecodes, noch kann im Laufe der Entwicklung nachvollzogen werden, welche Version des Moduls wann verwendet wurde. Dies ist dann m.E. nur z.B. am Dateinamen, wenn dieser eine Versionsnummer enthält, ersichtlich.

Um gemeinsam genutzte Module einzubinden sind 2 Best-Practises besonders bekannt:

  • git submodules
  • git subtree

Beide haben Ihre Vor- und Nachteile – je nach Situation im Unternehmen. Extrem kurz gefasst könnte eine Differenzierung etwa so aussehen:

  • Änderungen in submodules können einfacher gepushed werden, aber ein Pull ist aufwändiger.
    Der Grund dafür ist, dass ein Pointer auf das Original Repository gesetzt wird
  • Subtrees sind können einfacher gepulled werden, aber pushes sind komplexer, da eine Kopie des Orginal Repositories angelegt wird.

Für eine gemeinsam genutzte Bibliothek, wie in dem hier beschriebenen Fall, ist aus meiner Erfahrung wesentlich häufiger ein Pull als ein Push nötig. D.h. die einzelnen Produkte müssen häufiger (da ein Modul in vielen Produkten verwendet wird) upgedatet werden, als die Module.

Subtrees bieten hier 2 direkt ersichtliche Vorteile, durch die ich Subtrees in den allermeisten Fällen favorisiere:

  • Für den Entwickler ist die Einbindung eines Subtrees komplett transparent. Nach dem Clonen des Repository ist auch sämtlicher Code der Subtrees sofort verfügbar.
  • Er kann entwickeln wie in einem „normalen Repository“, d.h. ein Entwickler der nichts an den gemeinsam genutzten Modulen ändert, arbeitet wie gewohnt und muß nichts zusätzliches lernen.
    Einzig das Ändern innerhalb eines Subtrees erfordert zusätzliches Know-How.

Ein typisches Produkt Repository mit einem „lib“ Ordner in dem die extern verwalteten Module abgelegt sind könnte so aussehen:

Abb. 3: Repository Layout mit externen, als Subtree eingebunden, Modulen

In einem folgenden Artikel werde ich die Vorgehensweise für das Einrichten eines Projekts mit Subtrees detailliert beschreiben.