| FOTO | AUTO | EDV | AUDIO |

03 — Versionskontrolle und Branching

Teil B — Wie man arbeitet. Zurück zur Übersicht.

Lernziel

Nach diesem Kapitel kannst du ein Repository sinnvoll strukturieren, brauchbare Commits schreiben und die gängigen Branching-Strategien gegeneinander abwägen. Du weißt, warum langlebige Branches Continuous Integration unmöglich machen, und hast den KFZ-Rechner unter Versionskontrolle gebracht.

3.1 Warum das hier zuerst kommt

Alles Weitere in dieser Unterlage setzt eines voraus: Es gibt genau einen Ort, an dem der aktuelle Stand steht, und dieser Ort kennt seine eigene Geschichte.

Ohne Versionskontrolle gibt es keine Pipeline, keine Nachvollziehbarkeit, kein Rollback und keine Automatisierung. Der Ordner anwendung_final_v3_neu_KORRIGIERT ist kein Scherz aus einer Präsentation, sondern der Normalzustand in erschreckend vielen Fachabteilungen.

Git ist dabei nicht einfach ein Ablageort. Es ist die Quelle der Wahrheit — und später auch der Auslöser für alles, was automatisch passiert.

3.2 Das Minimum an Git

# Repository anlegen
git init
git config user.name  "Vorname Nachname"
git config user.email "vorname.nachname@firma.de"
 
# Stand ansehen
git status
git diff
git log --oneline --graph --decorate --all
 
# Änderungen aufnehmen
git add datei.py
git add -p              # abschnittsweise - sehr zu empfehlen
git commit -m "Nachricht"
 
# Mit dem Server abgleichen
git pull --rebase
git push
 
# Zweig anlegen und wechseln
git switch -c feature/schadenfreiheitsklasse
git switch main
 
# Zusammenführen
git merge feature/schadenfreiheitsklasse

git add -p geht die Änderungen abschnittsweise durch und fragt jeweils, ob sie in den Commit sollen. Das erzwingt, dass man den eigenen Code noch einmal liest, bevor man ihn festschreibt — und verhindert zuverlässig, dass Debug-Ausgaben oder auskommentierte Experimente mitwandern. Wer sich diese eine Angewohnheit aneignet, hebt die Qualität seiner Commits sofort.

3.3 Was gehört ins Repository — und was nicht

Gehört hinein Gehört nicht hinein
Quellcode Passwörter, Schlüssel, Zertifikate
Konfigurationsvorlagen echte Konfiguration mit Zugangsdaten
Containerfile, Pipeline-Definition Build-Ergebnisse, kompilierte Dateien
Datenbankmigrationen Abhängigkeiten (node_modules, venv)
Tests und Testdaten Produktivdaten
Dokumentation große Binärdateien
.gitignore
__pycache__/
*.pyc
.venv/
.env
*.log
.idea/
.vscode/
dist/

Ein einmal eingecheckter Zugangsschlüssel ist kompromittiert — auch nach dem Löschen. Git vergisst nichts; der alte Commit bleibt in der Historie und in jedem geklonten Repository auf jedem Notebook erhalten.

Die einzig richtige Reaktion lautet: den Schlüssel oder das Passwort sofort ändern. Das Bereinigen der Historie ist optional und aufwendig, das Wechseln des Geheimnisses ist Pflicht.

Vorbeugen lässt sich mit einem Pre-Commit-Hook und Werkzeugen wie gitleaks — mehr dazu in Kapitel 14.

3.4 Brauchbare Commits

Ein Commit sollte eine abgeschlossene, für sich verständliche Änderung enthalten. Die Nachricht erklärt das Warum; das Was steht bereits im Diff.

Schlecht                          Gut
──────────────────────────        ─────────────────────────────────────
"fix"                             "Division durch null bei SF-Klasse 0
"Änderungen"                       verhindern"
"asdf"
"funktioniert jetzt endlich"      "Prämienberechnung auf Dezimaltyp
"WIP"                              umgestellt

                                   Fließkomma führte bei Beträgen über
                                   10.000 EUR zu Rundungsdifferenzen
                                   von bis zu 2 Cent.
                                   Siehe Ticket #4711."

Verbreitet und praktisch ist die Konvention Conventional Commits:

feat:     neue Funktion
fix:      Fehlerbehebung
docs:     nur Dokumentation
refactor: Umbau ohne Verhaltensänderung
test:     Tests ergänzt oder geändert
chore:    Wartungsarbeiten, Abhängigkeiten

Beispiel:
feat(praemie): Rabatt fuer Wenigfahrer ergaenzen
fix(api): Zeitzone bei Vertragsbeginn korrigieren

