Skip to content

Latest commit

 

History

History
1462 lines (1103 loc) · 67.3 KB

gitHubWorkFlow.md

File metadata and controls

1462 lines (1103 loc) · 67.3 KB

Unser Git-Workflow: SupplierRatingSoftware

Dieses Dokument beschreibt den Git-Workflow für unsere Repositories supplierRatingBackend und supplierRatingFrontend. Die Einhaltung dieses Workflows ist für alle Teammitglieder verpflichtend, um Code-Qualität, Nachvollziehbarkeit und einen stabilen main-Branch sicherzustellen.

Unser Modell basiert auf dem GitHub Flow, einem schlanken und modernen Ansatz. Das Kernprinzip ist ein stabiler main-Branch, von dem alle Arbeiten in kurzlebigen "Topic Branches" abgezweigt werden. Dieser Workflow ist bewusst einfach gehalten und eine klare Alternative zu komplexeren Modellen wie dem Gitflow.

Ziele dieses Dokuments:

  1. Stabilität: Der main-Branch ist immer stabil, kompilierbar und potenziell auslieferbar.
  2. Qualitätssicherung: Kein Code wird ohne ein "Vier-Augen-Prinzip" (Code Review) in den main-Branch integriert.
  3. Parallelisierung: Alle Teammitglieder können parallel an verschiedenen Funktionen arbeiten, ohne sich gegenseitig zu blockieren.
  4. Nachvollziehbarkeit: Die Git-Historie ist sauber und lesbar.

Der Workflow-Kodex:

  1. Pushe niemals direkt auf den main-Branch.
  2. Zweige immer vom aktuellsten main-Branch ab (git pull origin main, bevor du einen neuen Branch erstellst).
  3. Jeder Merge in main erfordert einen Pull Request (PR).
  4. Jeder PR erfordert mindestens ein "Approval" von einem anderen Teammitglied (der Autor darf nicht selbst approven).
  5. Mache kleine, logische Commits
  6. Nach dem Merge wird der Arbeits-Branch gelöscht

1. Der tägliche Workflow (Die 90%-Nutzung)

Dies ist der Standard-Ablauf für jede neue Aufgabe (Feature, Bugfix, etc.). Für detailliertere Beschreibung der einzelnen Schritte, siehe weiter unten.

  1. Synchronisieren: Hole den neuesten Stand von GitHub.

    git checkout main
    git pull origin main
  2. Abzweigen: Erstelle deinen eigenen Arbeits-Branch.

    git checkout -b feature/supplier-list-component
  3. Arbeiten: Programmiere und erstelle lokale, saubere Commits.

    git add <meine-datei.java>
    git commit -m "feat: Add xyz"
  4. Pushen: Schiebe deinen fertigen Branch auf GitHub.

    git push -u origin feature/supplier-list-component
  5. Pull Request (PR) erstellen: Gehe auf GitHub und erstelle einen PR für deinen Branch, um sie in main zu mergen. Weise deine Teamkollegen als Reviewer zu.

  6. Review & Mergen: Warte auf das "Approval" (den grünen Haken) von mindestens einem Kollegen. Klicke dann auf "Squash and Merge" auf GitHub und lösche anschliessend deinen Branch.

  7. Aufräumen: Räume deine lokale Code-Basis auf und bringe sie auf den neusten Stand.

    git checkout main
    git pull origin main
    git branch -D feature/supplier-list-component

2. Der Workflow im Detail (Schritt für Schritt)

Dies ist der tägliche Prozess für jedes Teammitglied, das eine neue Aufgabe beginnt:

Schritt 1: Lokales `main` synchronisieren

git checkout main
git pull origin main

Schritt 2: Neuen Arbeits-Branch erstellen

Wähle den passenden Präfix (siehe unten).

# Beispiel für eine neue Funktion
git checkout -b feature/supplier-list-component

Schritt 3: Arbeiten & Committen

Jetzt programmierst du. Erstelle kleine, logische Commits gemäss unserer Commit-Message-Richtlinie (siehe unten).

Bevorzuge das gezielte Hinzufügen von Dateien (git add <file>) statt dem pauschalen git add ., um saubere, atomare Commits zu erstellen.

# Beispielhafter Arbeitszyklus (z.B. im Frontend-Repo)

# Füge nur die Datei für die neue Komponente hinzu
git add src/app/components/supplier-list/supplier-list.component.ts
git commit -m "feat: Add basic component structure for supplier list"

# ... weiter programmieren (z.B. am Styling) ...

# Füge nur die geänderte CSS-Datei hinzu
git add src/app/components/supplier-list/supplier-list.component.css
git commit -m "style: Adjust CSS grid for supplier list"

# Profi-Tipp: Mit `git add -p` kannst du sogar einzelne Code-Blöcke
# innerhalb einer einzigen Datei separat hinzufügen.

Noch genauer: git add -p (Patch-Modus) für atomare Commits

Manchmal hast du zwei logisch getrennte Dinge in derselben Datei geändert (z.B. ein Feature hinzugefügt UND einen Tippfehler korrigiert). Mit git add -p kannst du die Datei "Stück für Stück" (Hunk für Hunk) zum Commit hinzufügen.

Szenario: Du hast in supplier-list.component.ts eine neue Funktion (getSuppliers) hinzugefügt (feat) und in derselben Datei die Einrückung im Konstruktor korrigiert (style). Du willst dies in zwei separaten Commits festhalten.

  1. Du tippst: git add -p src/app/components/supplier-list/supplier-list.component.ts

  2. Git zeigt dir den ersten Block (den "Hunk") – die Einrückung:

    diff --git a/src/app/components/supplier-list/supplier-list.component.ts b/src/app/components/supplier-list/supplier-list.component.ts
    --- a/src/app/components/supplier-list/supplier-list.component.ts
    +++ b/src/app/components/supplier-list/supplier-list.component.ts
    @@ -10,7 +10,7 @@
     ) {
    -  this.pageTitle = 'Suppliers'; // SCHLECHTE EINRÜCKUNG
    +    this.pageTitle = 'Suppliers'; // KORRIGIERTE EINRÜCKUNG
     }

    Stage this hunk [y,n,q,a,d,s,e,?]?

  3. Du willst dies nicht im feat-Commit haben. Du tippst: n (für "No").

  4. Git zeigt dir den zweiten Block – die neue Funktion:

    @@ -20,6 +20,11 @@
       // ... alter Code ...
     }
    
    +  getSuppliers(): void {
    +    // ... neue coole Logik ...
    +  }
    +}

    Stage this hunk [y,n,q,a,d,s,e,?]?

  5. Das ist dein Feature. Du tippst: y (für "Yes").

  6. Jetzt machst du deinen ersten Commit (nur mit der neuen Funktion): git commit -m "feat: Add getSuppliers method"

  7. Anschliessend fügst du die übrig gebliebene Änderung (die Einrückung) hinzu und committest sie separat: git add src/app/components/supplier-list/supplier-list.component.ts git commit -m "style: Fix indentation in constructor"

Du hast nun eine perfekt saubere Git-Historie, obwohl beide Änderungen in derselben Datei waren.

Schritt 4: Branch auf GitHub pushen

git push -u origin feature/supplier-list-component

Schritt 5: Pull Request (PR) erstellen

  • Gehe auf GitHub. Du wirst einen gelben Banner sehen, der dir anbietet, einen PR zu erstellen.

  • Titel: Gib einen klaren Titel (z.B. Feature: Supplier List Component).

  • Beschreibung: Fülle die Beschreibung aus. Was macht dieser Code? Wie kann man ihn testen?

  • Reviewers: Weise die anderen beiden Teammitglieder als Reviewer zu.

