Überblick im Versionskontrollsystem Conventional Commits – mehr Struktur für Git

Von Mirco Lang |

Anbieter zum Thema

Der Commit ist viel mehr als nur eine Remote-Version des Speicherdialogs im Versionskontrollsystem – zumindest mit durchdachten Commit-Nachrichten. Conventional Commits versucht, mit einer Konvention den richtigen Weg zu ebnen.

Gewisse Konventionen helfen dabei, bei Änderungen eine gewisse Übersicht im Versionskontrollsystem zu behalten.
Gewisse Konventionen helfen dabei, bei Änderungen eine gewisse Übersicht im Versionskontrollsystem zu behalten.
(Bild: Gerd Altmann (geralt) / Pixabay)

Nicht selten lauten Commit-Nachrichten in Git „typos“, „small changes“, „added new foobar feature“ und so weiter – kurze, lieblose Wortfetzen, die wohl eher als Last denn als Chance begriffen werden. Doch selbst wenn sich Nutzer Mühe geben und aussagekräftige Nachrichten hinterlassen, dürfte das Ergebnis weit entfernt von jeglicher Konsistenz sein. In der Praxis gibt es dann häufig einfach interne Vorgaben, wie die Mitteilungen in etwa aussehen sollten.

Das Projekt Conventional Commits versucht sich hier an einer Standardisierung, angelehnt vor allem an die Commit-Richtlinien von Angular. Zudem wird auch eine inhaltliche Nähe zu Semantic Versioning aufgeführt, ein Projekt, das sinnvolle Versionierungen befördert.

Die Conventional-Commits-Richtlinen sorgen dafür, dass Commit-Nachrichten einheitlich, verständlich und nützlich sind – und so lassen sich automatisch aussagekräftige Changelogs erstellen! Solche Messages machen es einfacher, den Fortschritt von Projekten nachzuvollziehen, was sowohl für Kollegen als auch interessierte Neueinsteiger wertvoll ist.

Die Möglichkeiten gehen aber noch über die Verständlichkeit hinaus: Standardisierte Schlagwörter in Commit-Nachrichten könnten freilich auch genutzt werden, um weitere Skripte anzutriggern, beispielsweise zum Anstoßen von CI-Pipelines. Im Folgenden stellen wir Ihnen die Conventional-Commits-Konvention im Detail vor und zeigen, wie Sie deren Einhaltung automatisiert über den Befehl „git commit“ verifizieren können.

Conventional Commits

Die Konvention ist extrem simpel gehalten und sieht grundsätzlich so aus:

<type>[optional scope]: <description>
[optional body]
[optional footer(s)]

Die Commit-Nachrichten sollen also mit einer zwingenden ersten Zeile beginnen, die den Typ des Commits (etwa feat für eine neue Funktion) angibt, dazu eine kurze Beschreibung und optional einen Scope (zum Beispiel gui). Beispiel:

feat(gui): Neue Icons eingefügt

Welche Typen Sie nutzen, liegt dabei ganz bei Ihnen – feat und fix sind zwar Standards, die wohl in fast jedem Kontext sinnvoll sind; aber letztlich sagt die Konvention nur, dass und wie ein Typ angegeben werden muss, aber nicht, um welche Typen es sich handeln soll oder gar muss.

Der optionale Body wird schlicht durch eine Leerzeile abgetrennt und kann beliebig gestaltet werden. Natürlich mit dem Ziel, den Commit ausführlich zu beschreiben – oder auch, um Issues und dergleichen zu adressieren:

Neue Icons für das Dark-Theme aus dem freien Icon-Paket foobar hinzugefügt.
Die Icons liegen unter „/assets/images/icons“ und dürfen gemäß Lizenz bearbeitet werden.
Resultat aus ISSUE-123 und JIRA-ABC.

Der Footer ist ebenfalls optional, wobei auch mehrer Footer erlaubt sind. Diese werden vom Body wiederum per Leerzeile abtegrennt und folgen dem Muster „Schlagwort: text“ (inspiriert durch die Git Trailer Conventions):

