Release Notes aus Git Log automatisch generieren
AI generated
$_
#!/
Bash · Git · Release-Management · Real World
Release Notes aus Git Log automatisch generieren
lesbare Ankündigungen statt roher Commit-Listen

Release Notes richten sich an Nutzer und Stakeholder, nicht an Entwickler, und brauchen deshalb einen anderen Ton als ein technischer Changelog. Dieser Artikel zeigt, wie ein Bash-Skript aus dem Commit-Bereich zwischen zwei Tags automatisch lesbare Release Notes mit Mitwirkenden, hervorgehobenen Highlights und Vergleichslink erzeugt und direkt an die GitHub oder GitLab Release API übergibt.

18 Min. Lesezeit git log · git shortlog · curl · GitHub API Bash 4.x · 5.x · Git 2.3x

1. Release Notes sind kein Changelog

Ein Changelog protokolliert kontinuierlich jede einzelne technische Änderung eines Projekts, oft in einer einzigen, stetig wachsenden Datei. Release Notes dagegen sind eine punktuelle Zusammenfassung genau einer Veröffentlichung, geschrieben für Anwender, Kunden oder das Management, die keine Commit-Historie lesen wollen. Wer diese Unterscheidung ignoriert, produziert entweder zu technische Release Notes oder einen Changelog, der wie eine Marketing-Nachricht klingt.

Der Kern eines guten Skripts für Release Notes liegt darin, aus derselben Git-Historie zwei völlig unterschiedliche Ausgaben zu erzeugen: technische Details für Entwickler bleiben im Changelog, während die Release Notes nur die für Nutzer sichtbaren, spürbaren Änderungen hervorheben, angereichert um Kontext wie Mitwirkende und einen direkten Vergleichslink.

Dieser Artikel baut ein Bash-Skript, das genau diese Trennung umsetzt: Es liest denselben Commit-Bereich wie ein Changelog Generator aus, wendet aber andere Filter- und Formatierungsregeln an, um am Ende Release Notes zu erzeugen, die sich direkt auf einer GitHub- oder GitLab-Release-Seite veröffentlichen lassen.

2. Den Commit-Bereich zwischen zwei Tags bestimmen

Anders als ein kontinuierlich aktualisierter Changelog beziehen sich Release Notes immer auf genau ein abgeschlossenes Zeitfenster, meist den Bereich zwischen zwei aufeinanderfolgenden Tags. Das Skript muss deshalb sowohl den aktuellen als auch den vorherigen Tag zuverlässig ermitteln, typischerweise über git tag --sort=-v:refname, um nach Versionsnummer statt nach Erstellungsdatum zu sortieren.

Ein häufiger Stolperstein bei Release Notes: Lightweight-Tags ohne Annotation verhalten sich in einigen Git-Befehlen anders als annotierte Tags. Ein robustes Skript sollte deshalb explizit mit git for-each-ref arbeiten, das beide Tag-Typen einheitlich behandelt, statt sich auf git describe allein zu verlassen, das primär für annotierte Tags gedacht ist.


#!/usr/bin/env bash
# determine-range.sh — find the commit range for release notes
set -euo pipefail