Schritt 6: Review, Diskutieren & Mergen

  1. Review (Die Anderen): Die Reviewer schauen sich den Code an ("Files Changed").
  2. Diskutieren & Anpassen (Du): Du reagierst auf Kommentare und pusht ggf. neue Commits auf deinen Branch. Der PR aktualisiert sich automatisch.
  3. Approval (Die Anderen): Wenn alles passt, gibt mindestens ein Reviewer ein "Approval".
  4. Mergen (Du): Sobald du das "Approval" hast, klickst du auf den "Squash and Merge"-Button.
    • Warum "Squash and Merge"? Dies fasst alle deine (vielleicht 10 kleinen) Commits aus dem Arbeits-Branch zu einem einzigen, sauberen Commit im main-Branch zusammen. Das hält die main-Historie extrem sauber.
    • GitHub wird fragen, ob der Branch gelöscht werden soll. Ja, das soll er.

Schritt 7: Aufräumen nach dem Merge (Der "Post-Merge-Zyklus")

Sobald dein Pull Request in main gemerged und der Feature-Branch auf GitHub gelöscht wurde (Schritt 6), ist deine lokale Maschine noch nicht auf dem neuesten Stand.

Führe immer diese drei Schritte aus, um deinen Arbeitsplatz sauber zu halten und für die nächste Aufgabe vorzubereiten:

  1. Zum main-Branch wechseln

    Wechsle zurück auf den Haupt-Branch.

    git checkout main
  2. main synchronisieren (und aufräumen)

    Hole dir die neuesten Änderungen von main (inklusive deines Commits, der gerade gemerged wurde).

    git pull origin main

    Falls du global fetch.prune konfiguriert hast: Wenn du git config --global fetch.prune true gesetzt hast, wird bei diesem pull automatisch ein fetch --prune ausgeführt. Git bemerkt, dass origin/feature/supplier-list-component auf GitHub gelöscht wurde, und löscht diesen "Tracking-Pointer" lokal.

    Falls du fetch.prune nicht global konfiguriert hast: Wenn du fetch.prune true nicht gesetzt hast, müsstest du hier manuell den lokalen "Tracking-Pointer" löschen:

    git fetch --prune
  3. Alte lokale Branch löschen

    Dein lokales Repo ist jetzt sauber, aber der lokale Branch feature/supplier-list-component existiert noch. Da er nun sicher in main gemerged ist, wird er nicht mehr gebraucht.

    Da wir in Schritt 6 "Squash and Merge" verwenden, sind die ursprünglichen Commits unseres Arbeits-Branches technisch gesehen nicht mehr Teil von main. Git würde sich daher bei einem normalen -d (sicheres Löschen) beschweren.

    Wir müssen Git daher anweisen, das Löschen zu erzwingen (-D), da wir wissen, dass die Arbeit auf GitHub korrekt integriert wurde:

    git branch -D feature/supplier-list-component

    Jetzt ist dein lokales Repo sauber, main ist aktuell, und du bist bereit für Schritt 1 des nächsten Features.

3. Sonderfälle & Fehlerbehebung

Umgang mit Konflikten

Wenn dein PR nicht automatisch gemerged werden kann (weil main sich in der Zwischenzeit geändert hat), musst du den Konflikt in deinem Arbeits-Branch lösen.

  1. Hole dir dazu zunächst die neuesten Änderungen von main:

    git checkout feature/supplier-list-component
    git pull origin main
  2. Git wird nun versuchen, die main-Änderungen in deinen Branch zu integrieren. Wenn es Konflikte gibt, meldet Git dies (z.B. CONFLICT (content): Merge conflict in ...). Die Datei ist jetzt in einem speziellen "konfliktbehafteten" Zustand.

  3. Öffne die konfliktreichen Dateien in IntelliJ/WebStorm/Neovim.

    3a. Was du siehst: Die Konflikt-Marker

    Git hat die Datei an den problematischen Stellen bearbeitet und spezielle Marker eingefügt, um dir beide Versionen des Codes zu zeigen:

    <<<<<<< HEAD (Deine Änderungen)
    // Dein Code von deinem Branch 'feature/supplier-list-component'
    // z.B. eine Zeile, die du hinzugefügt hast
    System.out.println("Hallo Welt!");
    =======
    // Der "neue" Code von der 'main'-Branch
    // z.B. eine andere Zeile, die ein Kollege an derselben Stelle hinzugefügt hat
    System.out.println("Guten Tag!");
    >>>>>>> main
    • <<<<<<< HEAD: Dies markiert den Anfang deiner Änderungen (die Version auf deinem aktuellen Branch feature/supplier-list-component).
    • =======: Dies ist der Trennblock zwischen den beiden Versionen.
    • >>>>>>> main: Dies markiert das Ende der eingehenden Änderungen (die Version vom main-Branch, den du gerade gepullt hast).

    3b. Deine Aufgabe: Die Auflösung

    Deine Aufgabe ist es, diese Blöcke (inklusive der Marker-Zeilen <<<, ===, >>>!) komplett zu entfernen und sie durch den endgültigen, korrekten Code zu ersetzen.

    Du musst eine fachliche Entscheidung treffen:

    • Behältst du deinen Code? Dann lösche die main-Version und alle Marker.
    • Behältst du den main-Code? Dann lösche deine Version und alle Marker.
    • Kombinierst du beides? (z.B. wenn beide eine Zeile zu einer Liste hinzugefügt haben). Dann nimmst du beide Code-Teile, ordnest sie korrekt an und löschst alle Marker.

    Das Wichtigste ist: Am Ende muss die Datei wieder gültigen Code enthalten, so als ob der Konflikt nie passiert wäre.

    Nutze den Merge-Editor deiner IDE

    Du musst das fast nie von Hand (im reinen Texteditor) machen. IntelliJ / WebStorm / und z.T. Neovim erkennen den Konflikt und markieren die Datei rot.

    • Klicke mit der rechten Maustaste auf die Datei und wähle Git -> Resolve Conflicts....
    • Die IDE öffnet einen speziellen 3-Wege-Merge-Editor.
    • Dieser zeigt dir drei Spalten:
      1. Links: Deine Version (HEAD)
      2. Rechts: Die main-Version (Eingehend)
      3. Mitte: Das Endergebnis (Das ist, was du bearbeitest)
    • Du kannst jetzt mit kleinen Pfeil-Buttons (>> oder <<) auswählen, welche Zeilen du aus welcher Version in die mittlere Spalte (das Endergebnis) übernehmen möchtest. Du kannst in der Mitte auch direkt tippen, um beide Versionen zu kombinieren.
    • Wenn du fertig bist, klickst du auf "Apply" oder "Resolve". Die IDE speichert dann die Datei in der finalen Version (die mittlere Spalte) ohne die Konflikt-Marker.
  4. Wenn alle Konflikte in allen Dateien gelöst sind, markierst du die Dateien als gelöst (das macht die IDE oft automatisch, wenn du den Merge-Editor schliesst).

    Du führst dann git add aus, um die "reparierten" Dateien zu stagen, gefolgt von git commit, um den Merge-Vorgang abzuschliessen. Die Commit-Message wird oft von Git vor-ausgefüllt.

    # Füge die nun korrigierten Dateien hinzu
    git add .
    
    # Schliesse den Merge-Commit ab
    # (oft öffnet sich ein Editor mit einer vor-ausgefüllten Message wie "Merge branch 'main' into ...")
    # Alternativ:
    git commit -m "fix: Merge conflicts from main"
  5. Pushe deinen Branch erneut. Dein lokaler Branch enthält jetzt deine Änderungen UND die neuen Änderungen von main UND deinen "Reparatur-Commit". Der PR auf GitHub sollte jetzt "clean" sein und gemerged werden können.

    git push

