Ein Git-Änderungsprotokoll automatisch erstellen

Wie man aus git commit Nachrichten automatisch ein Änderungsprotokoll erstellen kann

Um einen Überblick über die Historie eines Projektes unter Git-Verwaltung zu bekommen, ist das Anlegen eines Änderungsprotokolls sinnvoll. Eine einfache Lösung listet alle commit Nachrichten sortiert nach Tags auf.

Über Sinn und Zweck eines Änderungsprotokolls lässt sich wenig streiten. Obwohl ein gutes Änderungsprotokoll mehr als nur ein einfaches chronologisches Auflisten von commit Nachrichten enthalten sollte, wäre eine solche Liste, wenn sie denn mit einfachen Mitteln zu erstellen wäre, bereits hilfreich.

Der Befehl git log

Die einfachste Möglichkeit besteht darin, den Befehl git log in einem Git-Repository auszuführen. Dieser listet alle commit Nachrichten mit einigen zusätzlichen Angaben in umgekehrter chronologischer Reihenfolge auf, d.h. von der Neuesten bis zur Ältesten.

Über zusätzliche Parameter lässt sich die Ausgabe anpassen. Zum Beispiel kann das Ausgabeformat angepasst werden oder mit der Angabe eines Pfades die Nachrichten selektiert werden, die sich auf den angegebenen Pfad beziehen (siehe diese Dokumentation mit weiteren Möglichkeiten und Beispielen).

Um ein aussagekräftigeres Änderungsprotokoll zu erstellen bedarf es jedoch mehr. Vielleicht möchte man das Änderungsprotokoll nach Versionen (Tags) gliedern, möglicherweise noch mit einer weiteren Untergliederung, welche neuen Features die Version enthält und welche Fehler beseitigt wurden.

Um dies zu erreichen bedarf es meist der Installation zusätzlicher Software und man ist gezwungen, sich an bestimmte Regeln beim Erstellen von commit Nachrichten zu halten (siehe z.B. diese Anleitung mit weiteren Links auf verschiedene Programme zum Erstellen eines Änderungsprotokolls).

Meine Anforderungen an dieser Stelle sind weniger anspruchsvoll:

  • Der Befehl zum Erstellen des Änderungsprotokolls muss einfach zu merken sein. Keine Parameter für irgendwelche Varianten.
  • Keine Nachbearbeitung des Protokolls erforderlich.
  • Keine zusätzliche Software notwendig, nur mit dem auskommen, was ohnehin schon installiert ist.
  • Das Änderungsprotokoll soll die Änderungen für Tags/Releases zusammenfassen.
  • Die commit Nachrichten müssen keiner bestimmten Form genügen.

Aus diesen Gründen ist nachfolgendes Bash-Skript entstanden. Es funktioniert in einer entsprechenden Shell und mit einer halbwegs aktuellen Git Version.

Skript zum Erstellen eines Änderungsprotokolls

Das nachfolgende Skript kann zum Beispiel unter dem Namen GitCreateChangelog.sh irgendwo im Pfad gespeichert und ohne Parameter aufgerufen werden. Die Ausgabe kann optional in eine Datei umgeleitet werden, z.B. in die Datei CHANGELOG.md, um in einem Git-Repository gespeichert zu werden.

#!/bin/bash

## Files/Paths which matches pattern are excluded from report
IGNORE="**/CHANGELOG*"

## Format for pretty printing the log entry (markdown list entry)
FORMAT="- %s [%an] %h"

## First check if we are running in a git repo or exit
git rev-parse > /dev/null 2>&1
[[ $? != 0 ]] && echo "This is not a git repo!" && exit 1

## Get the very first commit to report commits before first tag
FIRSTCOMMIT=$(git rev-list --max-parents=0 HEAD)

## Get the list of all TAGS prepended by first commit
declare -a TAGS=($FIRSTCOMMIT $(git tag) )

## Output header in markdown
echo -e "# Changelog\n"

## Recurse list of tags in reverse order
for (( i=$(( ${#TAGS[@]} - 1)); i>0; i-- ));do
#  echo "Commits from ${TAGS[$i-1]} to ${TAGS[$i]}"

## Get date of this tag
  DATE=$(git tag --list ${TAGS[$i]} --format '%(creatordate:short)')

## Output subheader in markdown
  echo -e "## ${TAGS[$i]} ($DATE)\n"

## Get list of commit messages between previous and this tag
  git --no-pager log --pretty="format:$FORMAT" ${TAGS[$i-1]}..${TAGS[$i]} -- . ":(exclude,glob,icase)$IGNORE"

  echo -e "\n"
done

exit 0

Wie man sieht, kommt man mit weniger als 15 Zeilen Code aus. Dabei werden nur Befehle verwendet, die aufgrund der installierten Programme Bash und Git ohnehin vorhanden sind.

Zur Erläuterung:

  • Mit der Variablen IGNORE wird ein Dateimuster festgelegt, das bei der Ausgabe der commit Nachrichten ignoriert wird. Der Sinn ist, damit z.B. die commit Nachrichten für das erstellte Änderungsprotokoll selbst nicht mit in das Änderungsprotokoll aufzunehmen. Der Dateiname muss in diesem Fall mit "CHANGELOG" beginnen.
  • Die Variable FORMAT speichert das Format, in dem mit git log die commit Nachrichten ausgegeben werden. Eine Erläuterung der Platzhalter findet sich in der Git Dokumentation. Da die Ausgabe im Markdown-Format als Liste erfolgen soll, wird jeder Nachricht ein - vorangestellt.
  • Anhand der Ausgabe des Befehls git rev-parse wird geprüft, ob man sich überhaupt in einem Git-Repository befindet. Wenn nicht, wird das Skript abgebrochen.
  • Um auch die commit Nachrichten zu erfassen, die vor dem ersten Tag/Release liegen, wird der SHA-Wert des ersten Commits ermittelt und in der Variablen FIRSTCOMMIT gespeichert.
  • Anschließend werden alle Tags/Releases ermittelt und zusammen mit dem SHA-Wert des ersten Commits in einem Bash Array (TAGS) gespeichert.
  • Bevor die Liste der commit Nachrichten abgerufen wird, wird ein Titel im Markdown-Format ausgegeben.
  • Die eigentlichen commit Nachrichten werden in einer Schleife abgerufen, wobei die Liste TAGS in umgekehrter Reihenfolge rückwärts durchlaufen wird.
  • In der Schleife wird zunächst das Datum des aktuellen Tags/Releases ermittelt und in der Variablen DATE gespeichert.
  • Anschließend wird im Markdown-Format ein Untertitel ausgegeben, der den Tag/Release und dessen Datum enthält.
  • Danach erfolgt die Ausgabe der commit Nachrichten im definierten Format und ohne Pager. Mit der Konstruktion ${TAGS[$i-1]}..${TAGS[$i]} werden nur die Commits erfasst, die zwischen dem vorhergehenden und dem aktuellen Tag/Release stattfanden. Die Ausgabe erfolgt für alle Dateien ab dem aktuellen Verzeichnis und schließt Dateien, die mit "CHANGELOG" beginnen (Groß-Kleinschreibung wird ignoriert), aus.

Zur Erstellung eines vollständigen Änderungsprotokolls befindet man sich am besten im Hauptverzeichnis des betreffenden Git-Repository und leitet die Ausgabe in die Datei CHANGELOG.md um:

$ GitCreateChangelog.sh > CHANGELOG.md

Das Ergebnis könnte dann wie in folgendem Beispiel aussehen:

CHANGELOG.md eines Git-Repository
Top