Commit-Messages, die wirklich nützen
git
HEAD
Git · Commit-Messages · Versionskontrolle · DevOps
Commit-Messages, die wirklich nützen
Klare Historie statt kryptischer Ein-Wort-Commits

Eine gute Commit-Message erklärt, warum eine Änderung nötig war, nicht nur was sich im Code verändert hat, denn das zeigt der Diff ohnehin. Mit der 50/72-Regel, dem Imperativ in der Betreffzeile, sauberen Trailern für Ticket-Referenzen und optional Conventional Commits wird aus der Git-Historie ein durchsuchbares Archiv, das Debugging und Code-Archäologie Monate später erheblich beschleunigt.

11 Min. Lesezeit 50/72-Regel Conventional Commits Git-Trailer

1. Warum die Commit-Message wichtiger ist als der Diff selbst

Der Diff zeigt zeilengenau, was sich geändert hat, aber er beantwortet nie, warum die Änderung nötig war. Ein Commit ohne aussagekräftige Nachricht zwingt jeden späteren Leser, die Motivation aus dem Kontext zu rekonstruieren: aus verlinkten Tickets, aus Slack-Verläufen oder schlimmstenfalls aus reinen Vermutungen. In einem Magento-Projekt mit hunderten Modulen und mehreren Entwicklern ist das kein theoretisches Problem, sondern tägliche Praxis bei jedem git blame auf eine kritische Codezeile.

Eine gute Commit-Message ist Dokumentation, die niemals veraltet, weil sie exakt in dem Moment entsteht, in dem der Kontext noch frisch im Kopf ist. Sie kostet beim Schreiben zwei Minuten, spart aber bei jedem späteren Debugging, Code-Review oder Bugfix-Backport ein Vielfaches davon. Wer Commit-Messages als lästige Pflichtübung behandelt, verschiebt die Kosten nur auf spätere Kollegen, oft auf sich selbst in sechs Monaten, wenn der Kontext längst verschwunden ist.

2. Die 50/72-Regel: Betreffzeile und Body richtig formatieren

Die 50/72-Regel ist die am längsten etablierte Formatierungskonvention für Commit-Messages und stammt ursprünglich aus den Git-eigenen Dokumentationsempfehlungen. Die Betreffzeile bleibt bei maximal 50 Zeichen, der Body wird bei 72 Zeichen pro Zeile umgebrochen. Der Grund ist rein praktisch: git log --oneline schneidet lange Betreffzeilen ab, und git log im Standardformat rückt den Text um vier Leerzeichen ein, sodass ein 72-Zeichen-Body in einem klassischen 80-Spalten-Terminal ohne zusätzlichen Zeilenumbruch lesbar bleibt.

Zwischen Betreffzeile und Body steht immer eine Leerzeile, sonst interpretiert Git die gesamte Nachricht als eine einzige lange Zeile in Tools wie git shortlog oder in GitHub-Commit-Listen. Viele Editoren und IDEs, auch PhpStorm, markieren die 50- und 72-Zeichen-Grenze visuell direkt in der Commit-Dialogbox. Wer diese Grenzen konsequent ignoriert, produziert Historien, die in git log --oneline, in Pull-Request-Listen und in Terminal-Ausgaben gleichermaßen unleserlich werden.

3. Imperativ und eine präzise, aussagekräftige Betreffzeile

Die Betreffzeile folgt dem Imperativ, nicht der Vergangenheitsform. „Fix null pointer in checkout observer" statt „Fixed null pointer" oder „Fixing null pointer". Der Test dafür ist simpel und funktioniert in jeder Sprache: Die Zeile muss sich grammatikalisch in den Satz „If applied, this commit will ..." einfügen lassen. Git selbst folgt dieser Konvention bei automatisch generierten Commits wie Merges und Reverts, weshalb der eigene Stil konsistent zum restlichen Werkzeug bleiben sollte.