Einen Arbeits-Branch komplett verwerfen ("Abbruch")

Manchmal stellt man während der Arbeit an einem Branch (z.B. feature/falsche-idee) fest, dass der gesamte Ansatz falsch war oder die Funktion doch nicht benötigt wird. Du willst alle Commits auf diesem Branch verwerfen und sauber zu main zurückkehren.

Wichtig: Dieser Prozess löscht die Arbeit auf diesem Branch unwiderruflich.

Schritt 1: Lokale Änderungen sichern (optional)

Falls du im Arbeitsverzeichnis noch ungespeicherte, nicht-committete Änderungen hast, die du vielleicht für einen anderen Branch brauchst, sichere sie mit git stash.

(Siehe den nächsten Abschnitt Sonderfall:git stash`` für Details, wie man alle oder auch nur einzelne Dateien sichert).

Schritt 2: Sicher zurück zu main wechseln

git checkout main

Schritt 3: Den Branch auf GitHub (Remote) löschen

Falls du den Branch schon einmal auf GitHub gepusht hast (Schritt 4 des Standard-Workflows), musst du ihn zuerst auf dem Remote-Server (origin) löschen.

# Syntax: git push <remote> --delete <branch-name>
git push origin --delete feature/falsche-idee

(Wenn der Branch nie gepusht wurde, schlägt dieser Befehl einfach fehl, das ist unbedenklich.)

Schritt 4: Den Branch lokal löschen

Da dieser Branch per Definition nicht in main gemerged wurde, musst du das Löschen mit -D (grosses D) erzwingen.

git branch -D feature/falsche-idee

Du bist jetzt wieder sauber auf main und der "falsche" Branch ist sowohl lokal als auch remote (falls er dort war) gelöscht.

`git stash` (Das "Handschuhfach")

Was ist der "Stash"?

Stell dir den git stash als ein "Handschuhfach" oder einen Stapel (Stack) für unfertige Arbeit vor. Es ist kein Branch, sondern ein separater, lokaler Speicherort (nur in deinem Repo, wird nie gepusht), um nicht-committete Änderungen schnell beiseitezulegen.

Der Hauptzweck ist, dein Arbeitsverzeichnis (Working Directory) schnell sauber zu bekommen (z.B. weil du dringend auf main wechseln musst, um einen Bugfix zu starten), ohne die aktuelle Arbeit committen zu müssen.

Änderungen im Stash speichern

Befehl Erklärung
git stash Alles sichern: Nimmt alle geänderten und gestageten Dateien (die Git bereits kennt) und legt sie in den Stash. Dein Arbeitsverzeichnis ist danach sauber (wie nach einem git commit).
git stash push -m "Nachricht" Sichern mit Nachricht: Wie git stash, aber fügt eine beschreibende Nachricht hinzu (sehr nützlich, wenn man mehrere Dinge stasht).
git stash -u Inklusive neuer Dateien: Sichert auch alle untracked (neuen) Dateien.
git stash -p Interaktiv sichern: (Patch-Modus) Geht interaktiv durch alle "Hunks" (Code-Blöcke) und fragt dich, ob du diesen Teil stashen möchtest ([y,n,q,...]). Identisch zu git add -p.
git stash push <dateiname> Nur bestimmte Dateien sichern: Sichert nur die Änderungen an den angegebenen Dateien.

Gespeicherte Änderungen ansehen

Befehl Erklärung
git stash list Stash-Liste anzeigen: Zeigt alle "Pakete" an, die im Stash liegen, mit ihrer ID (z.B. stash@{0}) und der Nachricht.
git stash show stash@{1} Inhalt ansehen: Zeigt an, welche Dateien in einem bestimmten Stash-Eintrag geändert wurden (z.B. stash@{1}).
git stash show -p stash@{0} Änderungen im Detail ansehen: Zeigt den vollen diff (den Code) des Stash-Eintrags an.

Änderungen aus dem Stash holen (und verschieben)

Das Holen von Änderungen ist nicht an einen Branch gebunden. Du kannst auf feature/A etwas stashen, zu feature/B wechseln und es dort wieder herausholen.

Befehl Erklärung
git stash pop Holen & Löschen (Standard): Nimmt den obersten Eintrag (stash@{0}), wendet ihn auf dein aktuelles Arbeitsverzeichnis an und löscht ihn vom Stash-Stapel.
git stash apply Holen & Behalten: Wendet den obersten Eintrag an, behält ihn aber auf dem Stash-Stapel (nützlich, wenn du dieselbe Änderung auf mehreren Branches anwenden willst).
git stash pop stash@{2} Spezifisches Holen & Löschen: Wendet einen bestimmten Eintrag (hier: stash@{2}) an und löscht ihn.

Stash-Einträge aufräumen

Befehl Erklärung
git stash drop stash@{1} Spezifisches Löschen: Löscht den Eintrag stash@{1} unwiderruflich, ohne ihn anzuwenden.
git stash clear Alles Löschen: Löscht alle Einträge im Stash unwiderruflich.

Persistenz: Stash-Einträge bleiben so lange in deinem lokalen Repo, bis du sie mit pop, drop oder clear explizit löschst. Sie überleben Reboots, Branch-Wechsel und Commits.

4. Referenz: Befehle & Begriffe

Was ist Git

Git ist ein verteiltes Versionskontrollsystem (VCS).

Versionierung: Git speichert "Schnappschüsse" (Snapshots) des gesamten Projekts bei jeder Änderung (einem "Commit"). Man kann jederzeit zu jedem beliebigen Schnappschuss in der Vergangenheit zurückkehren.

  • Verteilt: Jeder Entwickler hat eine vollständige, lokale Kopie des gesamten Projektverlaufs (aller Versionen) auf seinem eigenen Rechner. Dies ermöglicht Commits zu erstellen, Branches wechseln und die Historie einsehen, ohne online zu sein.

Was ist GitHub

GitHub ist nicht Git. GitHub ist eine Cloud-Hosting-Plattform für Git-Repositories (Projekte).

Git ist das Werkzeug, GitHub ist der zentrale Ort, an dem wir unseren Code (das origin-Repo) ablegen. GitHub stellt die Infrastruktur für die Zusammenarbeit um Git herum bereit, insbesondere für:

  • Zentraler Speicher: Der origin, mit dem alle ihre lokalen Kopien synchronisieren.

  • Code Reviews: Die Pull Requests (PRs), die das Kernstück unseres Workflows sind.

  • Team-Management: Unsere "SupplierRatingSoftware" Organization.

Die wichtigsten Git-Befehle

Täglicher Workflow

Dies ist ein "Cheat Sheet" der Befehle, die du täglich brauchen wirst.

Befehl Erklärung
Setup
git clone <url> Klont ein Remote-Repository (z.B. von GitHub) auf deinen lokalen Rechner. Das machst du einmal pro Repo.
Lokaler Workflow
git status Dein wichtigster Befehl. Zeigt dir an, welche Dateien geändert, welche "gestaged" sind und auf welcher Branch du bist.
git add <file> Fügt eine Datei zur Staging Area hinzu.
git add -p Fügt nur Teile (Hunks) einer Datei zur Staging Area hinzu (sehr mächtig für saubere Commits).
git commit -m "..." Erstellt einen Commit mit allen Änderungen aus der Staging Area.
git log Zeigt die Commit-Historie an. (Drücke q zum Beenden).
Branching
git branch Listet alle deine lokalen Branches auf und zeigt, wo du bist (*).
git checkout <branch> Wechselt zum angegebenen Branch (ändert die Dateien in deinem Arbeitsordner auf den Stand dieses Branch).
git checkout -b <name> Erstellt einen neue Branch vom aktuellen HEAD und wechselt sofort zu ihm. (Unser Standard zum Starten von Arbeit).
Zusammenarbeit (Sync)
git fetch origin Holen: Holt alle neuen Daten (neue Commits, neue Branches) von GitHub (origin), ohne deine lokale Arbeit anzufassen. Es aktualisiert nur Gits "Wissen" darüber, was remote passiert ist.
git pull origin <branch> Ziehen: Macht ein git fetch UND versucht sofort, die Änderungen in deinem aktuell ausgecheckte Branch zu mergen. (Wird in unserem Workflow primär für das Aktualisieren von main genutzt).
git push origin <branch> Schieben: Sendet deine lokalen Commits auf den angegebenen Branch auf GitHub (origin). Dies ist der Moment, in dem deine Arbeit für andere sichtbar wird.