Der Nutzen ist nicht kosmetisch: Aus solchen Nachrichten lassen sich Änderungsprotokolle automatisch erzeugen, und die Versionsnummer kann maschinell abgeleitet werden — feat erhöht die Nebenversion, fix die Patch-Nummer.

3.5 Semantische Versionierung

MAJOR . MINOR . PATCH
  │       │       └── Fehlerbehebung, abwärtskompatibel
  │       └────────── neue Funktion, abwärtskompatibel
  └────────────────── Änderung, die Bestehendes bricht

2.4.7 -> 2.4.8   Fehler behoben
2.4.8 -> 2.5.0   Funktion ergänzt
2.5.0 -> 3.0.0   Schnittstelle geändert
git tag -a v2.5.0 -m "Rabatt für Wenigfahrer"
git push --tags

Der entscheidende Punkt ist die dritte Stelle von links: Eine erhöhte Hauptversion ist ein Versprechen an alle, die das System benutzen — hier musst du etwas anpassen. Wer das ernst nimmt, erspart seinen Anwendern böse Überraschungen.

3.6 Branching-Strategien

Hier wird es entscheidungsrelevant, denn die Wahl bestimmt, ob Continuous Integration überhaupt möglich ist.

GitFlow

2010 von Vincent Driessen beschrieben. Sieht mehrere dauerhafte Zweige vor: main, develop, dazu feature/, release/ und hotfix/.

main     ──●──────────────●──────────────●──►  nur Releases
            \            /              /
release      \      ●───●              /
              \    /                  /
develop  ──●───●──●──────●───●───────●────►
            \       \       /
feature      ●───●───●     ●

Passt zu: Software mit mehreren parallel gepflegten Versionen, langen Freigabezyklen, ausgelieferten Produkten beim Kunden.

Passt nicht zu: Continuous Delivery. Der Autor selbst hat 2020 einen Hinweis nachgeschoben, dass GitFlow für Anwendungen mit laufender Auslieferung nicht die richtige Wahl sei — genau der Fall, um den es in dieser Unterlage geht.

GitHub Flow

Ein dauerhafter Zweig main, der jederzeit auslieferbar ist. Kurzlebige Feature-Branches, Pull Request, Zusammenführung, Auslieferung.

main  ──●───●───●───●───●───●───●──►  jederzeit auslieferbar
         \     /     \     /
feature   ●───●       ●───●
          (1-2 Tage)

Passt zu: Webanwendungen, internen Diensten, allem mit einer einzigen produktiven Version. Für den KFZ-Rechner ist das die richtige Wahl.

Trunk-based Development

Die konsequenteste Variante: Alle arbeiten am selben Zweig, Branches leben Stunden statt Tage. Unfertige Funktionen werden über Feature Flags verborgen statt in Zweigen versteckt.

main  ──●─●─●─●─●─●─●─●─●─●──►   mehrere Commits pro Tag
          \_/   \_/
        wenige Stunden

Passt zu: erfahrenen Teams mit guter Testabdeckung. Es ist die Strategie mit den nachweislich besten Werten in der DORA-Forschung — setzt aber Vertrauen in die eigene Testautomatisierung voraus.

Gegenüberstellung

GitFlow GitHub Flow Trunk-based
Lebensdauer eines Zweigs Wochen 1–2 Tage Stunden
Konfliktrisiko hoch gering sehr gering
Eignung für CI schlecht gut ideal
Nötige Testabdeckung mittel gut sehr gut
Mehrere Versionen parallel ja nein nein
Einstiegshürde hoch niedrig mittel

Der Kern der Sache: Continuous Integration bedeutet wörtlich fortlaufendes Zusammenführen. Ein Feature-Branch, der drei Wochen lebt, ist genau das Gegenteil — dort integriert niemand, dort divergiert etwas.

Je länger ein Zweig lebt, desto größer der Unterschied zum Hauptzweig, desto schmerzhafter die Zusammenführung. Teams, die wochenlange Branches führen, verbringen einen erheblichen Teil ihrer Zeit mit Konfliktauflösung und nennen das dann Entwicklung.

Faustregel: Kein Zweig lebt länger als zwei Tage. Was länger dauert, ist zu groß geschnitten.

3.7 Code Review

Der Pull Request — bei GitLab Merge Request — ist mehr als eine Formalie. Er erfüllt drei Zwecke gleichzeitig: Qualitätssicherung, Wissensverteilung und, in regulierten Umgebungen, den Nachweis des Vier-Augen-Prinzips. Dazu mehr in Kapitel 17.