determine_release_range() {
  local -a tags=()
  while IFS= read -r tag; do
    tags+=("$tag")
  done < <(git for-each-ref --sort=-v:refname --format='%(refname:short)' refs/tags)

  if [[ ${#tags[@]} -lt 1 ]]; then
    echo "[ERROR] No tags found in repository" >&2
    exit 1
  fi

  local current_tag="${tags[0]}"
  local previous_tag="${tags[1]:-}"

  echo "current=${current_tag}"
  echo "previous=${previous_tag}"
  echo "range=${previous_tag:+${previous_tag}..}${current_tag}"
}

determine_release_range

3. Mitwirkende automatisch auflisten

Ein Merkmal, das Release Notes von einem technischen Changelog unterscheidet, ist die namentliche Würdigung der Mitwirkenden. Gerade in Open-Source-Projekten oder größeren Teams schafft eine Liste der Autoren pro Release Anerkennung und Transparenz darüber, wer an einer Version beteiligt war. git shortlog liefert diese Information bereits fertig aggregiert und nach Anzahl der Commits sortiert.

Für Release Notes sollte die Liste zusätzlich dedupliziert werden, falls dieselbe Person mit unterschiedlichen E-Mail-Adressen committet hat, etwa privat und geschäftlich. Eine einfache Normalisierung über den Namen statt der E-Mail-Adresse reduziert doppelte Einträge, ohne eine vollständige Mailmap-Datei pflegen zu müssen.


#!/usr/bin/env bash
# list-contributors.sh — list unique contributors for the release notes
set -euo pipefail

list_contributors() {
  local range="$1"
  git log "$range" --no-merges --format='%an' | sort -u
}

range="${1:?Usage: list-contributors.sh <range>}"

echo "## Contributors"
echo
list_contributors "$range" | while IFS= read -r name; do
  echo "- ${name}"
done

4. Wichtigste Änderungen als Highlights hervorheben

Ein reiner Abdruck aller Commit-Betreffzeilen wäre für Release Notes zu unstrukturiert, da nicht jede Änderung für Nutzer gleich relevant ist. Eine praktikable Heuristik: Commits mit dem Präfix feat: gelten als potenzielle Highlights, während chore:, test: und ci: für Nutzer irrelevant sind und aus den Release Notes herausgefiltert werden sollten, obwohl sie im technischen Changelog durchaus auftauchen dürfen.

Zusätzlich lohnt sich ein Blick auf die Länge und den Detailgrad der Commit-Nachricht: Nachrichten mit einem ausführlichen Body enthalten häufig genau die Beschreibung, die sich fast unverändert in die Release Notes übernehmen lässt, während knappe Einzeiler oft zu technisch formuliert sind, um sie direkt an Endnutzer zu kommunizieren.


#!/usr/bin/env bash
# extract-highlights.sh — pick user-facing changes for release notes
set -euo pipefail

extract_highlights() {
  local range="$1"
  git log "$range" --no-merges --format='%s' |
    grep -E '^feat(\(.+\))?!?: ' |
    sed -E 's/^feat(\(.+\))?!?: //'
}

range="${1:?Usage: extract-highlights.sh <range>}"

echo "## Highlights"
echo
extract_highlights "$range" | while IFS= read -r line; do
  echo "- ${line^}"
done

5. Nach nutzerrelevanten Kategorien gruppieren

Während ein Changelog Generator streng nach Commit-Typ gruppiert, orientieren sich gute Release Notes an nutzerrelevanten Kategorien wie Neue Funktionen, Verbesserungen und Behobene Probleme. Diese Kategorien überschneiden sich zwar mit den technischen Commit-Typen, sind aber bewusst umformuliert, um für Leser ohne technischen Hintergrund verständlich zu bleiben.

Ein Beispiel: Ein perf: Commit wird im technischen Changelog als Performance-Optimierung geführt, in den Release Notes aber möglicherweise als Verbesserung unter Schnellere Ladezeiten zusammengefasst, gemeinsam mit anderen performance-relevanten Änderungen, die für Nutzer als ein einziger, spürbarer Vorteil erscheinen.

Auch wenn Release Notes primär für nicht-technische Leser gedacht sind, sollte am Ende immer ein Link zum vollständigen Diff zwischen dem aktuellen und dem vorherigen Tag stehen. Interessierte technische Nutzer, etwa Partner-Entwickler oder das eigene Support-Team, können so bei Bedarf jederzeit tiefer in die tatsächlichen Code-Änderungen einsteigen, ohne dass die Release Notes selbst mit Details überladen werden müssen.

Der Vergleichslink lässt sich unmittelbar aus den beiden Tag-Namen und der bekannten Repository-URL zusammensetzen, etwa in der Form https://github.com/org/repo/compare/v1.4.0...v1.5.0. Bei GitLab-gehosteten Projekten unterscheidet sich nur das URL-Schema, das Prinzip bleibt identisch.

7. Textbausteine für einen konsistenten Ton

Automatisch generierte Release Notes wirken schnell roboterhaft, wenn jede Version exakt dieselbe Struktur ohne jede Variation zeigt. Ein einfacher Trick: Ein Array mit mehreren Formulierungsvarianten für die Einleitung, aus dem das Skript pro Lauf zufällig eine Variante auswählt, sodass aufeinanderfolgende Release Notes nicht wortwörtlich identisch beginnen.

Wichtiger als stilistische Variation ist jedoch ein konsistentes Grundgerüst: Einleitung, Highlights, vollständige Kategorienliste, Mitwirkende, Vergleichslink, immer in derselben Reihenfolge. Diese Konsistenz sorgt dafür, dass Leser sich über mehrere Versionen hinweg in den Release Notes sofort zurechtfinden, unabhängig von den inhaltlichen Details der jeweiligen Version.


#!/usr/bin/env bash
# build-release-notes.sh — assemble the final release notes document
set -euo pipefail

version="$1"
range="$2"

intros=(
  "This release focuses on"
  "In this update, we shipped"
  "Here is what changed in"
)
intro="${intros[$((RANDOM % ${#intros[@]}))]}"

{
  echo "# ${version}"
  echo
  echo "${intro} ${version}."
  echo
  bash extract-highlights.sh "$range"
  echo
  bash list-contributors.sh "$range"
  echo
  echo "**Full diff:** https://github.com/mironsoft/example-repo/compare/${range/../.../.../}"
} > release-notes.md

echo "[OK] release-notes.md generated for ${version}"

8. Release Notes an die Release API übergeben

Statt die fertigen Release Notes manuell in die GitHub- oder GitLab-Oberfläche zu kopieren, lässt sich der gesamte Vorgang über die jeweilige Release API automatisieren. Für GitHub genügt ein curl Aufruf gegen /repos/{owner}/{repo}/releases mit dem generierten Markdown-Text im body Feld des JSON-Payloads.

Wichtig für die Automatisierung: Sonderzeichen und Zeilenumbrüche im generierten Text müssen korrekt für JSON escaped werden, wofür sich jq -Rs . anbietet, das eine mehrzeilige Textdatei zuverlässig in einen gültigen JSON-String umwandelt, ohne manuelles Escaping von Anführungszeichen oder Newlines.


#!/usr/bin/env bash
# publish-release.sh — push release notes to the GitHub Releases API
set -euo pipefail

readonly REPO="mironsoft/example-repo"
readonly TOKEN="${GITHUB_TOKEN:?Set GITHUB_TOKEN}"
version="$1"

body_json="$(jq -Rs . < release-notes.md)"

payload=$(jq -n \
  --arg tag "$version" \
  --argjson body "$body_json" \
  '{ tag_name: $tag, name: $tag, body: $body, draft: false, prerelease: false }')

curl -sf -X POST \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Accept: application/vnd.github+json" \
  -d "$payload" \
  "https://api.github.com/repos/${REPO}/releases" > /dev/null

echo "[OK] Release ${version} published with generated release notes"

9. Release Notes Generator im Vergleich

GitHub bietet mit den automatisch generierten Release Notes ein eingebautes Feature, das Pull-Request-Titel gruppiert. Für Projekte, die konsequent über Pull Requests statt direkte Commits arbeiten, ist dieses eingebaute Werkzeug oft ausreichend. Ein eigenes Bash-Skript lohnt sich, sobald individuelle Kategorien, Mitwirkenden-Formatierung oder eine Cross-Plattform-Veröffentlichung auf mehreren Release-Kanälen gebraucht werden.

Ansatz Anpassbarkeit Mitwirkenden-Liste Geeignet für
Bash Release Notes Generator Vollständig Individuell formatiert Cross-Plattform, individuelle Kategorien
GitHub Auto-Generated Notes Begrenzt konfigurierbar Nach PR-Autor Reine GitHub-PR-Workflows
Manuelle Release Notes Vollständig Manuell, fehleranfällig Sehr seltene Releases
Release-Please (Google) Konfigurierbar über Presets Begrenzt Node-lastige Google-Style-Projekte

Für Teams mit eigenem Release-Kanal, etwa einer internen Status-Seite zusätzlich zu GitHub, ist ein selbst gebauter Ansatz für Release Notes oft die einzige Möglichkeit, denselben Text konsistent an mehrere Ziele gleichzeitig zu verteilen.

Mironsoft

Shell-Automatisierung, Release-Management und Deployment-Infrastruktur

Release Notes, die sich automatisch schreiben?

Wir bauen ein Skript, das aus eurer Git-Historie lesbare Release Notes mit Mitwirkenden, Highlights und Vergleichslink erzeugt und direkt an GitHub, GitLab oder eure eigene Status-Seite übergibt.

Skript-Entwicklung

Individueller Release Notes Generator passend zu eurem Ton

API-Integration

Automatische Veröffentlichung auf GitHub, GitLab oder eigenen Kanälen

Release-Prozess

Einbindung in bestehende CI/CD-Pipelines und Tag-Workflows

10. Zusammenfassung

Automatisch generierte Release Notes unterscheiden sich bewusst von einem technischen Changelog: Sie richten sich an Nutzer, heben Highlights statt jeder einzelnen Änderung hervor und würdigen Mitwirkende namentlich. Ein Bash-Skript, das den Commit-Bereich zwischen zwei Tags ausliest, feat-Commits filtert und einen Vergleichslink ergänzt, liefert die Grundlage für konsistente Release Notes bei jeder Veröffentlichung.

Der letzte Schritt, die direkte Veröffentlichung über die GitHub- oder GitLab-Release-API, macht aus dem Skript einen vollständigen Teil des Release-Prozesses. Statt Release Notes manuell zu formatieren und in eine Web-Oberfläche zu kopieren, entsteht ein einziger Befehl, der Text generiert und gleichzeitig veröffentlicht.

Release Notes aus Git Log — Das Wichtigste auf einen Blick

Abgrenzung zum Changelog

Release Notes richten sich an Nutzer, ein Changelog an Entwickler, beide entstehen aber aus derselben Historie.

Highlights statt Vollständigkeit

feat-Commits werden gefiltert und hervorgehoben, technische chore- und ci-Commits bleiben außen vor.

Mitwirkende

git shortlog liefert eine deduplizierte Liste der Autoren pro Release automatisch.

Veröffentlichung

Direkte Übergabe an die GitHub oder GitLab Release API mit korrekt escaptem JSON-Text.

11. FAQ: Release Notes aus Git Log generieren

1Unterschied zu einem Changelog?
Ein Changelog dokumentiert kontinuierlich für Entwickler, Release Notes fassen punktuell für Nutzer zusammen.
2Wie wird der Commit-Bereich bestimmt?
Über die beiden letzten Tags, sortiert nach Versionsnummer mit git for-each-ref.
3Wie werden Mitwirkende aufgelistet?
Über git log mit Autorennamen, sortiert und dedupliziert, ohne Merge-Commits.
4Wie werden Highlights ausgewählt?
Über eine Filterung auf feat-Commits, technische Commit-Typen bleiben außen vor.
5Warum andere Gruppierung als ein Changelog?
Technische Commit-Typen sind für Nutzer wenig aussagekräftig, Release Notes gruppieren nach spürbaren Effekten.
6Wozu der Vergleichslink?
Er erlaubt technischen Lesern den vollständigen Diff einzusehen, ohne die Notes selbst zu überladen.
7Wie wird veröffentlicht?
Über curl gegen die Release API, mit jq -Rs . korrekt als JSON-String escaped.
8Reicht GitHubs eingebaute Funktion nicht?
Für reine PR-Workflows oft ja, für individuelle Kategorien oder mehrere Kanäle lohnt sich ein eigenes Skript.
9Wie bleibt der Ton konsistent?
Über ein festes Grundgerüst mit leicht variierenden Einleitungssätzen.
10Funktioniert das mit Lightweight-Tags?
Ja, solange git for-each-ref statt git describe verwendet wird.