Weiterführende Git-Befehle

Dies ist ein "Cheat Sheet" weiterführender Befehle für Sonderfälle.

Befehl Erklärung
Lokale Änderungen verwalten
git restore --staged <file> Unstage: Entfernt eine Datei wieder aus der Staging Area (das Gegenteil von git add).
git restore <file> Änderungen verwerfen: Setzt eine Datei auf den Stand des letzten Commits zurück. (Vorsicht, alle nicht-committeten Änderungen an dieser Datei sind weg!)
git stash Änderungen parken: Legt alle deine aktuellen (nicht-committeten) Änderungen "ins Handschuhfach", um sauber den Branch wechseln zu können.
git stash pop Geparktes zurückholen: Holt die zuletzt geparkten Änderungen aus dem Handschuhfach zurück.
git diff Unterschiede anzeigen: Zeigt alle Änderungen im Arbeitsverzeichnis an, die noch nicht gestaged sind.
git diff --staged Unterschiede (Staging) anzeigen: Zeigt alle Änderungen an, die bereits in der Staging Area sind.
Lokale Branches verwalten
git branch -d <branch> Lokalen Branch löschen: Löscht den Branch auf deinem Rechner (sicher, funktioniert nur, wenn sie in main gemerged wurde).
git branch -D <branch> Löschen erzwingen: Löscht einen lokalen Branch, auch wenn er nicht gemerged wurde (mit Vorsicht!).
git fetch --prune Remote-Refs aufräumen: Aktualisiert deine lokale "Sicht" auf origin und löscht alle "Tracking-Branches", die auf GitHub schon gelöscht wurden.
Historie & Rollback
git checkout <hash> Alten Commit ansehen: "Reist" in der Zeit zurück zu einem alten Commit (z.B. git checkout a1b2c3d). Du bist dann im "detached HEAD"-Zustand (nur zum Schauen).
git checkout <branch> Zurück zur Gegenwart: Verlässt den "detached HEAD"-Zustand und kehrt zur Spitze eines Branch zurück (z.B. git checkout main).
git revert <hash> Rollback (Sicher): Deine Rollback-Strategie. Erstellt einen neuen Commit, der die Änderungen eines alten Commits (<hash>) exakt rückgängig macht.
git reset --hard <hash> Rollback (Gefährlich): Setzt den HEAD-Zeiger deiner Branch auf einen alten Commit (<hash>) und löscht alle Commits danach. (NIEMALS auf main verwenden!).

Wichtige Git Konfigurationsbefehle

Diese Befehle werden meist einmal global (--global) gesetzt oder dienen zur Überprüfung der Konfiguration.

Befehl Erklärung
git config --global user.name "..." Autorennamen: Setzt deinen Autorennamen global (erscheint in allen Commits).
git config --global user.email "..." Autoren-E-Mail: Setzt deine Autoren-E-Mail global (muss mit GitHub-Account übereinstimmen).
git config --global init.defaultBranch main Standard-Branch: Stellt sicher, dass neue Repos lokal main als Standard-Branch nutzen.
git config --global fetch.prune true Veraltete Remote-Zeiger: Sorgt dafür, dass git fetch und git pull veraltete Remote-Zeiger löschen.
git config --global core.editor "..." Standard-Texteditor: (Optional) Legt deinen Standard-Texteditor fest (z.B. nvim).
git config --list Konfiguration anzeigen: Listet alle aktuellen Git-Einstellungen auf.

GitHub CLI (`gh`): die wichtigsten Befehle

Mit gh kannst du fast alles, was du auf der GitHub-Webseite machst, direkt im Terminal erledigen. Der wichtigste Befehl ist gh auth login (siehe Setup). Hier sind die nützlichsten Befehle für unseren Workflow:

Befehl Erklärung
gh repo clone <org>/<repo> Klont ein Repo der Organization (z.B: gh repo clone SupplierRatingSoftware/supplierRatingBackend).
gh pr create Der Wichtigste: Erstellt einen Pull Request von deinem aktuellen Branch. Startet einen interaktiven Prozess für Titel, Body, Reviewer etc.
gh pr create --web Erstellt den PR und öffnet ihn direkt im Browser zur finalen Kontrolle (sehr empfohlen).
gh pr create -t "Titel" -b "Body" -r <user> Erstellt einen PR non-interaktiv mit Titel, Body und Reviewer.
gh pr list Zeigt alle offenen PRs im Repository an.
gh pr checkout <id> Extrem nützlich für Reviews: Lädt den Code eines PRs (anhand seiner ID-Nummer) in einen lokalen Branch, damit du ihn testen kannst.
gh pr status Zeigt dir den Status deiner eigenen PRs und der PRs, die auf dein Review warten.
gh pr merge <id> Merged einen PR (nach Approval) direkt von der CLI aus. (Das "Squash & Merge" im Browser ist aber oft sicherer für unseren Workflow).

Die wichtigsten Git Begriffe: Glossar