Eine gute Betreffzeile ist zudem spezifisch, nicht generisch. „Fix bug" oder „Update code" tragen keine Information, die nicht ohnehin aus dem Diff hervorgeht. „Fix null pointer in checkout observer when quote has no items" grenzt sofort ein, welches Modul und welches Szenario betroffen ist, ohne dass der Body überhaupt gelesen werden muss. Wichtig: kein Punkt am Ende der Betreffzeile, das ist Konvention seit den frühen Linux-Kernel-Commit-Guidelines und hat sich seither in praktisch jedem Open-Source-Projekt durchgesetzt.

4. Das Warum erklären, nicht das Was: der Diff zeigt es bereits

Der häufigste Fehler in Commit-Bodies ist die Wiederholung dessen, was der Diff ohnehin zeigt. „Changed the observer class to check for null" beschreibt nur den Code, nicht die Motivation dahinter. Der Body sollte stattdessen erklären, warum die Änderung notwendig war: welcher Bug reproduziert wurde, welche Alternative verworfen wurde und welche Nebenwirkung von Reviewern erwartet werden sollte.

Bei einem Magento-Observer-Fix bedeutet das konkret: nicht nur „Null-Check hinzugefügt", sondern der Kontext, dass der Observer bei Gastbestellungen ohne gesetzte Kundengruppe abstürzt, weil ein Drittanbieter-Modul das Event vor der Standardvalidierung auslöst. Diese Information steht nirgendwo im Diff, ist aber für jeden entscheidend, der die Stelle später anfasst, um dieselbe Falle nicht erneut zu treffen oder den Fix versehentlich rückgängig zu machen.

5. Ticket-IDs referenzieren und Trailer nutzen (Refs, Fixes, Co-authored-by)

Trailer sind strukturierte Metadaten-Zeilen am Ende des Commit-Bodies im Format Key: Value, die Git und Tools wie GitHub oder Jira maschinell auswerten können. Refs: JIRA-1234 verlinkt einen Commit mit einem Ticket, ohne das Ticket automatisch zu schließen. Fixes: #456 schließt ein GitHub-Issue automatisch beim Merge in den Default-Branch. Co-authored-by: Name <email> würdigt Pair-Programming-Partner sichtbar in der Historie und in GitHub-Beitragsstatistiken.

Wichtig ist die klare Trennung zwischen Freitext-Body und Trailer-Block: Trailer stehen immer als letzter Absatz, durch eine Leerzeile vom Fließtext getrennt, jeweils eine Zeile pro Trailer. git interpret-trailers kann diese Zeilen automatisiert parsen und validieren, was sich in CI-Pipelines nutzen lässt, um Commits ohne Ticket-Referenz konsequent abzulehnen, sofern das im Team verpflichtend festgelegt ist.


$ git commit

Fix null pointer in checkout observer

The Sales_Order_Place_After observer accessed the customer
group attribute directly. Guest orders created via the B2B
quote API do not set this attribute before the event fires,
which caused a fatal error during checkout.

Add a null check and fall back to the default customer group
id instead of failing the whole order placement.

Refs: JIRA-2481
Fixes: #742
Co-authored-by: Anna Schmidt <anna.schmidt@mironsoft.de>

6. Praxisbeispiel: eine schlechte Commit-Message wird zur guten

Am konkretesten wird der Unterschied an einem realen Fall: Ein Magento-Observer für sales_order_place_after wirft eine Exception, weil er auf ein Attribut zugreift, das bei Angeboten aus dem B2B-Modul noch nicht gesetzt ist. Die schlechte Version begnügt sich mit einem einzigen Wort, die gute Version erklärt Ursache, Lösung und Kontext in wenigen Zeilen, exakt nach der 50/72-Regel und mit einem Trailer für die Ticket-Referenz.