Was ein Prüfer tun sollte:

  • innerhalb weniger Stunden reagieren — ein liegengebliebener Pull Request blockiert den Fluss
  • auf Verständlichkeit, Testabdeckung und Sicherheit achten
  • Fragen stellen statt Urteile fällen: „Was passiert hier bei einer leeren Liste?„
  • Kleinigkeiten als solche kennzeichnen: „Randbemerkung, nicht blockierend“

Was ein Prüfer nicht tun sollte:

  • Formatierung diskutieren — dafür gibt es Formatierungswerkzeuge, die das automatisch erledigen
  • die gesamte Architektur in Frage stellen, wenn das im Entwurf schon geklärt war
  • einen Pull Request mit 2.000 geänderten Zeilen ernsthaft prüfen wollen. Ab etwa 400 Zeilen sinkt die Fehlerentdeckungsrate messbar — geprüft wird dann nur noch pro forma

3.8 Lab: Der KFZ-Rechner kommt unter Versionskontrolle

# 1. Repository anlegen
mkdir kfz-rechner && cd kfz-rechner
git init -b main
 
# 2. Struktur erzeugen
mkdir -p src tests docs
cat > src/praemie.py <<'EOF'
"""Prämienberechnung für die KFZ-Versicherung."""
from decimal import Decimal
 
GRUNDBEITRAG = Decimal("480.00")
 
SF_RABATT = {
    0: Decimal("1.00"),
    1: Decimal("0.85"),
    5: Decimal("0.60"),
    10: Decimal("0.40"),
}
 
 
def berechne_praemie(sf_klasse: int, fahrzeugalter: int) -> Decimal:
    """Errechnet den Jahresbeitrag in Euro."""
    if sf_klasse < 0:
        raise ValueError("SF-Klasse darf nicht negativ sein")
 
    faktor = SF_RABATT.get(sf_klasse, Decimal("0.50"))
    beitrag = GRUNDBEITRAG * faktor
 
    if fahrzeugalter > 10:
        beitrag *= Decimal("0.90")
 
    return beitrag.quantize(Decimal("0.01"))
EOF
 
# 3. .gitignore anlegen
cat > .gitignore <<'EOF'
__pycache__/
*.pyc
.venv/
.env
EOF
 
# 4. Erster Commit
git add .
git commit -m "feat: Prämienberechnung als erste Fassung
 
Grundbeitrag mit SF-Rabatt und Altersnachlass.
Dezimaltyp statt Fließkomma, um Rundungsdifferenzen
bei der Beitragsberechnung auszuschließen."
 
# 5. Feature-Branch, Änderung, Zusammenführung
git switch -c feat/wenigfahrer
# ... Rabatt für Wenigfahrer ergänzen ...
git add -p
git commit -m "feat(praemie): Rabatt für Wenigfahrer ergänzen"
git switch main
git merge --no-ff feat/wenigfahrer
git branch -d feat/wenigfahrer
 
# 6. Version markieren
git tag -a v0.1.0 -m "Erste lauffähige Fassung"
git log --oneline --graph --all

Zusatzaufgabe: Lege das Repository auf einem Git-Server an (Gitea, GitLab oder GitHub) und richte einen Pull Request ein. Lass jemanden aus deinem Umfeld eine Anmerkung hinterlassen und arbeite sie ein.

3.9 Anti-Pattern

Anti-Pattern Folge Abhilfe
Langlebige Feature-Branches Zusammenführungskonflikte, keine echte Integration Zweige unter zwei Tagen, Feature Flags
Sammelcommits „Änderungen an 47 Dateien„ — nicht prüfbar, nicht zurückrollbar Kleine, thematische Commits
Direkt auf main pushen Prüfung entfällt, Historie wird unsauber Branch-Schutz aktivieren
Geheimnisse im Repository Kompromittierung, oft jahrelang unbemerkt Secret-Scanning, .gitignore, Tresor
Build-Ergebnisse eingecheckt Repository wird riesig, ständige Konflikte .gitignore, Artefakt-Registry
–force auf gemeinsame Zweige überschreibt fremde Arbeit –force-with-lease, besser gar nicht
Riesige Pull Requests Prüfung wird zur Formalie Änderungen aufteilen

3.10 Reflexionsfrage

Wie lange lebt in deinem Umfeld der durchschnittliche Feature-Branch — und was wäre nötig, damit er nur noch zwei Tage lebt?

Die Antwort führt fast immer zu denselben zwei Punkten: kleinere Arbeitspakete und bessere Tests. Womit wir beim nächsten Kapitel wären.


Weiter mit Kapitel 04 — Testen und Qualität