Um den Workflow zu verstehen, müssen wir die folgenden Begriffe kennen.

  • Repository (Repo): Das gesamte Projektverzeichnis, inklusive aller Dateien und der kompletten Git-Historie.

    • Remote-Repository: Unsere "Master-Kopie", die auf den Servern von GitHub liegt. Dies ist der origin.
    • Lokales-Repository: Deine persönliche Kopie des gesamten Projekts auf deinem Laptop, die nach einem git clone in einem Ordner liegt.
  • Clone (Klonen): Der einmalige Vorgang (git clone), bei dem du eine vollständige Kopie eines Remote-Repositorys (von GitHub) herunterlädst, um dein Lokales-Repository zu erstellen. Dieser Befehl richtet auch automatisch die Verbindung zu GitHub als origin ein.

  • Branch: Eine parallele Entwicklungslinie. Ein Branch ist im Grunde nur ein simpler, verschiebbarer Zeiger auf einen bestimmten Commit. Das Erstellen ist extrem schnell und erlaubt es uns, an Features, Fixes und Chores zu arbeiten, ohne den stabilen main-Branch zu gefährden.

  • Checkout: Der Vorgang (git checkout <branch>), bei dem du zwischen Branches wechselst. Wenn du eine Branch "auscheckst", ändert Git die Dateien in deinem Arbeitsverzeichnis, um dem Stand (dem Schnappschuss) dieses Branches zu entsprechen.

  • HEAD: Ein spezieller Zeiger, der Git sagt, "wo du gerade bist". HEAD zeigt fast immer auf den Namen des Branches, den du gerade ausgecheckt hast (z.B. feature/user-login). Wenn du einen neuen Commit machst, bewegt Git den Zeiger dieses Branches (und damit auch HEAD) automatisch auf den neuen Commit.

  • Stage / Staging Area (Index): Das "Wartezimmer" für deine Commits. Wenn du eine Datei änderst, ist sie nicht automatisch Teil des nächsten Commits. Du musst sie mit git add explizit in die Staging Area verschieben. Nur was in der Staging Area ist, wird beim nächsten git commit gespeichert.

  • Stash (Das "Handschuhfach"): Ein separater, lokaler Speicherort (kein Branch), um nicht-committete Änderungen schnell beiseitezulegen (z.B. um schnell den Branch zu wechseln). Die Änderungen können später wiedergeholt (git stash pop) werden.

  • Commit: Ein Schnappschuss deines Projekts. Sobald du git commit ausführst, werden alle Änderungen in der Staging Area genommen und als neue, permanente Version in deiner lokalen Git-Datenbank gespeichert.

  • Fetch (Holen): Der Vorgang (git fetch), bei dem dein lokales Git-Repo bei GitHub (origin) nachfragt: "Gibt es etwas Neues?". Es lädt alle neuen Daten herunter (neue Commits, neue Branches), aber ändert absolut nichts an deinem eigenen Code (deiner "Arbeitskopie"). Dein main bleibt, wo er ist. Es ist ein reines "Aktualisieren der Information".

  • Pull (Ziehen): Eine Kombination aus zwei Befehlen: git fetch + git merge. Ein git pull holt nicht nur die neuen Daten (wie fetch), sondern versucht auch sofort, diese neuen Änderungen in deine aktuell ausgecheckte Branch zu integrieren (zu mergen).

  • Push (Schieben): Der Vorgang (git push), bei dem du deine lokalen Commits (die GitHub noch nicht kennt) zum Remote-Repository (origin) hochlädst. Dies ist der Moment, in dem deine Arbeit für den Rest des Teams sichtbar wird.

  • Merge (Zusammenführen): Der Vorgang, bei dem die Historien von zwei verschiedenen Branches kombiniert werden. Wenn du z.B. feature/user-login fertig hast, "mergst" du ihn in main, um alle Commits des Feature-Branch in die Hauptlinie zu integrieren. Hier können Merge-Konflikte entstehen, falls dieselbe Zeile in beiden Branches geändert wurde.

  • Pull Request (PR): Dies ist ein GitHub-Konzept (kein reines Git-Konzept). Ein PR ist ein formaler "Merge-Antrag". Anstatt direkt in main zu mergen, sagst du dem Team: "Hallo, ich habe feature/user-login fertig. Bitte schaut euch meinen Code an (Code Review).

  • Squash (Zerdrücken/Zusammenfassen): Dies ist eine spezielle Merge-Option auf GitHub. In unserem Workflow machen wir viele kleine Commits auf einem Feature-Branch (feat: add button, style: fix padding, fix: typo). Ein "Squash and Merge" fasst all diese 10 Commits zu einem einzigen, sauberen Commit zusammen, bevor dieser in den main-Branch gemerged wird. Dies hält die main-Historie extrem sauber.

  • Remote-Tracking Branch (Tracking-Zeiger): Ein lokales "Lesezeichen" oder "Zeiger" (Pointer) auf den Stand eines Branch auf dem Remote-Server (origin). Diese Zeiger heissen z.B. origin/main oder origin/feature/user-login. Du kannst sie nicht direkt bearbeiten; sie werden nur durch git fetch aktualisiert und zeigen dir, "Wo war GitHub, als du das letzte Mal nachgeschaut hast?".

  • Prune (Bereinigen): Ein Git-Aufräumbefehl (git fetch --prune), der dein lokales Repo "sauber" hält. Wenn ein Feature-Branch auf GitHub gelöscht wird (z.B. nach einem Merge), existiert dein lokaler "Remote-Tracking Branch" (origin/feature/user-login) immer noch. Ein "Prune"-Befehl löscht diese veralteten Zeiger, die auf dem Remote-Server nicht mehr existieren.

  • origin: Der Standard-Kurzname für das Remote-Repository (also das "Master-Repo" auf GitHub). Wenn du git push origin main tippst, bedeutet das: "Pushe meine lokale main-Branch zur main-Branch auf dem Remote-Server namens origin."

5. Projekt-Konventionen & Setup

Lokales Setup: Git & GitHub CLI (Einmalig)

Bevor du mit dem Projekt startest, muss deine lokale Entwicklungsumgebung einmalig konfiguriert werden.

Installation

Stelle sicher, dass git (das Tool selbst) und gh (der offizielle GitHub Befehlszeilen-Client) auf deinem System installiert sind. gh ist nicht zwingend notwendig, vereinfacht aber die Authentifizierung und das Erstellen von Pull Requests enorm.

Betriebssystem git Installation gh (GitHub CLI) Installation
Arch Linux sudo pacman -S git sudo pacman -S github-cli
Debian/Ubuntu sudo apt update && sudo apt install git sudo apt update && sudo apt install gh
macOS brew install git brew install gh
Windows winget install --id Git.Git -e --source winget winget install --id GitHub.cli

Einmalige Git-Konfiguration

Nach der Installation musst du Git einmalig konfigurieren. Diese Einstellungen werden global (--global) für alle deine Projekte auf diesem Rechner gespeichert.

# 1. Identität (ESSENZIELL)
# Diese Daten erscheinen in jedem Commit, den du machst.
# WICHTIG: Verwende exakt die E-Mail, die mit deinem GitHub-Account verknüpft
# ist.
git config --global user.name "Dein Vorname Nachname"
git config --global user.email "deine-github-email@adresse.com"

# 2. Standard-Branch-Name
# Stellt sicher, dass neue Repos lokal mit 'main' statt 'master' erstellt
# werden.
git config --global init.defaultBranch main

# 3. Automatisches Aufräumen (Prune)
# Sorgt dafür, dass 'git fetch' und 'git pull' automatisch veraltete
# Tracking-Zeiger löschen.
git config --global fetch.prune true

# 4. (Optional) Standard-Editor
# Definiert deinen bevorzugten Editor für Commit-Messages (z.B. vim, nvim, nano)
# `git config --global core.editor "nvim"`

Authentifizierung (Verbindung zu GitHub)

Dein lokales git muss sich bei GitHub authentifizieren, um push und pull Befehle auf private Repositories (wie unseres) ausführen zu dürfen.

Der moderne Weg (Empfohlen): gh auth login

Früher musste man manuell "Personal Access Tokens" (PATs) auf der GitHub-Webseite erstellen und diese (oft unsicher) in der lokalen Git-Konfiguration speichern.

Der gh Client (den du in Schritt 1.1 installiert hast) macht diesen Prozess trivial. Er verbindet sich sicher über den Browser (OAuth) mit deinem GitHub-Account und richtet die Authentifizierung (als sicherer Token im System-Schlüsselbund) für beides ein: für gh und für git.

Anleitung:

  1. Führe im Terminal den Befehl aus:

    gh auth login
  2. Der Client stellt dir ein paar interaktive Fragen:

    • What account do you want to log into? -> GitHub.com (Enter)
    • What is your preferred protocol for Git operations? -> HTTPS (Enter)
    • Authenticate Git with your GitHub credentials? -> Yes (Enter)
    • How would you like to authenticate? -> Login with a web browser (Enter)
  3. gh gibt dir einen Einmal-Code und öffnet deinen Browser (oder fordert dich auf, einen Link zu öffnen).

  4. Bestätige im Browser die Berechtigungen.

  5. Fertig. Dein System ist nun sicher mit GitHub verbunden. Du musst nie wieder ein Passwort oder Token manuell in der CLI eingeben, solange der Token im Schlüsselbund deines Systems gespeichert ist.

Technischer Hintergrund

Der Befehl gh auth login (oder genauer gh auth setup-git) konfiguriert Git so, dass es gh als Credential Helper (Helfer für Anmeldeinformationen) verwendet.