Der Unterschied zeigt sich nicht beim Schreiben, sondern Monate später beim git blame auf genau diese Zeile. Die gute Version beantwortet sofort, warum die Prüfung existiert, sodass niemand sie versehentlich als überflüssig entfernt. Die schlechte Version zwingt zur erneuten Recherche, im schlimmsten Fall zur erneuten Reproduktion des ursprünglichen Bugs, den ein Kollege vor Monaten bereits gelöst hatte.


# BAD: no context, cannot be searched, hides the actual problem
git commit -m "fix bug"

# BAD: describes the diff, not the reasoning behind it
git commit -m "added null check in observer"

# GOOD: imperative subject, 50/72 rule, explains the why
git commit -m "Fix null pointer in checkout observer" -m "
The Sales_Order_Place_After observer read the customer group
attribute directly. Guest orders from the B2B quote API do
not set this attribute before the event fires, causing a
fatal error during checkout for a subset of B2B customers.

Fall back to the default customer group id instead of
assuming the attribute is always present.

Refs: JIRA-2481"

7. Commit-Templates mit git config commit.template

Ein Commit-Template ist eine lokale Textdatei, die Git bei jedem git commit ohne -m-Flag im Editor vorausfüllt. Mit git config commit.template ~/.gitmessage wird die Datei global für alle Repositories aktiviert. Das Template kann Kommentarzeilen mit Erinnerungen an die 50/72-Regel enthalten, Platzhalter für Trailer und sogar teamweite Pflichtfelder wie eine Ticket-Referenz, die vor dem Commit sichtbar sind, statt nachträglich mühsam ergänzt zu werden.

In einem Team-Setting lässt sich das Template versionieren und über git config --local commit.template .gitmessage pro Repository setzen, sodass jeder Entwickler beim Klonen dieselbe Struktur erhält, sofern ein Setup-Skript den Config-Wert automatisch für alle Beteiligten setzt. Kommentarzeilen, die mit # beginnen, werden von Git beim finalen Commit automatisch entfernt und tauchen niemals in der Historie auf.


# Store the template in the repo and share it with the team
cat > .gitmessage <<'EOF'
# Subject: max 50 chars, imperative mood, no trailing period
#
# Body: wrap at 72 chars. Explain WHY, not WHAT (the diff
# already shows what changed). Mention alternatives you
# rejected and side effects reviewers should watch for.
#
# Refs: JIRA-XXXX
# Fixes: #XXXX
EOF

git config --local commit.template .gitmessage

8. Conventional Commits als optionale Konvention

Conventional Commits strukturieren die Betreffzeile nach dem Schema type(scope): description, zum Beispiel fix(checkout): handle missing customer group in guest orders. Die Typen feat, fix, chore, refactor, docs und test sind maschinenlesbar und ermöglichen automatisches Generieren von Changelogs sowie automatisches Semantic Versioning über Tools wie semantic-release oder commitlint.

Für kleinere Teams oder interne Magento-Projekte ohne öffentliches Package-Release ist Conventional Commits eine Option, keine Pflicht: die 50/72-Regel und der erklärende Body bleiben in jedem Fall wichtiger als das Präfix. Wer sich für das Format entscheidet, sollte es konsequent per commitlint-Hook in der CI-Pipeline erzwingen, sonst driftet die Konvention innerhalb weniger Wochen wieder auseinander, weil einzelne Commits das Schema schlicht ignorieren.


# Conventional Commits: type(scope): description
git commit -m "fix(checkout): handle missing customer group in guest orders"
git commit -m "feat(catalog): add bulk price import via CSV"
git commit -m "refactor(observer): extract validation into a service"
git commit -m "chore(deps): bump hyva/theme-fallback to 1.3.2"

# BREAKING CHANGE footer triggers a major version bump
git commit -m "feat(api): remove deprecated v1 product endpoint" -m "
BREAKING CHANGE: v1 endpoints are removed. Clients must
migrate to /rest/V2/products before the next release."