Mocked-about-by: Peter
BREAKING CHANGE: Neues Nutzer-Interface

Üblicherweise müssen Footer-Schlagwörter ohne Leerzeichen auskommen, „BREAKING CHANGES“ ist die Ausnahme: An dieser Stelle sollen große Änderungen kurz angefeatured werden, die auch in den automatisch generierten Changelogs in der Art auftauchen – unter einer eigenen Überschrift.

Im Grunde ist das bereits die ganze Konvention, der Rest sind Kleinigkeiten. Beispielsweise gibt es eine zweite Möglichkeit, „Breaking Changes“ zu kennzeichnen – nämlich direkt im Header:

feat(gui)!: Neue Icons eingefügt

Hier ist lediglich das Ausrufungszeichen hinzugekommen, das eben solche Breaking Changes impliziert. Ist ein solches Ausrufungszeichen gesetzt, kann auf die Kennzeichnung im Footer verzichtet werden.

Wenn Sie des öfteren Konventionen, Standards oder Gesetze lesen, wird Ihnen die Verwendung von Wörtern wie SOLL, MUSS, KANN, DARF oder DARF NICHT vertraut sein – und die gesamte Spezifikation der Conventional Commits besteht lediglich aus 16 derartig formulierten Punkten.

Nun ist eine Konvention sicherlich eine schöne Sache, aber ohne konkrete Implementierung eben nicht mehr als eine Selbstverpflichtung. Es gibt allerlei Tools, die von Conventional Commits Gebrauch machen, besonders schlicht und einfach erledigt das das Kommandozeilenwerkzeug Git Conventional Commits.

Conventional Commits in der Git-Praxis

Git Conventional Commits basiert auf Node.js, entsprechend wird es über dessen Paketmanager npm installiert:

npm install --global git-conventional-commits

Anschließend wird die benötigte Konfigurationsdatei „git-conventional-commits.json“ erstellt:

git-conventional-commits init

Hier eine gekürzte Form der Datei mit den zwei Hauptbereichen „convention“ und „changelog“:

{
  "convention" : {
    "commitTypes": [
      "feat",
      "fix",
      "perf"
    ],
    "commitScopes": [],
    "releaseTagGlobPattern": "v[0-9]*.[0-9]*.[0-9]*",
    "issueRegexPattern": "(^|\\s)#\\d+(\\s|$)"
  },
  "changelog" : {
    "commitTypes": [
      "feat",
      "fix",
      "perf"
    ],
    "includeInvalidCommits": true,
    "commitScopes": [],
    "commitIgnoreRegexPattern": "^WIP ",
    "headlines": {
      "feat": "Features",
      "fix": "Bug Fixes",
      "perf": "Performance Improvements",
      "breakingChange": "BREAKING CHANGES"
    },
    "commitUrl": "https://github.com/ACCOUNT/REPOSITORY/commit/%commit%",
    "commitRangeUrl": "https://github.com/ACCOUNT/REPOSITORY/compare/%from%...%to%?diff=split",
    "issueUrl": "https://github.com/ACCOUNT/REPOSITORY/issues/%issue%"
  }
}

Es gibt zwei Abschnitte, beginnend mit der Konvention („convention“). Im Bereich „commitTypes“ werden die erlaubten Commit-Typen festgelegt; die Scopes/Themen im Bereich „commitScopes“ bleiben hier undefiniert, so dass entsprechend beliebige Themen gesetzt werden dürfen.

Jetzt Newsletter abonnieren

Täglich die wichtigsten Infos zu Softwareentwicklung und DevOps

Mit Klick auf „Newsletter abonnieren“ erkläre ich mich mit der Verarbeitung und Nutzung meiner Daten gemäß Einwilligungserklärung (bitte aufklappen für Details) einverstanden und akzeptiere die Nutzungsbedingungen. Weitere Informationen finde ich in unserer Datenschutzerklärung.