Wenn git (z.B. bei git pull) nun ein Passwort für GitHub benötigt, fragt es nicht mehr den Benutzer, sondern es fragt gh. gh wiederum holt sich das sichere Token aus dem System-Schlüsselbund (z.B. macOS Keychain, Windows Credential Manager, Linux Secret Service) und gibt es an git weiter. Das ist sicher und komfortabel.

GitHub Branch Protection

Dies sollte für den main-Branch in beiden Repositories eingerichtet werden:

  1. Gehe zu Settings > Branches.
  2. Erstelle eine "Branch protection rule" für main.
  3. Aktiviere folgende Haken:
    • Require a pull request before merging (Verhindert direkte Pushes auf main).
    • Require at least 1 approval (Erzwingt das Code Review).
    • (Optional, aber empfohlen) Dismiss stale pull request approvals when new commits are pushed.

Note: Diese Einstellung wurde bereits in unseren Repositories vorgenommen.

Was sind GitHub Actions? (CI/CD)

GitHub Actions ist das eingebaute Automatisierungs-Werkzeug (CI/CD) von GitHub. "CI/CD" steht für "Continuous Integration" (kontinuierliches Bauen und Testen) und "Continuous Delivery" (kontinuierliches Ausliefern).

Für uns bedeutet das: Wir können GitHub anweisen, bei bestimmten Ereignissen (z.B. "jedes Mal, wenn jemand auf main pusht") automatisch Befehle auszuführen.

Anwendungsbeispiele für unser Projekt:

  • Testen: Jedes Mal, wenn ein PR erstellt wird, könnte eine Action automatisch alle Tests (z.B. mvn test im Backend) ausführen und den PR blockieren, wenn die Tests fehlschlagen.
  • Bauen (Build): Jedes Mal, wenn ein PR in main gemerged wird, könnte eine Action das Projekt bauen (die .jar-Datei oder die statischen Frontend-Dateien).
  • Automatisches Releasen: Eine Action könnte den gesamten Release-Prozess (Version erhöhen, Tag erstellen, Release veröffentlichen) automatisieren.

Wo und Wie konfiguriert man Actions?

Actions werden durch das Erstellen von YAML-Dateien (Textdateien mit der Endung .yml) in einem speziellen Verzeichnis innerhalb unseres Repositories konfiguriert:

.github/workflows/

Wenn wir z.B. eine Datei namens .github/workflows/run-tests.yml erstellen, würde GitHub diese automatisch erkennen und ausführen.

Beispiel (.github/workflows/run-tests.yml):

# Name des Workflows
name: Run Backend Tests

# Auslöser (Event): Wann soll das passieren?
on:
  pull_request: # Bei jedem Pull Request auf...
    branches: [main] # ... den main-Branch

# Jobs: Was soll passieren?
jobs:
  build:
    # Auf welchem Betriebssystem soll der Job laufen?
    runs-on: ubuntu-latest

    steps:
      # Schritt 1: Code auschecken
      - uses: actions/checkout@v5

      # Schritt 2: Java installieren
      - name: Set up JDK 25
        uses: actions/setup-java@v5
        with:
          java-version: "25"
          distribution: "temurin"

      # Schritt 3: Maven-Tests ausführen
      - name: Run Maven Tests
        run: mvn test

Was kostet das?

  • Öffentliche Repos: GitHub Actions sind komplett kostenlos.

  • Private Repos (Unser Fall):

    • Im kostenlosen Plan (den wir als Organization nutzen) sind 2'000 Minuten pro Monat kostenlos enthalten.
    • Diese 2'000 Minuten sind für ein 3-Personen-Studienprojekt fast sicherlich mehr als ausreichend.
    • Falls wir den "Team"-Plan (z.B. für Branch Protection) wählen würden, wären dort 3'000 Minuten enthalten.

Benennungs-Konventionen

Hier definieren wir die exakten Namensregeln für unsere Branches und Commits.

Branch-Benennung (Branch-Typen)

Um den Zweck eines Branch sofort zu erkennen, verwenden wir klare Präfixe. Die Liste deckt unsere gängigsten Anwendungsfälle ab:

  • feature/: Für das Hinzufügen einer neuen, für den Benutzer sichtbaren Funktion.
    • feature/user-login-api
    • feature/supplier-list-component
  • fix/: Zur Behebung eines Bugs im Code.
    • fix/login-button-disabled-error
    • fix/supplier-list-sorting
  • docs/: Für Ergänzungen oder Änderungen an der Dokumentation (z.B. README.md).
    • docs/update-setup-guide
    • docs/add-api-endpoints-to-readme
  • refactor/: Für Code-Überarbeitungen, die weder ein Bugfix noch ein neues Feature sind (z.B. Code "aufräumen").
    • refactor/rename-database-service
    • refactor/rename-supplier-class
  • chore/: Für "Hausmeisterarbeiten", die nichts mit dem eigentlichen Code zu tun haben.
    • chore/initial-project-setup (siehe unten)
    • chore/upgrade-angular-version
    • chore/add-prettier-config

Richtlinie für Commit-Messages (Conventional Commits)

Um eine saubere und einheitliche Historie zu haben, verwenden wir Conventional Commits.

Jede Commit-Message hat das Format: <typ>: <beschreibung>

Das von uns verwendete Set an Typen ist: feat, fix, docs, style, refactor, test, chore.

Was bedeuten die Typen?
  • feat (Feature): Eine neue Funktion für den Benutzer.
  • fix (Bugfix): Eine Behebung eines Fehlers.
  • docs (Documentation): Änderungen an der Dokumentation.
  • style (Styling): Code-Formatierung, Semikolons, Einrückung. Keine Code-Logik-Änderung.
  • refactor (Refactoring): Code-Änderung, die weder Bugfix noch Feature ist (z.B. "Aufräumen").
  • test (Tests): Hinzufügen oder Anpassen von Tests.
  • chore (Chore, "Hausarbeit"): Routineaufgaben, Build-Skripte, Tool-Konfiguration. Nichts, was der Benutzer sieht.
Beispiele für gute Commit-Messages
Typ Beispiel-Commit Erklärung
feat feat: Add supplier search bar to list view Ein klares neues Feature wird hinzugefügt.
feat feat: Implement POST /api/v1/suppliers endpoint Auch das Erstellen eines API-Endpoints ist ein feat.
fix fix: Prevent app crash when supplier list is empty Ein klarer Bug wird behoben.
fix fix: Correct http status code from 404 to 401 on auth failure Ein Bug in der API-Logik.
docs docs: Update README with new setup instructions Reiner Doku-Change.
style style: Run prettier on all .ts files Nur Formatierung, keine Logik geändert.
style style: Fix indentation in pom.xml Auch Konfigurationsdateien haben einen Stil.
refactor refactor: Extract supplier-card into new component Funktionalität bleibt gleich, aber Code-Struktur ist besser.
refactor refactor: Rename SupplierService to SupplierDataService Umbenennung für mehr Klarheit.
test test: Add unit tests for supplier-rating validator Ein neuer Test wird geschrieben.
test test: Update test for login component Ein bestehender Test wird angepasst.
chore chore: Add .dockerignore file Eine Konfigurationsdatei für ein Tool.
chore chore: Upgrade angular from 14 to 15 Eine Abhängigkeit aktualisieren.
chore chore: Add initial project structure Siehe nächster Abschnitt.
Wie signalisieren wir einen BREAKING CHANGE?