9. Historie produktiv lesen: git log, git blame und Vergleich

Eine konsistente Commit-Historie zahlt sich vor allem beim git log und git blame aus. Mit git log --oneline --graph --decorate lässt sich der Verlauf eines Features in Sekunden überblicken, vorausgesetzt die Betreffzeilen sind aussagekräftig und nicht alle nur „wip" oder „fix". git log --format mit eigenem Pretty-Format zeigt genau die Felder, die für Code-Archäologie relevant sind: Autor, Datum, Betreffzeile und gegebenenfalls Trailer-Inhalte.

git blame -L 40,60 datei.php zeigt für jede Zeile den zugehörigen Commit, und erst eine gute Message macht diese Information tatsächlich nutzbar. Bei schlecht dokumentierten Historien folgt auf git blame meist ein zweiter Schritt: git show <hash>, um den vollständigen Diff zu lesen, und im schlimmsten Fall eine Nachfrage beim Autor, sofern der noch im Team ist. Die folgende Tabelle vergleicht schlechte und gute Praxis auf einen Blick.


# Compact, decorated overview of recent history
git log --oneline --graph --decorate -20

# Custom pretty-format for code archaeology
git log --format="%h %ad | %s [%an]" --date=short -- app/code/Mironsoft/SeoSuite

# Show trailers explicitly for a given commit
git show --format="%B" -s a1b2c3d | git interpret-trailers --parse

# Find who touched a specific line and why
git blame -L 40,60 app/code/Mironsoft/SeoSuite/Observer/SalesOrderPlaceAfter.php
Aspekt Schlechte Praxis Gute Praxis Vorteil
Betreffzeile fix stuff Fix null pointer in checkout observer Beschreibt konkret Modul und Fehler
Formatierung Alles in einer Zeile, über 100 Zeichen Betreffzeile <= 50 Zeichen, Body bei 72 umgebrochen Lesbar in git log --oneline und Terminals
Inhalt des Body Wiederholt den Diff ("added null check") Erklärt Ursache und Kontext des Bugs Verhindert erneute Recherche bei git blame
Ticket-Referenz Fehlt komplett oder nur im Slack-Chat Trailer Refs: JIRA-1234 im Commit Nachvollziehbar auch nach Slack-Verlust
Umfang der Änderung Ein Commit für fünf unabhängige Fixes Ein Commit pro logischer Änderung Ermöglicht gezieltes Revert und Cherry-Pick

In der Praxis hängen alle fünf Aspekte zusammen: Eine unklare Betreffzeile führt fast immer auch zu einem Body ohne Kontext, und beides zusammen macht spätere Cherry-Picks und Reverts unnötig riskant. Wer die Empfehlungen aus der Tabelle konsequent umsetzt, gewinnt eine Historie, die sich wie eine durchsuchbare Dokumentation liest statt wie ein Nebenprodukt der Versionsverwaltung.

Mironsoft

Git-Workflows, Code-Reviews und Entwickler-Tooling für Magento-Teams

Saubere Commit-Historie im Team etablieren?

Wir richten Commit-Konventionen, Templates und Review-Prozesse für euer Magento-Team ein, inklusive CI-Checks für Betreffzeilen-Länge, Trailer-Pflichtfelder und automatisierter Changelog-Generierung.

Commit-Konventionen

50/72-Regel, Imperativ und Trailer-Standards für das gesamte Team dokumentieren

Git-Hooks & CI

commitlint, Templates und Pre-Commit-Checks in eure Pipeline integrieren

Code-Review-Prozesse

Pull-Request-Workflows mit klaren Standards für Magento- und Hyvä-Projekte aufbauen

10. Zusammenfassung