Aufklappen für Details zu Ihrer Einwilligung

Es folgen Regexe für Issue- und Release-Angaben, um diese – sofern vorhanden – ebenfalls parsen zu können. Der Abschnitt „changelog“ legt nun fest, welche Commit-Typen aufgenommen werden, ob Commits mit ungültigen Nachrichten berücksichtigt weren, wie die Überschriften aussehen und wie die Links auf Issues und Commits zusammengebaut werden.

Tipp: Über den Befehl „git-conventional-commits version“ wird nicht nur die Version des Tools ausgegeben, sondern auch Warnmeldungen zu allen alten Commits, dass diese nicht der Konvention entsprechen. Das ist so zwar nicht dokumentiert, funktioniert aber zumindest hier auf mehreren Testrechnern.

Eine manuelle Überprüfung alter Nachrichten ist aber nicht Sinn der Sache, vielmehr soll das ja automatisch geschehen – und das ist Aufgabe eines Git-Hook. Standardmäßig liegen diese in einem Git-internen Verzeichnis und werden nicht via „git clone“ oder „git pull“ verteilt. Hier ist es aber gewünscht, dass die Hooks geteilt werden, also kommen sie in ein eigenes Verzeichnis:

Erstellen Sie also einen entsprechenden Ordner im Repository, teilen Sie Git diese Konfiguration mit, erstellen Sie die Hook-Datei „commit-msg“ und machen Sie sie ausführbar:

mkdir .git-hooks
git config core.hooksPath .git-hooks
touch .git-hooks/commit-msg
chmod +x .git-hooks/commit-msg

Der commit-msg-Hook wird durch den Befehl „git commit“ ausgelöst und hat folgenden simplen Inhalt:

#!/bin/sh
git-conventional-commits commit-msg-hook "$1"

Die Commit-Nachrichten-Datei wird hier schlicht über $1 übergeben.

Damit ist die Überprüfung der Conventional-Commits-Konvention via Hook fertig eingerichtet. Die Arbeit damit läuft nun beispielhaft so ab:

… einige Änderungen …
git commit -am 'feat(gui)!: Neues Super-Feature'
git-conventional-commits changelog --release 2.1.4 --file „changelog_2.1.4.md“

Der erstellte Changelog sieht etwa so aus:

2.1.4 2022-08-30 (c9988fb...0defb5c)
Features
Neues Super-Feature (50ce88g)    guiBEAKING CHANGESgui Neues Super-Feature

Der Kopf eines automatisch generierten Changelogs.
Der Kopf eines automatisch generierten Changelogs.
(Bild: Lang / DownMarker)

Das Layout ist eher hemdsärmelig vom originalen Markdown übernommen, aber grundsätzlich sollte sich erkennen lassen, dass so ein wirklich gut lesbarer Changelog entsteht, der anstelle der drei Punkte in der Mitte freilich noch reichlich weitere Informationen und Einträge liefert.

Was auch klar sein sollte: Wenn die Commit-Nachrichten nicht dem Standard entsprechen, wird der git-commit-Befehl mit einer passenden Fehlermeldung unterbrochen.

Wenn die Conventional Commits via Git produktiv eingesetzt werden sollen, würde sich zudem noch der Einsatz von pre-commit anbieten, einem Framework zum Verwalten von Git-Hooks. Schließlich ist es unter Umständen sinnvoll, die Prüfung vom Client auf den Server zu verlegen, damit alle Kontributoren gezwungen sind, sich an die Konvention zu halten. Im obigen Setup wird der commit-msg-Hook zwar geteilt, aber das Kommandozeilenwerkzeug git-conventional-commits müsste auf jedem Client installiert werden.

Conventional Commits mag derzeit noch ein eher kleines Projekt sein, überzeugt aber mit sehr guten Ansätzen und Ideen – über die sich so ziemlich alle Git-Nutzer Gedanken machen sollten.

(ID:48558012)