Ein "Breaking Change" (eine Änderung, die bestehenden Code bricht, z.B. eine API-Änderung) ist kein eigener Typ, sondern wird auf zwei Arten im Commit signalisiert. Dies löst immer eine MAJOR-Versionserhöhung aus (z.B. v1.2.0 -> v2.0.0).

  1. Durch einen Footer (Fusszeile): Der Commit-Body wird um einen Block erweitert, der mit BREAKING CHANGE: beginnt.

    feat: Change API endpoint for users
    
    This commit renames the main user endpoint from /users to /api/v1/users
    to follow the new API guidelines.
    
    BREAKING CHANGE: The `/users` endpoint is now `/api/v1/users`.
    All clients must update their requests.
    
  2. Durch ein ! (Ausrufezeichen) nach dem Typ: Dies ist die schnellere Kurzform, wenn die Betreffzeile ausreicht, um den Bruch zu erklären.

    feat!: Remove 'username' field from User-API

Beide Methoden sind gültig und werden von automatisierten Tools erkannt.

Der erste Commit: Was tun beim leeren Repo?

Bevor wir Features (feat) fixen (fix) oder testen (test) können, muss das Grundgerüst stehen.

  1. Repo-Initialisierung: Durch das Erstellen des Repos auf GitHub mit LICENSE, README.md und .gitignore, wurde bereits ein Initial commit erstellt.
  2. Das Grundgerüst: Das Hinzufügen der grundlegenden Ordnerstruktur (z.B. das leere SpringBoot-Projekt von start.spring.io oder das leere Angular-Projekt von ng new) ist kein Feature. Es ist eine "Hausmeistertätigkeit" und somit chore.
  3. Der richtige Workflow:
    • Erstelle einen Branch: git checkout -b chore/initial-project-setup
    • Füge das Projekt-Grundgerüst hinzu (z.B. den Code von start.spring.io).
    • Commite es: git add . -> git commit -m "chore: Add initial SpringBoot project structure"
    • Pushe diesen Branch und erstelle einen PR.
    • Warum? Auch wenn es trivial erscheint, ist es eine gute Übung. Vielleicht hat ein Kollege einen Einwand ("Warum hast du 'Lombok' nicht als Dependency hinzugefügt?"). So wird selbst das Setup diskutierbar.
  4. Erstes Feature: Dein nächstes Ticket ist dann vielleicht "Erstelle den ersten API-Endpoint". Dafür erstellst du einen neuen Branch: git checkout -b feature/hello-world-endpoint. Dein Commit wäre dann: feat: Add GET /hello endpoint.

Versionierung (Semantic Versioning)

Sobald wir einen ersten stabilen Stand (MVP) haben und diesen z.B. dem Auftraggeber übergeben kommt Versionierung ins Spiel. Es ist essenziell, es von Anfang an mitzudenken, da unsere Commit-Typen die Versionierung direkt steuern.

Wir verwenden Semantic Versioning (SemVer): MAJOR.MINOR.PATCH (z.B. 1.2.3)

  • PATCH (z.B. 1.2.2 -> 1.2.3): Wird erhöht, wenn wir einen Bug beheben.
  • MINOR (z.B. 1.2.3 -> 1.3.0): Wird erhöht, wenn wir ein neues Feature hinzufügen, das abwärtskompatibel ist.
  • MAJOR (z.B. 1.3.0 -> 2.0.0): Wird erhöht, wenn wir eine Änderung machen, die nicht abwärtskompatibel ist (eine "Breaking Change").

Verbindung zu Commits

  • Ein Merge eines fix:-Commits -> Erhöht PATCH.
  • Ein Merge eines feat:-Commits -> Erhöht MINOR.

Start-Version: Wir fangen mit 0.1.0 an, sobald wir die erste präsentable Version des MVP haben. Alle Versionen unter 1.0.0 gelten per Definition als "instabil".

Der Release-Prozess (Eine Version veröffentlichen)

Dies ist der Prozess, um eine offizielle, neue Version unserer Software zu erstellen (z.B. v0.1.0).

Wichtiger Hinweis: Unabhängige Versionierung

Da wir zwei separate Repositories (frontend und backend) haben, versionieren wir diese unabhängig voneinander. Ein Release-Prozess wird immer nur innerhalb eines Repositories durchgeführt.

Wenn wir nur einen Bug im Backend beheben, erstellen wir eine neue Backend-Version (z.B. v0.1.1), während die Frontend-Version (z.B. v0.1.0) unverändert bleibt.

Wann starten wir?

Wir starten mit v0.1.0 für das jeweilige Repo, sobald dessen MVP-Status erreicht ist.

Wie bestimmen wir die neue Versionsnummer?

Wir schauen uns die Commits an, die seit dem letzten Release in main gemerged wurden.

  • Wurden nur fix:-Commits gemerged? -> PATCH-Erhöhung (z.B. v0.1.0 -> v0.1.1)
  • Wurden (auch) feat:-Commits gemerged? -> MINOR-Erhöhung (z.B. v0.1.1 -> v0.2.0)
  • Gab es einen BREAKING CHANGE: (oder feat!)? -> MAJOR-Erhöhung (z.B. v0.2.0 -> v1.0.0)

Der manuelle Release-Workflow (Schritt-für-Schritt)

Der folgende Prozess muss im Ordner des jeweiligen Repositories ausgeführt werden.

Schritt 1: Release-Branch erstellen

Wir bündeln die Versionsänderung in einem eigenen chore-Branch.

git checkout main
git pull origin main
git checkout -b chore/release-v0.1.0
Schritt 2: Versionsnummer manuell erhöhen

Wir passen die Version nur in der relevanten Datei dieses Repos an.

  • Backend: pom.xml -> <version>0.1.0</version>
  • Frontend: package.json -> "version": "0.1.0"
Schritt 3: Änderung committen und mergen

Wir committen diese Änderung, erstellen einen PR und mergen ihn.

# Beispiel für Backend. Für Frontend 'package.json' adden.
git add pom.xml
git commit -m "chore: Bump version to 0.1.0"
git push -u origin chore/release-v0.1.0

(Jetzt den PR auf GitHub erstellen, reviewen und per Squash and Merge in main mergen.)

Der main-Branch enthält jetzt den Code, der offiziell v0.1.0 ist.

Schritt 4: Den main-Branch "taggen"
  1. Hole den aktuellsten Stand von main:

    git checkout main
    git pull origin main
  2. Erstelle einen "annotated" (kommentierten) Tag:

    git tag -a v0.1.0 -m "Initial MVP release v0.1.0"
  3. Pushe diesen neuen Tag auf GitHub (origin):

    git push origin v0.1.0
Schritt 5: Die "Release" auf GitHub erstellen (CLI-Weg)

Der gh-Befehl wird im Ordner des Repositories ausgeführt. Er erkennt automatisch, zu welchem Repo auf GitHub er gehört.

gh release create v0.1.0 --generate-notes

Was macht --generate-notes? gh schaut sich alle Commits in diesem Repo seit dem letzten Tag an. Es listet automatisch alle gemergedten PRs dieses Repos auf und gruppiert sie, um eine perfekte Release-Beschreibung zu erstellen.

Automatisierung des Release-Prozesses (Ausblick)

Der oben beschriebene Prozess ist manuell. Für die Zukunft kann dieser Workflow fast vollständig automatisiert werden.

Wie funktioniert die Automatisierung?

Eine CI/CD-Pipeline (z.B. eine GitHub Action) wird so konfiguriert, dass sie bei jedem Merge in main die Commit-Messages liest.