Die wichtigsten Regeln für Commit-Messages, die wirklich nützen, lösen alle dasselbe Grundproblem: Eine Historie ohne Kontext wird zur Blackbox, sobald der ursprüngliche Autor die Details vergessen hat. Die 50/72-Regel hält Betreffzeile und Body lesbar in git log --oneline und im Terminal. Der Imperativ-Stil sorgt für Konsistenz mit Gits eigenen automatisch generierten Commits. Der Body erklärt das Warum, nicht das Was, denn der Diff zeigt das Was ohnehin bereits vollständig. Trailer wie Refs, Fixes und Co-authored-by machen Ticket-Referenzen und Mitwirkende maschinenlesbar.

Der größte Hebel liegt in der konsequenten Anwendung über das gesamte Team hinweg, nicht in Einzelfällen. Ein Commit-Template mit git config commit.template senkt die Einstiegshürde spürbar, weil die Struktur bereits vorgegeben ist. Conventional Commits sind dabei ein optionales, zusätzliches Schema für automatisierte Changelogs, kein Ersatz für einen erklärenden Body. Wer diese Prinzipien konsequent verfolgt, gewinnt eine Git-Historie, die bei jedem git blame und jeder Code-Archäologie tatsächlich weiterhilft.

Commit-Messages, die wirklich nützen, Das Wichtigste auf einen Blick

50/72-Regel

Betreffzeile max. 50 Zeichen, Body bei 72 Zeichen umgebrochen, Leerzeile dazwischen. Macht die Historie in git log lesbar.

Imperativ und Warum

Betreffzeile im Imperativ, Body erklärt die Motivation, nicht den ohnehin sichtbaren Diff-Inhalt.

Trailer & Ticket-Referenzen

Refs, Fixes und Co-authored-by als strukturierte Metadaten am Ende des Bodies, maschinenlesbar.

Templates & Konventionen

git config commit.template für konsistente Struktur, Conventional Commits optional für automatisierte Changelogs.

11. FAQ: Commit-Messages, die wirklich nützen

1Warum ist die Commit-Message wichtiger als der Diff?
Der Diff zeigt nur was sich geändert hat, niemals warum. Die Commit-Message dokumentiert die Motivation, solange der Kontext noch frisch ist.
2Was bedeutet die 50/72-Regel genau?
Betreffzeile maximal 50 Zeichen, Body bei 72 Zeichen pro Zeile umgebrochen. Hält git log --oneline und den eingerückten Standard-Log lesbar.
3Warum sollte die Betreffzeile im Imperativ stehen?
Passt grammatikalisch in "If applied, this commit will ..." und entspricht dem Stil von Gits eigenen automatischen Commits wie Merges.
4Was gehört in den Body einer Commit-Message?
Grund der Änderung, verworfene Alternativen, mögliche Nebenwirkungen. Was sich geändert hat, zeigt bereits der Diff.
5Was ist ein Git-Trailer?
Eine strukturierte Key: Value Zeile am Ende des Bodies, zum Beispiel Refs: JIRA-1234, maschinell auswertbar über git interpret-trailers.
6Wie richte ich ein Commit-Template ein?
git config commit.template ~/.gitmessage global, oder --local commit.template .gitmessage pro Repository. Öffnet die Datei als Vorlage im Editor.
7Was sind Conventional Commits?
Format type(scope): description für maschinenlesbare Betreffzeilen, ermöglicht automatische Changelogs und Semantic Versioning. Bei Package-Releases besonders sinnvoll.
8Wie zeige ich eigene Felder mit git log --format an?
Mit einem Pretty-Format-String wie git log --format="%h %ad | %s [%an]" --date=short für Hash, Datum, Betreffzeile und Autor.
9Wie schließe ich ein GitHub-Issue automatisch?
Mit dem Trailer Fixes: #456 oder Closes: #456. Beim Merge in den Default-Branch schließt GitHub das referenzierte Issue automatisch.
10Unterschied zwischen Refs und Fixes?
Refs verlinkt ohne zu schließen, geeignet für Teilschritte. Fixes schließt das Issue automatisch beim Merge, geeignet für vollständige Lösungen.