Tools für die Automatisierung (Repo-spezifisch)

  1. Frontend: semantic-release (JavaScript/npm)

    • Dieses Tool liest die feat:/fix: Commits im Frontend-Repo.
    • Es ändert dann automatisch die package.json, committet, erstellt den Git-Tag (z.B. v0.2.0) und erstellt die GitHub Release.
  2. Backend: maven-release-plugin (oder JReleaser)

    • Dies sind die Standard-Tools für das Backend-Repo.
    • Sie verwalten die pom.xml-Versionen (z.B. von 0.1.0-SNAPSHOT auf 0.1.0 und dann auf 0.1.1-SNAPSHOT).
    • Sie können ebenfalls automatisiert werden, um Tags und Releases zu erstellen.
  3. GitHub Auto-Generated Release Notes

    • Dies ist die Funktionalität, die wir im manuellen Prozess mit gh release create --generate-notes nutzen.
    • Wichtig: Wenn wir unsere PR-Titel (nicht nur die Commits) im "Conventional Commit"-Stil schreiben (z.B. feat: Add login page), kann GitHub die Release Notes automatisch in "Features", "Bugfixes" etc. gruppieren, da es beim "Squash and Merge" den PR-Titel als Commit-Message für den Merge auf main verwendet.

Automatisches Bauen & Anhängen von Release-Artefakten (CI/CD)

Der in den vorigen Abschnitten beschriebene "manuelle" Release-Prozess hat noch einen manuellen Schritt: Er erstellt zwar die Release-Notizen (gh release create --generate-notes), hängt aber keine Dateien (wie die .jar- oder Frontend-dist-Dateien) an.

Diesen Schritt können (und sollten) wir ebenfalls automatisieren.

Der automatisierte Release-Workflow (Übersicht)

Der neue Workflow ändert sich leicht. Anstatt dass wir gh release create lokal ausführen (Schritt 5), lassen wir eine GitHub Action die gesamte Release-Erstellung übernehmen, sobald sie einen neuen Tag erkennt.

Der neue lokale Prozess:

  1. chore/release-vX.Y.Z-Branch erstellen.
  2. pom.xml / package.json ändern.
  3. PR erstellen und mergen.
  4. main pullen.
  5. git tag -a vX.Y.Z -m "..."
  6. git push origin vX.Y.Z <-- DAS IST DER AUSLÖSER (TRIGGER)

Der automatische GitHub Action Prozess:

  1. Die Action wird durch den neuen Tag-Push ausgelöst.
  2. Sie checkt den Code (des Tags) aus.
  3. Sie installiert die nötige Umgebung (z.B. Java/Maven).
  4. Sie führt den Build-Befehl aus (z.B. mvn package).
  5. Sie erstellt die Release (wie gh release create ...) UND hängt die Artefakte (die .jar-Datei aus dem target-Ordner) an die Release an.

Beispiel: .github/workflows/release-backend.yml

Dies ist ein Beispiel für eine Action-Datei, die wir im Backend-Repo unter .github/workflows/release-backend.yml erstellen können. Eine ähnliche Datei (mit npm run build) würde im Frontend-Repo liegen.

name: Create Release and Upload Artifact

# Auslöser (Trigger):
on:
  push:
    tags:
      - "v*.*.*" # Startet nur, wenn ein Tag gepusht wird, der wie v1.2.3 aussieht

jobs:
  build-and-release:
    runs-on: ubuntu-latest

    steps:
      # 1. Code (des Tags) auschecken
      - name: Checkout code
        uses: actions/checkout@v5

      # 2. Java (z.B. Version 17) installieren
      - name: Set up JDK 17
        uses: actions/setup-java@v5
        with:
          java-version: "25"
          distribution: "temurin"
          cache: "maven" # Beschleunigt zukünftige Builds

      # 3. Das Projekt bauen (erzeugt die .jar Datei)
      # Das 'clean package' stellt sicher, dass alles neu gebaut wird.
      # '-B' = Batch-Modus (für Server)
      - name: Build with Maven
        run: mvn -B clean package

      # 4. Release erstellen UND Artefakt anhängen
      # Dies ist der wichtigste Schritt.
      # Der Befehl 'gh release create' kann Dateipfade als Argumente entgegennehmen.
      # GITHUB_TOKEN ist ein geheimer Token, den die Action automatisch erhält.
      - name: Create Release and Upload Artifact
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          # Finde den Namen der .jar-Datei (da er die Version enthält)
          JAR_FILE=$(find target -name "*.jar" | head -n 1)

          # Erstelle die Release mit Notizen UND hänge die .jar-Datei an
          gh release create ${{ github.ref_name }} \
              --generate-notes \
              "$JAR_FILE"

6. Weiterführende Ressourcen / Grundlagen für dieses Dokument

  • Offizielle Git-Webseite & Dokumentation

  • Offizielle GitHub CLI Dokumentation

    • Die Dokumentation für gh mit Installationsanleitungen und einer vollständigen Befehlsreferenz.
    • GitHub Docs: GitHub CLI
  • GitHub Flow (Unser Workflow)

    • Offizielle Anleitung (GitHub Docs): Die beste und direkteste Erklärung, was GitHub Flow ist und wie es funktioniert.
    • Original-Webseite: Die ursprüngliche Webseite, die die Philosophie dahinter erklärt
  • Gitflow (Komplexerer Workflow zum Vergleich)

    • Atlassian Tutorial: Erklärung des alternativen, komplexeren Gitflow-Modells (mit develop-Branch).
  • Conventional Commits (Unsere Commit-Benennung)

    • Offizielle Spezifikation: Die Grundlage für feat:, fix:, chore:, etc. Hier sind alle Regeln und Typen definiert.
  • Semantic Versioning (Unsere Versionierung)

    • Offizielle Spezifikation: Die exakte Definition von MAJOR.MINOR.PATCH und den Regeln.
  • Branch-Benennung (Best Practices)

7. KnowHow / Probleme

1. Problem Abhängige Branches ("Stacked Branches"): Weiterarbeiten trotz Wartezeit

Dies ist ein häufiges Szenario: Du hast Feature A fertig und einen PR erstellt. Deine Kollegen haben aber noch keine Zeit für ein Review. Du möchtest nicht warten, sondern direkt mit Feature B weitermachen, welches aber auf dem Code von Feature A aufbaut.

Man nennt dies "Stacked Branches" (gestapelte Branches).

Das Problem (Die Falle)

Wenn du Feature B startest, während Feature A noch nicht in main ist, musst du Feature B von Feature A abzweigen (nicht von main).

Der kritische Moment kommt beim Mergen:

  1. Feature A wird in main gemerged und der Branch Feature A wird gelöscht.
  2. Dein PR für Feature B zeigt aber immer noch auf den (jetzt gelöschten) Branch Feature A als Basis.
  3. Fehler: Wenn du jetzt versuchst, PR B in den "toten" Branch A zu mergen, landen deine Commits im Nichts. Sie kommen nie in main an.

Der korrekte Workflow (Best Practice)

So gehst du vor, um sicher mit gestapelten Branches zu arbeiten:

1. Starten (Der Stack)

  1. Du befindest dich auf feature/A (dein fertiger Branch).
  2. Erstelle den neuen Branch von hier aus: git checkout -b feature/B
  3. Arbeite an feature/B, committe und pushe.
  4. Erstelle einen PR für feature/B und setze als "Base" (Ziel) den Branch feature/A (nicht main!).
    • Vorteil: Der Reviewer sieht im PR B nur die neuen Änderungen von B, nicht nochmal alles von A.

2. Auflösen (Das "Re-Targeting")

Sobald Feature A approved und in main gemerged wurde:

  1. Lösche den Branch feature/A auf GitHub (der übliche Button nach dem Merge).
  2. Gehe sofort zu deinem offenen PR für Feature B.
  3. GitHub wird oft schon warnen, dass die Basis fehlt.
  4. Klicke oben neben dem Titel auf "Edit" (oder "Change base").
  5. Ändere die Basis (Base Branch) von feature/A auf main.
  6. Resultat: Da feature/A jetzt Teil von main ist, zeigt der PR B nun korrekt nur noch die Differenz (deine neuen Commits) im Vergleich zu main an.
  7. Jetzt kann PR B ganz normal reviewed und gemerged werden.