Kapitel 2: Fortgeschrittenes Branching – Strategien für professionelle Entwicklung
Einleitung: Im Einsteigerkurs hast du gelernt, Branches zu erstellen und zu mergen. In der professionellen Entwicklung reicht das nicht – du brauchst eine durchdachte Branching-Strategie, die zu deinem Projekt und Team passt. Git Flow, GitHub Flow, Trunk-Based Development – jede Strategie hat ihre Vor- und Nachteile. Außerdem wirst du lernen, wie du mit Long-Running Branches umgehst, Release-Branches verwaltest und Hotfixes in mehrere Branches gleichzeitig einpflegst. Dieses Kapitel verwandelt dich vom Branch-Nutzer zum Branch-Strategen.
- Branching-Strategien im Vergleich: Git Flow, GitHub Flow und Trunk-Based Development
- Long-Running Branches verstehen und synchron halten
- Der Hotfix-Workflow: Kritische Bugfixes sicher in alle Branches bringen 🚨
- Protected Branches auf GitHub – Dein Sicherheitsnetz für kritische Branches
Branching-Strategien im Vergleich: Git Flow, GitHub Flow und Trunk-Based Development
Die Wahl der richtigen Branching-Strategie ist eine der wichtigsten architektonischen Entscheidungen für dein Projekt. Sie beeinflusst, wie dein Team zusammenarbeitet, wie schnell ihr Features ausliefern könnt und wie stabil eure Releases sind. Lass mich dir die drei populärsten Strategien im Detail vorstellen.
🌳 Git Flow – der strukturierte Klassiker
Git Flow wurde 2010 von Vincent Driessen vorgestellt und war lange Zeit der Standard für professionelle Softwareentwicklung. Es ist eine ausgeklügelte, aber komplexe Strategie mit klar definierten Branch-Typen und strengen Regeln.
Die Branch-Struktur
gitGraph
commit id: "Initial"
branch develop
checkout develop
commit id: "Setup"
branch feature/login
checkout feature/login
commit id: "Login UI"
commit id: "Login Logic"
checkout develop
merge feature/login id: "Merge Login"
branch release/1.0
checkout release/1.0
commit id: "Bugfix"
commit id: "Version bump"
checkout main
merge release/1.0 id: "Release 1.0" tag: "v1.0"
checkout develop
merge release/1.0 id: "Back-merge"
Git Flow definiert fünf Branch-Typen mit jeweils spezifischen Aufgaben:
-
main(frühermaster)Der heilige Gral deines Repositories. Dieser Branch enthält ausschließlich produktionsreifen Code. Jeder Commit auf
mainrepräsentiert eine Release-Version und wird typischerweise mit einem Tag versehen (z.B.v1.0.0,v1.1.0). Direktes Committen aufmainist streng verboten – Änderungen kommen nur durch Merges von Release- oder Hotfix-Branches. -
developDer Integrations-Branch für alle neuen Entwicklungen. Hier fließen alle abgeschlossenen Features zusammen.
developrepräsentiert den aktuellen Entwicklungsstand und ist die Basis für neue Feature-Branches. Man könnte sagen:mainist „was der Kunde sieht",developist „was als nächstes kommt". -
feature/*(z.B.feature/user-authentication,feature/shopping-cart)Für jedes neue Feature wird ein eigener Branch von
developabgezweigt. Hier findet die eigentliche Entwicklungsarbeit statt. Feature-Branches können beliebig viele Commits enthalten und existieren so lange, bis das Feature fertig ist. Nach Abschluss wird der Branch zurück indevelopgemergt und gelöscht. -
release/*(z.B.release/1.2.0)Wenn
developgenügend Features für eine neue Version angesammelt hat, wird ein Release-Branch abgezweigt. Ab diesem Zeitpunkt kommen keine neuen Features mehr hinzu – nur noch Bugfixes, Dokumentation und Release-Vorbereitungen (Versionsnummern aktualisieren, Changelog schreiben). Nach Fertigstellung wird der Release-Branch sowohl inmainals auch zurück indevelopgemergt. -
hotfix/*(z.B.hotfix/1.0.1-security-patch)Für kritische Bugs in der Produktion, die nicht auf den nächsten regulären Release warten können. Hotfix-Branches werden direkt von
mainabgezweigt, enthalten nur die minimale Änderung zur Fehlerbehebung und werden nach Fertigstellung sowohl inmain(mit neuem Tag) als auch indevelopgemergt.
Der typische Workflow in Git Flow
Ein neues Feature durchläuft folgenden Weg:
develop → feature/xyz → develop → release/1.0 → main + develop
↑
(Bugfixes)
Und ein Hotfix:
main → hotfix/1.0.1 → main + develop
Vor- und Nachteile von Git Flow
| ✅ Vorteile | ❌ Nachteile |
|---|---|
| Klare Trennung zwischen Entwicklung und Produktion | Hohe Komplexität durch viele Branch-Typen |
| Parallele Arbeit an Release und neuen Features möglich | Lange Feature-Branches führen zu schwierigen Merges |
| Gut geeignet für versionierte Software mit Releases | Langsamerer Entwicklungszyklus |
Stabile main-Branch garantiert |
Erfordert Disziplin und Tooling |
| Hotfixes können unabhängig eingespielt werden | Overkill für kleine Teams oder Web-Apps |
Wann Git Flow verwenden?
Git Flow eignet sich besonders für:
- Versionierte Software mit klaren Release-Zyklen (z.B. Desktop-Apps, Mobile Apps, Libraries)
- Projekte mit Support für mehrere Versionen gleichzeitig
- Größere Teams (5+ Entwickler) mit spezialisierten Rollen
- Regulierte Umgebungen, die Nachvollziehbarkeit und Audits erfordern
- Software mit längeren Release-Zyklen (Wochen bis Monate)
🚀 GitHub Flow – schlank und kontinuierlich
GitHub Flow wurde von GitHub selbst als Reaktion auf die Komplexität von Git Flow entwickelt. Es ist radikal einfacher: Es gibt nur main und Feature-Branches. Die Philosophie ist „ship early, ship often" – kontinuierliche Auslieferung kleiner Änderungen.
Die Branch-Struktur
gitGraph
commit id: "Initial"
branch feature/login
checkout feature/login
commit id: "Login UI"
commit id: "Login Tests"
checkout main
merge feature/login id: "PR #1" tag: "deployed"
branch feature/dashboard
checkout feature/dashboard
commit id: "Dashboard"
checkout main
merge feature/dashboard id: "PR #2" tag: "deployed"
branch bugfix/header
checkout bugfix/header
commit id: "Fix Header"
checkout main
merge bugfix/header id: "PR #3" tag: "deployed"
Die sechs Regeln von GitHub Flow
-
mainist immer deploybarDer
main-Branch muss zu jedem Zeitpunkt in Produktion deployt werden können. Das bedeutet: Nur getesteter, funktionierender Code kommt hinein. -
Für jede Änderung einen Branch erstellen
Egal ob Feature, Bugfix oder Experiment – erstelle einen beschreibend benannten Branch von
main(z.B.add-user-profile,fix-login-timeout,experiment/new-checkout). -
Regelmäßig committen und pushen
Kleine, häufige Commits mit klaren Nachrichten. Push regelmäßig zu GitHub, damit andere den Fortschritt sehen und du ein Backup hast.
-
Pull Request erstellen, wenn bereit für Review
Sobald deine Änderung bereit für Feedback ist (oder du Diskussion brauchst), öffne einen Pull Request. Dies ist der zentrale Ort für Code Review und Diskussion.
-
Nach Review und Approval: Merge in
mainSobald der PR approved ist und alle Tests grün sind, wird er in
maingemergt. Bei GitHub Flow gibt es keine Zwischenstationen. -
Sofort nach dem Merge deployen
Der Merge in
maintriggert (idealerweise automatisch) ein Deployment. Das bedeutet, dass jeder Merge innerhalb von Minuten in Produktion ist.
Der typische Workflow in GitHub Flow
main → feature-branch → Pull Request → Code Review → main → Deploy
↑ ↓
(entwickeln) (automatische Tests)
Vor- und Nachteile von GitHub Flow
| ✅ Vorteile | ❌ Nachteile |
|---|---|
| Extrem einfach zu verstehen und umzusetzen | Keine native Unterstützung für versionierte Releases |
| Fördert kontinuierliche Integration | Erfordert sehr gute Testabdeckung und CI/CD |
| Schnelle Iteration und Feedback-Zyklen | Hotfixes sind „normal" – keine spezielle Behandlung |
| Pull Requests als zentrale Review-Plattform | Weniger geeignet, wenn mehrere Versionen unterstützt werden müssen |
| Ideal für Web-Applikationen und SaaS | Kann bei großen Features unübersichtlich werden |
Wann GitHub Flow verwenden?
GitHub Flow eignet sich besonders für:
- Web-Applikationen und SaaS, die kontinuierlich deployt werden
- Kleine bis mittlere Teams (1–10 Entwickler)
- Projekte mit guter CI/CD-Pipeline und automatisierten Tests
- Agile Teams mit kurzen Sprints und häufigen Releases
- Startups und schnell iterierende Produkte
🎯 Trunk-Based Development – der radikale Ansatz
Trunk-Based Development (TBD) geht noch einen Schritt weiter als GitHub Flow. Die Idee: Alle Entwickler committen direkt in einen einzigen Branch (den „Trunk", typischerweise main). Feature-Branches existieren entweder gar nicht oder sind extrem kurzlebig (maximal 1–2 Tage).
Die Branch-Struktur
gitGraph
commit id: "Feature A - Part 1"
commit id: "Feature A - Part 2"
commit id: "Bugfix"
commit id: "Feature B" tag: "deployed"
commit id: "Refactoring"
commit id: "Feature A - Part 3"
commit id: "Feature A complete" tag: "deployed"
Die Kernprinzipien
-
Ein einziger Branch für alle
Es gibt nur
main(den „Trunk"). Alle Entwickler integrieren ihre Änderungen hier. Lange lebende Feature-Branches sind verboten. -
Sehr kleine, sehr häufige Commits
Statt eines großen Commits am Ende eines Features gibt es viele kleine Commits während der Entwicklung. Jeder Commit muss den Build grün halten.
-
Feature Flags für unfertige Features
Wie bringt man Code für ein halbfertiges Feature in
main, ohne dass Nutzer es sehen? Durch Feature Flags (auch Feature Toggles genannt):if ($featureFlags->isEnabled('new-checkout-process')) { return $this->newCheckoutProcess($cart); } else { return $this->legacyCheckout($cart); }Das Feature wird erst „eingeschaltet", wenn es fertig ist – obwohl der Code schon lange in Produktion ist.
-
Kurzlebige Feature-Branches (optional)
Manche Teams erlauben Feature-Branches, aber mit strikter Regel: Sie dürfen maximal 1–2 Tage existieren und müssen dann gemergt werden. Das erzwingt kleine, inkrementelle Änderungen.
-
Exzellente CI/CD ist Pflicht
Da jeder Commit potenziell in Produktion geht, muss die Test-Pipeline schnell und zuverlässig sein. Typischerweise < 10 Minuten für den kompletten Build.
Branch by Abstraction
Für größere Refactorings, die nicht in 1–2 Tagen erledigt sind, nutzt TBD das Pattern „Branch by Abstraction":
- Erstelle eine Abstraktionsschicht (Interface) vor dem zu ändernden Code
- Implementiere die neue Version hinter dem Interface
- Schalte schrittweise um (Feature Flag oder schrittweise Migration)
- Entferne die alte Implementierung, sobald die neue stabil ist
So bleibt der Code immer lauffähig, obwohl du parallel zwei Implementierungen hast.
Vor- und Nachteile von Trunk-Based Development
| ✅ Vorteile | ❌ Nachteile |
|---|---|
| Keine Merge-Hölle durch lang lebende Branches | Erfordert erfahrene, disziplinierte Entwickler |
| Maximale Kontinuierliche Integration | Feature Flags erhöhen Code-Komplexität |
| Sehr schnelles Feedback | Sehr gute Testabdeckung ist Voraussetzung |
| Fördert kleine, fokussierte Änderungen | Kann für Junior-Entwickler überfordernd sein |
| Von Google, Facebook und anderen Tech-Giganten verwendet | Weniger Review-Möglichkeiten vor dem Merge |
Wann Trunk-Based Development verwenden?
Trunk-Based Development eignet sich besonders für:
- Erfahrene, hochperformante Teams mit starker Ingenieurskultur
- Unternehmen mit exzellenter CI/CD-Infrastruktur
- Projekte, die mehrmals täglich deployen wollen
- Teams, die Pair Programming praktizieren (ersetzt Code Review)
- Microservices-Architekturen mit kleinen, fokussierten Repositories
📊 Der große Vergleich
| Aspekt | Git Flow | GitHub Flow | Trunk-Based |
|---|---|---|---|
| Komplexität | Hoch | Niedrig | Mittel* |
| Branch-Typen | 5 (main, develop, feature, release, hotfix) | 2 (main, feature) | 1 (main) |
| Lebensdauer Feature-Branch | Tage bis Wochen | Stunden bis Tage | Keine oder < 2 Tage |
| Release-Modell | Geplante Versionen | Kontinuierlich | Kontinuierlich |
| Merge-Frequenz | Selten, dafür größer | Häufig | Sehr häufig (mehrmals täglich) |
| CI/CD-Anforderung | Optional | Empfohlen | Pflicht |
| Feature Flags nötig? | Nein | Selten | Ja |
| Parallele Versionen | Ja | Nein | Nein |
| Team-Größe | Mittel bis groß | Klein bis mittel | Klein bis mittel (erfahren) |
| Lernkurve | Steil | Flach | Mittel |
*Die Komplexität bei TBD liegt nicht im Branching, sondern in den Praktiken (Feature Flags, kleine Commits, CI/CD).
💡 Meine Empfehlung für dein PHP-Webprojekt
Du beschreibst ein mittelgroßes PHP-Webprojekt mit regelmäßigen Releases. Lass mich die Faktoren analysieren:
Analyse deiner Situation
- „Mittelgroß" deutet auf ein Team von 2–8 Entwicklern hin
- „PHP-Webprojekt" ist typischerweise eine Web-Applikation, die deployt wird (nicht installierte Software)
- „Regelmäßige Releases" können unterschiedlich interpretiert werden:
- Wöchentlich/zweiwöchentlich → GitHub Flow geeignet
- Monatlich mit festen Versionen → Git Flow könnte passen
- Kontinuierlich → Trunk-Based möglich
Meine Empfehlung: GitHub Flow mit Release-Tags 🏆
Für dein Szenario empfehle ich GitHub Flow, aber mit einer kleinen Erweiterung für die Versionierung:
flowchart LR
subgraph Entwicklung
A["Feature-Branch\nerstellen"] --> B["Entwickeln\nund testen"]
B --> C["Pull Request\nerstellen"]
C --> D["Code Review"]
D --> E["Merge in main"]
end
subgraph Release
E --> F{"Release\nfällig?"}
F -->|Ja| G["Tag erstellen\nv1.2.0"]
F -->|Nein| H["Weiter entwickeln"]
G --> I["Deployment"]
end
style G fill:#90EE90,color:#000000
style I fill:#87CEEB,color:#000000
Warum GitHub Flow für dich?
-
Einfachheit: Du und dein Team müsst nicht fünf verschiedene Branch-Typen jonglieren. Die Regeln passen auf einen Bierdeckel.
-
Web-Applikation: PHP-Webprojekte werden deployt, nicht installiert. Du brauchst keine parallelen Versionen zu unterstützen – es gibt nur „was gerade live ist".
-
Pull Requests als Qualitätssicherung: Jede Änderung durchläuft einen Review-Prozess. Das ist Gold wert für die Code-Qualität.
-
Flexibilität bei Releases: Du kannst so oft oder selten releasen, wie du möchtest. Ein Release ist einfach ein Tag auf
main. -
Einfacher Einstieg: Falls Teammitglieder noch nicht so Git-erfahren sind, ist GitHub Flow viel leichter zu erlernen als Git Flow.
Wie du „regelmäßige Releases" mit GitHub Flow umsetzt
Anstatt Release-Branches zu nutzen, arbeitest du mit Tags und einem Release-Rhythmus:
# Nach dem Merge aller Features für Version 1.2.0
git tag -a v1.2.0 -m "Release 1.2.0: User Dashboard, Performance Improvements"
git push origin v1.2.0
In GitHub kannst du dann einen formalen Release erstellen, der auf diesen Tag verweist – mit Changelog, Release Notes und ggf. Assets.
Dein angepasster Workflow
-
Für jede Aufgabe: Erstelle einen Branch von
maingit checkout main git pull git checkout -b feature/user-dashboard -
Entwickle und committe regelmäßig mit aussagekräftigen Nachrichten
-
Erstelle einen Pull Request auf GitHub, wenn bereit für Review
-
Nach Approval: Merge in
main(ich empfehle „Squash and Merge" für eine saubere Historie) -
Für Releases: Wenn genügend Features gemergt sind, erstelle einen Tag und deploye:
git checkout main git pull git tag -a v1.2.0 -m "Release 1.2.0" git push origin v1.2.0 # Deployment triggern (manuell oder automatisch via GitHub Actions) -
Für Hotfixes: Behandle sie wie normale Features – Branch erstellen, fixen, PR, mergen. Danach ggf. Patch-Version taggen (
v1.2.1).
Wann du doch Git Flow in Betracht ziehen solltest
Überdenke meine Empfehlung, wenn einer dieser Punkte zutrifft:
- Du musst mehrere Versionen parallel unterstützen (z.B. v1.x und v2.x gleichzeitig pflegen)
- Du hast sehr lange Release-Zyklen (> 1 Monat) mit umfangreichen QA-Phasen
- Du arbeitest in einer regulierten Branche (Medizin, Finanzen) mit strengen Audit-Anforderungen
- Dein Team ist sehr groß (> 10 Entwickler) und braucht klare Abgrenzungen
Wann Trunk-Based Development interessant wäre
- Du möchtest mehrmals täglich deployen
- Dein Team ist sehr erfahren und praktiziert Pair Programming
- Du hast eine exzellente CI/CD-Pipeline mit < 10 Minuten Build-Zeit
- Du bist bereit, in Feature Flags zu investieren
🎓 Zusammenfassung
| Strategie | Motto | Ideal für |
|---|---|---|
| Git Flow | „Struktur und Kontrolle" | Versionierte Software, große Teams, lange Zyklen |
| GitHub Flow | „Ship early, ship often" | Web-Apps, kleine-mittlere Teams, kontinuierliche Delivery |
| Trunk-Based | „Immer integriert" | Hochperformante Teams, mehrfach tägliches Deployment |
Für dein mittelgroßes PHP-Webprojekt ist GitHub Flow der Sweet Spot: Einfach genug, um schnell produktiv zu sein, aber strukturiert genug für professionelle Zusammenarbeit. Mit Tags und GitHub Releases bekommst du die Versionierung, die du für „regelmäßige Releases" brauchst – ohne die Komplexität von Git Flow.
Starte mit GitHub Flow, und wenn du merkst, dass du mehr Struktur brauchst, kannst du immer noch zu Git Flow wechseln. Der umgekehrte Weg (von komplex zu einfach) ist erfahrungsgemäß schwieriger! 😉
Long-Running Branches verstehen und synchron halten
Was sind Long-Running Branches?
Long-Running Branches (auch langlebige oder permanente Branches genannt) sind Branches, die über die gesamte Lebensdauer eines Projekts oder zumindest über längere Zeiträume hinweg bestehen bleiben – im Gegensatz zu kurzlebigen Feature-Branches, die nach dem Merge gelöscht werden.
Die typischen Long-Running Branches
In den meisten professionellen Projekten findest du folgende Struktur:
| Branch | Zweck | Lebensdauer |
|---|---|---|
main (oder master) |
Produktionscode – enthält nur stabilen, deploybaren Code | Permanent |
develop |
Integrationsbranch für die nächste Version – hier fließen Feature-Branches zusammen | Permanent |
release/* |
Vorbereitung einer neuen Version, nur noch Bugfixes erlaubt | Mittelfristig (Wochen) |
hotfix/* |
Kritische Fixes für Produktion | Kurzfristig (Tage) |
gitGraph
commit id: "Initial"
branch develop
commit id: "Setup"
branch feature/login
commit id: "Login-Form"
commit id: "Validation"
checkout develop
merge feature/login id: "Merge Login"
branch release/1.0
commit id: "Version bump"
commit id: "Bugfix"
checkout main
merge release/1.0 id: "Release 1.0" tag: "v1.0.0"
checkout develop
merge release/1.0 id: "Sync Release"
Warum ist Synchronisation so wichtig? ⚠️
Stell dir folgendes Szenario vor:
- Du erstellst am Montag einen Feature-Branch von
main - Du arbeitest zwei Wochen an deinem Feature
- In der Zwischenzeit wurden 50 Commits in
maingemergt (von anderen Entwicklern oder anderen Features) - Dein Feature-Branch ist jetzt zwei Wochen und 50 Commits hinter
main
Die Probleme, die entstehen können:
- Merge-Konflikte: Je länger du wartest, desto mehr Konflikte häufen sich an. Ein Merge nach zwei Wochen kann dutzende Konflikte haben, die du alle auf einmal lösen musst.
- Inkompatibilitäten: Vielleicht hat jemand eine Funktion geändert, die du verwendest. Dein Code funktioniert nicht mehr mit dem aktuellen Stand.
- Doppelte Arbeit: Möglicherweise wurde bereits Code geschrieben, den du gerade selbst implementierst.
- Veraltete Basis: Du testest dein Feature gegen eine veraltete Codebasis – es könnte in Produktion anders funktionieren.
Die Lösung: Regelmäßige Synchronisation – idealerweise täglich oder zumindest bei jedem größeren Meilenstein in main.
Die zwei Strategien: Merge vs. Rebase
Es gibt zwei grundlegend verschiedene Ansätze, um Änderungen aus main in deinen Feature-Branch zu integrieren. Beide haben ihre Berechtigung, und die Wahl hängt von deinem Workflow und deinen Team-Konventionen ab.
Strategie 1: Merge (main → Feature-Branch)
Bei einem Merge werden die Änderungen aus main in deinen Feature-Branch integriert, wobei ein Merge-Commit entsteht, der beide Historien verbindet.
gitGraph
commit id: "A"
commit id: "B"
branch feature/shop
commit id: "F1"
commit id: "F2"
checkout main
commit id: "C"
commit id: "D"
checkout feature/shop
merge main id: "Merge main" type: HIGHLIGHT
commit id: "F3"
Was passiert hier?
- Dein Feature-Branch enthält nach dem Merge sowohl deine Commits (F1, F2) als auch die neuen Commits aus
main(C, D) - Ein Merge-Commit dokumentiert, wann du synchronisiert hast
- Die ursprüngliche Historie bleibt vollständig erhalten
Strategie 2: Rebase (Feature-Branch auf main)
Bei einem Rebase werden deine Feature-Commits „abgelöst" und auf die Spitze von main neu aufgesetzt. Es entsteht eine lineare Historie ohne Merge-Commits.
gitGraph
commit id: "A"
commit id: "B"
commit id: "C"
commit id: "D"
branch feature/shop
commit id: "F1 neu" type: HIGHLIGHT
commit id: "F2 neu" type: HIGHLIGHT
commit id: "F3"
Was passiert hier?
- Deine Commits werden „verschoben" – sie basieren jetzt auf dem aktuellen Stand von
main - Es entstehen technisch gesehen neue Commits (mit neuen Hashes), auch wenn der Inhalt identisch ist
- Die Historie sieht aus, als hättest du erst jetzt mit deiner Arbeit begonnen
Ausführliche Anleitung: Synchronisation mit Merge
Szenario
Du arbeitest an einem Feature-Branch feature/newsletter und möchtest die neuesten Änderungen aus main integrieren.
Schritt 1: Sicherstellen, dass du einen sauberen Arbeitsstand hast
Bevor du irgendetwas synchronisierst, sollte dein Working Directory „sauber" sein – also keine uncommitteten Änderungen enthalten.
In PhpStorm prüfen:
- Schau in der linken unteren Ecke auf den „Git"-Tab
- Unter „Local Changes" sollten keine Dateien stehen
Falls du unfertige Änderungen hast, nutze Stash:
# Terminal-Variante
git stash push -m "WIP: Newsletter-Formular"
In PhpStorm:
- Menü: Git → Uncommitted Changes → Stash Changes...
- Gib eine aussagekräftige Nachricht ein
- Klicke Create Stash
Schritt 2: Den neuesten Stand von main holen (Fetch)
Zuerst musst du sicherstellen, dass dein lokales Repository die neuesten Informationen vom Remote hat.
In PhpStorm:
- Klicke auf das blaue ↓ Pfeil-Symbol in der Toolbar (oder Ctrl+T / Cmd+T)
- Oder: Menü Git → Fetch
Dies aktualisiert origin/main mit dem Stand auf GitHub, ohne deinen lokalen main-Branch zu verändern.
Schritt 3: main in deinen Feature-Branch mergen
Methode A: Über das Git-Log in PhpStorm (empfohlen)
- Öffne das Git-Log: Klicke unten auf den Tab „Git" und dann auf „Log"
- Stelle sicher, dass du auf deinem Feature-Branch bist (sichtbar unten rechts in der Statusleiste)
- Im Log siehst du alle Branches – finde
origin/main - Rechtsklick auf den neuesten Commit von
origin/main - Wähle „Merge 'origin/main' into 'feature/newsletter'"
flowchart TD
A["1. Git-Log offnen\n Ansicht: Git - Log"] --> B["2. Branch prufen\n Bist du auf feature/newsletter?"]
B --> C["3. origin/main finden\n Im Log-Graphen"]
C --> D["4. Rechtsklick auf\n neuesten Commit"]
D --> E["5. Merge origin/main\n into feature/newsletter"]
E --> F{Konflikte?}
F -->|Ja| G["Konflikte losen\n 3-Way-Merge-Editor"]
F -->|Nein| H["Fertig!\n Merge-Commit erstellt"]
G --> H
style A fill:#e1f5fe,color:#000000
style E fill:#fff3e0,color:#000000
style H fill:#e8f5e9,color:#000000
Methode B: Über das Branch-Menü
- Klicke unten rechts auf den aktuellen Branch-Namen (z.B.
feature/newsletter) - Im Dropdown: Finde „origin/main" unter „Remote Branches"
- Klicke darauf und wähle „Merge into Current"
Methode C: Über das Hauptmenü
- Menü: Git → Merge...
- Wähle
origin/mainaus der Liste - Klicke Merge
Schritt 4: Konflikte lösen (falls vorhanden)
Wenn es Konflikte gibt, öffnet PhpStorm automatisch ein Dialogfenster „Files Merged with Conflicts":
- Klicke auf „Merge..." neben der konfliktbehafteten Datei
- Der 3-Way-Merge-Editor öffnet sich:
- Links: Deine Version (feature/newsletter)
- Rechts: Ihre Version (main)
- Mitte: Das Ergebnis, das du zusammenbaust
- Nutze die
>>und<<Buttons, um Änderungen zu übernehmen - Wenn du fertig bist: Apply
- Nachdem alle Konflikte gelöst sind: Der Merge-Commit wird automatisch erstellt
Schritt 5: Ergebnis überprüfen
Nach dem Merge solltest du kurz prüfen:
- Schau ins Git-Log: Du siehst jetzt einen Merge-Commit mit zwei Eltern-Commits
- Führe deine Tests aus: Stelle sicher, dass alles noch funktioniert
- Pushe deinen Branch:
Ctrl+Shift+Koder Git → Push
Terminal-Befehle (zur Referenz)
# 1. Auf Feature-Branch wechseln (falls nicht schon dort)
git checkout feature/newsletter
# 2. Neueste Daten vom Remote holen
git fetch origin
# 3. main in Feature-Branch mergen
git merge origin/main
# 4. Bei Konflikten: Nach dem manuellen Lösen
git add .
git commit # Merge-Commit abschließen
# 5. Pushen
git push origin feature/newsletter
Ausführliche Anleitung: Synchronisation mit Rebase
Wichtiger Hinweis vorab ⚠️
Rebase schreibt die Historie um – deine Commits bekommen neue SHA-Hashes. Das bedeutet:
- Wenn du deinen Branch noch nicht gepusht hast: Kein Problem, rebase frei.
- Wenn du schon gepusht hast und alleine am Branch arbeitest: Möglich, aber du brauchst
git push --force-with-lease. - Wenn andere auch an deinem Branch arbeiten: 🚫 Kein Rebase! Du würdest die Arbeit der anderen durcheinanderbringen.
Szenario
Du arbeitest an feature/checkout und möchtest deine Commits auf den aktuellen Stand von main neu aufsetzen.
Schritt 1: Vorbereitung (wie beim Merge)
- Sicherstellen, dass das Working Directory sauber ist
- Unfertige Änderungen ggf. stashen
git fetchausführen, umorigin/mainzu aktualisieren
Schritt 2: Rebase in PhpStorm starten
Methode A: Über das Git-Menü (empfohlen für Einsteiger)
- Stelle sicher, dass du auf deinem Feature-Branch bist
- Menü: Git → Rebase...
- Im Dialog „Rebase":
- „Onto": Wähle
origin/main - Die anderen Optionen kannst du auf Standard lassen
- „Onto": Wähle
- Klicke Rebase
Methode B: Über das Branch-Popup
- Klicke unten rechts auf deinen Branch-Namen
- Finde
origin/mainunter „Remote Branches" - Klicke darauf und wähle „Rebase Current onto Selected"
Methode C: Über das Git-Log
- Öffne das Git-Log
- Rechtsklick auf den neuesten Commit von
origin/main - Wähle „Rebase 'feature/checkout' onto 'origin/main'"
Schritt 3: Konflikte während des Rebases lösen
Beim Rebase werden deine Commits einzeln auf main neu angewendet. Das bedeutet: Wenn es Konflikte gibt, musst du sie für jeden betroffenen Commit einzeln lösen.
Der Ablauf bei Konflikten:
flowchart TD
A["Rebase starten"] --> B["Commit 1 anwenden"]
B --> C{Konflikt?}
C -->|Nein| D["Commit 2 anwenden"]
C -->|Ja| E["Konflikt losen"]
E --> F["Git - Rebase - Continue"]
F --> D
D --> G{Konflikt?}
G -->|Nein| H["Commit 3 anwenden"]
G -->|Ja| I["Konflikt losen"]
I --> J["Git - Rebase - Continue"]
J --> H
H --> K["Rebase abgeschlossen!"]
style A fill:#e1f5fe,color:#000000
style E fill:#ffcccc,color:#000000
style I fill:#ffcccc,color:#000000
style K fill:#e8f5e9,color:#000000
Konkret in PhpStorm:
- Bei einem Konflikt zeigt PhpStorm eine Notification: „Rebase stopped due to conflicts"
- Löse die Konflikte im 3-Way-Merge-Editor (wie beim Merge)
- Nach dem Lösen: Git → Rebase → Continue
- PhpStorm wendet den nächsten Commit an
- Wiederhole, bis alle Commits neu angewendet wurden
Wenn du abbrechen möchtest:
- Git → Rebase → Abort – Stellt den Zustand vor dem Rebase wieder her
Schritt 4: Force-Push (falls bereits gepusht)
Da Rebase neue Commits erstellt, musst du mit --force pushen, wenn der Branch bereits auf GitHub existiert.
In PhpStorm:
Ctrl+Shift+K(oder Git → Push)- PhpStorm zeigt dir, dass ein Force-Push nötig ist
- Klicke auf den kleinen Pfeil neben „Push" und wähle „Force Push"
Besser: Verwende --force-with-lease:
git push --force-with-lease origin feature/checkout
Dies ist sicherer als --force, weil es prüft, ob jemand anderes in der Zwischenzeit gepusht hat.
In PhpStorm aktivieren:
- Settings → Version Control → Git
- Aktiviere „Use --force-with-lease for force push"
Terminal-Befehle (zur Referenz)
# 1. Auf Feature-Branch wechseln
git checkout feature/checkout
# 2. Fetch
git fetch origin
# 3. Rebase starten
git rebase origin/main
# 4a. Bei Konflikten: Nach dem Lösen weitermachen
git add .
git rebase --continue
# 4b. Oder abbrechen
git rebase --abort
# 5. Force-Push (nur wenn schon gepusht)
git push --force-with-lease origin feature/checkout
Merge vs. Rebase: Direkter Vergleich
| Aspekt | Merge | Rebase |
|---|---|---|
| Historie | Bewahrt die komplette, echte Historie mit allen Verzweigungen | Erzeugt eine lineare, „saubere" Historie |
| Merge-Commits | Ja, ein Merge-Commit pro Synchronisation | Nein, keine zusätzlichen Commits |
| Commit-Hashes | Bleiben unverändert | Ändern sich (neue Commits) |
| Konfliktlösung | Alle Konflikte auf einmal | Konflikte pro Commit einzeln |
| Force-Push nötig? | Nein | Ja, wenn Branch bereits gepusht |
| Sicher bei Team-Branches? | ✅ Ja | ⚠️ Nur mit Vorsicht |
| Nachvollziehbarkeit | Sehr gut (wann wurde was integriert?) | Geschichte wird „umgeschrieben" |
| Komplexität | Einfacher | Komplexer |
Visuelle Gegenüberstellung
Nach mehreren Merge-Synchronisationen:
main: A───B───C───D───E───F───G
\ \ \
feature: X───Y───M───Z───M───W
↑ ↑
Merge-Commits
Nach Rebase (vor dem finalen Merge):
main: A───B───C───D───E───F───G
\
feature: X'──Y'──Z'──W'
↑
Neu aufgesetzte Commits
Welche Strategie solltest du wählen?
Wähle Merge, wenn...
- 👥 Mehrere Personen am selben Branch arbeiten – Rebase würde deren Arbeit zerstören
- 📜 Du eine vollständige Audit-Trail brauchst – z.B. in regulierten Umgebungen
- 🆕 Du noch unsicher mit Git bist – Merge ist verzeihender
- 🔀 Dein Team Merge bevorzugt – Konsistenz ist wichtiger als persönliche Vorliebe
Wähle Rebase, wenn...
- 👤 Du alleine am Feature-Branch arbeitest
- ✨ Du eine saubere, lineare Historie bevorzugst
- 📖 Dein Team „Rebase before Merge" als Konvention hat
- 🧹 Du deine Commits vor dem PR aufräumen möchtest (Interactive Rebase)
Der Kompromiss: „Rebase lokal, Merge für Integration"
Viele Teams nutzen eine hybride Strategie:
- Während der Feature-Entwicklung: Rebase nutzen, um den Feature-Branch aktuell zu halten (solange nicht gepusht oder alleine am Branch)
- Für den finalen Merge: Einen normalen Merge (oder Squash-Merge) in
mainmachen
flowchart LR
A["Feature-Branch\n erstellen"] --> B["Entwickeln"]
B --> C{"Neue Commits\n in main?"}
C -->|Ja| D["Rebase auf main\n lokal"]
D --> B
C -->|Nein| E{"Feature\n fertig?"}
E -->|Nein| B
E -->|Ja| F["Pull Request\n erstellen"]
F --> G["Code Review"]
G --> H["Merge PR\n in main"]
style D fill:#fff3e0,color:#000000
style H fill:#e8f5e9,color:#000000
Praktische Tipps für die tägliche Arbeit 💡
Tipp 1: Synchronisiere regelmäßig
Mache es dir zur Gewohnheit, mindestens einmal täglich (oder bei jedem PR, der in main gemergt wird) zu synchronisieren:
# Schneller Check am Morgen
git fetch origin
git log HEAD..origin/main --oneline
# Zeigt dir, wie viele Commits du "hinterher" bist
In PhpStorm siehst du das im Git-Log: Wenn origin/main weiter ist als der Verzweigungspunkt deines Branches, ist es Zeit zu synchronisieren.
Tipp 2: Nutze PhpStorms „Update Project"
Der schnellste Weg für den täglichen Workflow:
Ctrl+T(oder Git → Update Project)- Wähle Rebase oder Merge (du kannst eine Standardeinstellung setzen)
- PhpStorm führt Fetch + Merge/Rebase in einem Schritt aus
Tipp 3: Konfiguriere deine Standard-Strategie
In PhpStorm:
- Settings → Version Control → Git
- Unter „Update Method": Wähle deine bevorzugte Methode
Global in Git:
# Für Rebase als Standard
git config --global pull.rebase true
# Für Merge als Standard (default)
git config --global pull.rebase false
Tipp 4: Erstelle einen Alias für den Workflow
Wenn du häufig synchronisierst, erstelle einen Git-Alias:
# In ~/.gitconfig oder via Befehl:
git config --global alias.sync-main '!git fetch origin && git rebase origin/main'
# Nutzung:
git sync-main
Tipp 5: Kommuniziere mit deinem Team
Die wichtigste „Technik" ist eigentlich keine technische: Sprich mit deinem Team!
- Einigt euch auf eine Strategie (Merge oder Rebase)
- Dokumentiert sie in der
CONTRIBUTING.md - Nutzt Branch Protection Rules, um die Strategie durchzusetzen
Zusammenfassung 📋
Long-Running Branches sind permanente Branches wie main und develop, die als Integrationspunkte dienen und niemals gelöscht werden.
Regelmäßige Synchronisation ist essentiell, um:
- Merge-Konflikte klein zu halten
- Gegen aktuellen Code zu entwickeln
- Integrationsprobleme früh zu erkennen
Zwei Strategien stehen zur Verfügung:
-
Merge (
git merge origin/main):- Erzeugt Merge-Commits
- Bewahrt die echte Historie
- Sicher für Team-Branches
-
Rebase (
git rebase origin/main):- Erzeugt lineare Historie
- Ändert Commit-Hashes (Force-Push nötig)
- Nur für Branches, an denen du alleine arbeitest
Die beste Strategie ist die, auf die sich dein Team einigt und die konsistent angewendet wird.
Der Hotfix-Workflow: Kritische Bugfixes sicher in alle Branches bringen 🚨
Ein Hotfix ist eine der stressigsten Situationen in der Softwareentwicklung: Ein kritischer Bug ist in der Produktion aufgetaucht und muss sofort behoben werden – ohne dabei die laufende Entwicklungsarbeit zu gefährden oder den Fix irgendwo zu „vergessen". In dieser Lektion zeige ich dir, wie du einen professionellen Hotfix-Workflow umsetzt und dabei sicherstellst, dass der Fix in allen relevanten Branches ankommt.
Das Grundproblem verstehen
Stell dir folgendes Szenario vor:
main (v1.2.0) ──●──●──● ← Produktionsversion, hier ist der Bug!
\
develop ─────────────●──●──●──● ← Hier wird an v1.3.0 gearbeitet
\
feature/neues-login ───────●──● ← Feature in Entwicklung
Der Bug existiert in main (der Produktionsversion), aber auch in develop und allen Feature-Branches, die von main oder develop abgezweigt wurden. Wenn du den Fix nur in main einspielst, wird er bei der nächsten Entwicklung wieder überschrieben. Spielst du ihn nur in develop ein, dauert es bis zum nächsten Release, bis er in Produktion kommt.
Die Lösung: Ein dedizierter Hotfix-Branch, der von main abzweigt und nach der Behebung in alle relevanten Branches gemergt wird.
Der klassische Hotfix-Workflow (Schritt für Schritt)
1. Hotfix-Branch von main erstellen
Der Hotfix-Branch wird immer von main (oder dem Branch, der die Produktion repräsentiert) erstellt – niemals von develop! So stellst du sicher, dass du exakt den Code-Stand der Produktion als Basis hast.
In PhpStorm:
- Öffne das Git-Tool-Window (
Alt+9/Cmd+9) - Stelle sicher, dass du auf
mainbist (Rechtsklick aufmain→ Checkout) - Führe einen Pull durch, um sicherzustellen, dass du den aktuellen Stand hast
- Klicke auf New Branch (oder
Ctrl+Shift+` /Cmd+Shift+`) - Benenne den Branch nach dem Schema:
hotfix/kurze-beschreibungoderhotfix/v1.2.1
Auf der Kommandozeile:
# Sicherstellen, dass main aktuell ist
git checkout main
git pull origin main
# Hotfix-Branch erstellen
git checkout -b hotfix/kritischer-login-bug
Wichtige Namenskonventionen:
hotfix/beschreibung– z.B.hotfix/sql-injection-fixhotfix/vX.Y.Z– z.B.hotfix/v1.2.1(wenn du semantische Versionierung nutzt)hotfix/issue-123– wenn du ein Issue-Tracking-System verwendest
2. Den Bug beheben und committen
Jetzt behebst du den Bug. Dabei gelten einige wichtige Regeln:
Nur den Bug fixen – nichts anderes! Ein Hotfix sollte so minimal wie möglich sein. Jede zusätzliche Änderung erhöht das Risiko, neue Probleme einzuführen. Widerstehe der Versuchung, „schnell noch" andere kleine Dinge zu korrigieren.
Commit-Message mit Kontext:
git add src/Auth/LoginController.php
git commit -m "fix: SQL-Injection-Schwachstelle im Login behoben
- Prepared Statements statt String-Konkatenation
- Betrifft Login und Passwort-Reset
- Fixes #247"
In PhpStorm:
- Mache deine Änderungen im Code
- Öffne das Commit-Tool-Window (
Ctrl+K/Cmd+K) - Wähle nur die relevanten Dateien aus
- Schreibe eine aussagekräftige Commit-Message
- Nutze optional Amend wenn du nachbessern musst
3. Hotfix testen
Bevor du den Hotfix irgendwohin mergst, teste ihn gründlich:
- Führe deine automatisierten Tests aus
- Teste den spezifischen Fix manuell
- Wenn möglich, deploye auf eine Staging-Umgebung
In PhpStorm kannst du Tests direkt ausführen:
- Rechtsklick auf den Test-Ordner → Run Tests
- Oder nutze die Run-Konfiguration für PHPUnit
4. Hotfix in main mergen und taggen
Jetzt kommt der kritische Teil: Der Fix muss zurück nach main, damit er in die Produktion deployed werden kann.
In PhpStorm:
- Wechsle zu
main(Rechtsklick → Checkout) - Rechtsklick auf deinen Hotfix-Branch → Merge into Current
- PhpStorm zeigt dir den Merge-Dialog – überprüfe die Änderungen
- Bestätige den Merge
Auf der Kommandozeile:
# Zu main wechseln
git checkout main
# Hotfix mergen (mit --no-ff für einen expliziten Merge-Commit)
git merge --no-ff hotfix/kritischer-login-bug -m "Merge hotfix/kritischer-login-bug: SQL-Injection behoben"
Warum --no-ff? Die Option --no-ff (no fast-forward) erzwingt einen Merge-Commit, auch wenn ein Fast-Forward möglich wäre. Das hat zwei Vorteile:
- Der Hotfix bleibt in der Historie als eigenständiger „Block" erkennbar
- Du hast einen klaren Merge-Commit, der dokumentiert, wann der Hotfix eingespielt wurde
Version-Tag erstellen:
Nach einem Hotfix solltest du einen neuen Version-Tag erstellen:
# Tag erstellen
git tag -a v1.2.1 -m "Hotfix: SQL-Injection-Schwachstelle behoben"
# Tag auf GitHub pushen
git push origin v1.2.1
In PhpStorm:
- Menü: Git → New Tag
- Oder im Git-Log: Rechtsklick auf den Commit → New Tag
5. Hotfix in develop mergen ⚠️
Das ist der Schritt, der am häufigsten vergessen wird! Wenn du den Hotfix nicht auch in develop mergst, wird der Bug beim nächsten Release wieder auftauchen, weil develop dann ohne den Fix nach main gemergt wird.
In PhpStorm:
- Wechsle zu
develop(Rechtsklick → Checkout) - Rechtsklick auf
main(oder den Hotfix-Branch) → Merge into Current - Löse eventuelle Konflikte im Merge-Tool
Auf der Kommandozeile:
git checkout develop
git pull origin develop # Sicherstellen, dass develop aktuell ist
git merge --no-ff hotfix/kritischer-login-bug -m "Merge hotfix in develop: SQL-Injection behoben"
Oder alternativ – den Tag mergen:
git checkout develop
git merge --no-ff v1.2.1 -m "Merge v1.2.1 hotfix in develop"
6. Alles pushen
Jetzt müssen alle Änderungen auf GitHub landen:
# main mit dem Fix pushen
git push origin main
# develop mit dem Fix pushen
git push origin develop
# Tags pushen (falls noch nicht geschehen)
git push origin --tags
In PhpStorm:
Ctrl+Shift+K/Cmd+Shift+Köffnet den Push-Dialog- Stelle sicher, dass Push Tags aktiviert ist
7. Hotfix-Branch aufräumen
Nach erfolgreichem Merge in beide Branches kann der Hotfix-Branch gelöscht werden:
Lokal löschen:
git branch -d hotfix/kritischer-login-bug
Auf GitHub löschen:
git push origin --delete hotfix/kritischer-login-bug
In PhpStorm:
- Im Git-Tool-Window: Rechtsklick auf den Branch → Delete
- Häkchen setzen bei Delete Tracking Branch um ihn auch remote zu löschen
Visualisierung des Workflows
flowchart TD
subgraph Ausgangssituation
A["main\n v1.2.0 - Bug vorhanden"]
B["develop\n Aktive Entwicklung"]
A -.->|"abgezweigt"| B
end
subgraph Hotfix-Prozess
C["1. Hotfix-Branch erstellen\n von main"]
D["2. Bug beheben\n und committen"]
E["3. Testen"]
F["4. Merge in main\n Tag: v1.2.1"]
G["5. Merge in develop"]
H["6. Push und Cleanup"]
end
A --> C
C --> D
D --> E
E --> F
F --> G
G --> H
style C fill:#ffcccc,color:#000000
style F fill:#ccffcc,color:#000000
style G fill:#ccffcc,color:#000000
Die Commit-Historie nach dem Hotfix:
main: ──●──●──●─────────────●── (v1.2.1)
\ /
hotfix: ●──●──●────┘
\
develop: ──●──●──●──●────●── (enthält den Fix)
Umgang mit Konflikten beim Merge in develop
Es ist sehr wahrscheinlich, dass beim Merge des Hotfixes in develop Konflikte auftreten – schließlich wurde in develop weiterentwickelt, während der Hotfix auf dem älteren main-Stand basiert.
Typisches Konflikt-Szenario:
Der Hotfix hat eine Funktion in LoginController.php geändert, aber in develop wurde dieselbe Funktion für ein neues Feature erweitert.
Konfliktlösung in PhpStorm:
- Nach dem Merge-Versuch zeigt PhpStorm die konfligierenden Dateien an
- Doppelklick auf eine Datei öffnet den 3-Way-Merge-Editor
- Du siehst drei Spalten:
- Links (Yours): Der aktuelle Stand von
develop - Rechts (Theirs): Der Hotfix
- Mitte (Result): Das gewünschte Ergebnis
- Links (Yours): Der aktuelle Stand von
- Übernimm die Sicherheitsänderungen aus dem Hotfix
- Behalte die neuen Features aus
develop - Stelle sicher, dass beides zusammen funktioniert
Wichtig: Nach dem Lösen von Konflikten immer testen! Ein schlecht gelöster Konflikt kann den Fix unwirksam machen.
Was ist mit Feature-Branches?
Wenn zum Zeitpunkt des Hotfixes Feature-Branches existieren, die von develop (oder main) abgezweigt wurden, enthalten auch diese den Bug. Hier gibt es mehrere Strategien:
Option A: Feature-Branches auf develop rebasen
Nachdem der Hotfix in develop gemergt wurde, können Feature-Branch-Entwickler ihren Branch auf den neuen develop-Stand rebasen:
git checkout feature/neues-login
git fetch origin
git rebase origin/develop
Vorteil: Der Feature-Branch enthält automatisch den Fix.
Nachteil: Erfordert einen Force-Push, wenn der Branch bereits gepusht wurde.
Option B: develop in Feature-Branches mergen
Alternativ können die Feature-Branch-Entwickler develop in ihren Branch mergen:
git checkout feature/neues-login
git fetch origin
git merge origin/develop
Vorteil: Kein Force-Push nötig.
Nachteil: Zusätzliche Merge-Commits.
Option C: Nichts tun und beim PR lösen
Wenn der Feature-Branch ohnehin bald gemergt wird, kann der Konflikt auch beim finalen Merge/PR gelöst werden. Der Hotfix landet dann automatisch im Feature-Branch, sobald dieser nach develop gemergt wurde.
Empfehlung: Für sicherheitskritische Hotfixes ist Option A oder B besser, damit alle aktiv entwickelten Branches sofort geschützt sind.
Der Hotfix-Workflow mit Pull Requests
In professionellen Teams wird der Hotfix-Workflow oft mit Pull Requests kombiniert, um Code Reviews auch für kritische Fixes sicherzustellen:
Workflow mit PRs
-
Hotfix-Branch erstellen und pushen:
git checkout -b hotfix/kritischer-bug main # Fix implementieren git push -u origin hotfix/kritischer-bug -
Ersten PR erstellen: Hotfix → main
- Auf GitHub: New Pull Request
- Base:
main, Compare:hotfix/kritischer-bug - Als Critical oder Urgent markieren
- Reviewer zuweisen (idealerweise jemand, der sofort verfügbar ist)
-
Review und Merge in main
- Schnelles, fokussiertes Review
- Nach Approval: Merge (nicht Squash, damit der Commit-Hash erhalten bleibt)
- Tag erstellen
-
Zweiten PR erstellen: main → develop (oder Hotfix → develop)
- Base:
develop, Compare:main - Dieser PR dokumentiert, dass der Hotfix auch in
developübernommen wurde
- Base:
Automatisierung mit GitHub Actions
Du kannst einen GitHub Actions Workflow erstellen, der automatisch einen PR von main nach develop erstellt, sobald ein Hotfix-Tag gepusht wird:
name: Create Hotfix Backport PR
on:
push:
tags:
- 'v*.*.*' # Triggert bei Version-Tags
jobs:
create-backport-pr:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Create Pull Request to develop
uses: peter-evans/create-pull-request@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: backport/${{ github.ref_name }}-to-develop
base: develop
title: "Backport ${{ github.ref_name }} to develop"
body: |
Automatischer Backport-PR für Hotfix ${{ github.ref_name }}.
**Bitte prüfen und mergen, damit der Fix in develop landet!**
labels: hotfix, backport
Checkliste für Hotfixes ✅
Hier ist eine praktische Checkliste, die du bei jedem Hotfix abarbeiten solltest:
Vor dem Hotfix
- Bug ist verifiziert und reproduzierbar
- Priorität ist klar (ist es wirklich ein kritischer Hotfix?)
- Alle Stakeholder sind informiert
Während des Hotfixes
- Hotfix-Branch von
mainerstellt (nicht vondevelop!) - Nur den Bug behoben, keine anderen Änderungen
- Aussagekräftige Commit-Message mit Issue-Referenz
- Tests geschrieben oder angepasst
- Alle Tests laufen durch
Nach dem Hotfix
- In
maingemergt - Version-Tag erstellt
- In
developgemergt ← Wird am häufigsten vergessen! - Alle Branches gepusht
- Tag gepusht
- Hotfix-Branch gelöscht (lokal und remote)
- Deployment in Produktion durchgeführt
- Fix in Produktion verifiziert
- Team informiert (welche Feature-Branches sollten aktualisiert werden?)
Häufige Fehler und wie du sie vermeidest
Fehler 1: Hotfix von develop statt main erstellen
Problem: Du erstellst den Hotfix von develop, das bereits neue, ungetestete Features enthält. Wenn du jetzt nach main mergst, landen diese Features ungewollt in der Produktion.
Lösung: Immer zuerst git checkout main und dann den Hotfix-Branch erstellen.
Fehler 2: Vergessen, den Hotfix in develop zu mergen
Problem: Der Fix ist in Produktion, aber bei der nächsten großen Release-Integration wird der Bug wieder eingeführt, weil develop den Fix nicht enthält.
Lösung: Nutze die Checkliste oder automatisiere den Backport mit GitHub Actions.
Fehler 3: Zu viele Änderungen im Hotfix
Problem: Du nutzt den Hotfix als Gelegenheit, „schnell noch" andere Dinge zu fixen. Der Hotfix wird groß und schwer zu reviewen, das Risiko für neue Bugs steigt.
Lösung: Disziplin! Andere Fixes kommen in reguläre Feature-Branches. Ein Hotfix ist nur für den kritischen Bug.
Fehler 4: Keinen Tag erstellen
Problem: Ohne Tag ist später schwer nachzuvollziehen, welcher exakte Code-Stand in Produktion deployed wurde.
Lösung: Immer einen semantischen Version-Tag erstellen (v1.2.1 für einen Hotfix auf v1.2.0).
Hotfixes in PhpStorm – Die wichtigsten Shortcuts
| Aktion | Windows/Linux | macOS |
|---|---|---|
| Git-Tool-Window öffnen | Alt+9 |
Cmd+9 |
| Neuen Branch erstellen | Ctrl+Shift+`` |
Cmd+Shift+`` |
| Commit-Dialog öffnen | Ctrl+K |
Cmd+K |
| Push-Dialog öffnen | Ctrl+Shift+K |
Cmd+Shift+K |
| Pull (Update Project) | Ctrl+T |
Cmd+T |
| Branches anzeigen | Ctrl+Shift+`` |
Cmd+Shift+`` |
| Git-Log anzeigen | Alt+9, dann Tab Log |
Cmd+9, dann Tab Log |
Zusammenfassung
Der Hotfix-Workflow folgt einem klaren Muster:
- Branch von
main– Nicht vondevelop! - Minimal fixen – Nur den Bug, nichts anderes
- Testen – Automatisiert und manuell
- Merge in
main– Mit--no-ffund Version-Tag - Merge in
develop– Der kritische Schritt, der oft vergessen wird - Aufräumen – Branch löschen, Team informieren
Mit diesem Workflow stellst du sicher, dass kritische Bugfixes schnell in die Produktion gelangen und in allen Entwicklungszweigen ankommen, sodass der Bug nicht bei der nächsten Release-Integration wieder auftaucht.
Protected Branches auf GitHub – Dein Sicherheitsnetz für kritische Branches
Protected Branches sind eines der wichtigsten Features, um die Integrität deines Codes zu schützen. Sie verhindern, dass versehentlich (oder absichtlich) schädliche Änderungen direkt in wichtige Branches wie main oder develop gelangen. Stell dir Protected Branches als Türsteher vor, die genau prüfen, wer unter welchen Bedingungen Änderungen einbringen darf.
Was sind Protected Branches? 🛡️
Ein Protected Branch ist ein Branch, für den du auf GitHub spezielle Regeln definierst, die eingehalten werden müssen, bevor Änderungen akzeptiert werden. Ohne Schutzregeln kann jeder mit Schreibrechten direkt auf main pushen – ein einziger falscher Befehl wie git push --force origin main könnte die gesamte Projekthistorie zerstören.
Mit Protected Branches kannst du unter anderem festlegen:
- Wer überhaupt auf den Branch pushen darf
- Ob direkte Pushes erlaubt sind oder nur über Pull Requests
- Welche Prüfungen (Tests, Reviews) bestanden sein müssen
- Ob die Branch-Historie überschrieben werden darf (Force Push)
Warum sind sie so wichtig?
| Szenario ohne Schutz | Mögliche Konsequenz |
|---|---|
Entwickler pusht ungetesteten Code direkt auf main |
Produktionssystem fällt aus |
Jemand führt versehentlich git push --force aus |
Commit-Historie geht verloren |
| Merge ohne Code Review | Bugs und Sicherheitslücken gelangen unbemerkt in die Produktion |
| Commits von unverifizierten Accounts | Supply-Chain-Angriffe werden möglich |
Die wichtigsten Schutzregeln im Detail
GitHub bietet eine Vielzahl von Schutzregeln, die du kombinieren kannst. Hier sind die wichtigsten mit Erklärungen und Empfehlungen:
1. „Require a pull request before merging"
Diese Regel ist das Herzstück der Branch Protection. Sie verhindert, dass irgendjemand direkt auf den geschützten Branch pushen kann. Alle Änderungen müssen über einen Pull Request laufen.
Unteroptionen:
-
„Require approvals" – Legt fest, wie viele Personen den PR genehmigen müssen, bevor er gemerged werden kann. Du kannst 1 bis 6 erforderliche Approvals einstellen.
Empfehlung: Für kleine Teams (1–3 Entwickler) reicht 1 Approval. Für größere Teams oder kritische Projekte empfehle ich 2 Approvals.
-
„Dismiss stale pull request approvals when new commits are pushed" – Wenn diese Option aktiviert ist, werden bestehende Approvals ungültig, sobald neue Commits zum PR hinzugefügt werden. Das ist wichtig, weil ein Reviewer vielleicht Code genehmigt hat, der danach noch verändert wurde.
Empfehlung: Unbedingt aktivieren! Sonst könnte jemand nach dem Approval noch problematischen Code hinzufügen.
-
„Require review from Code Owners" – Wenn dein Repository eine
CODEOWNERS-Datei hat, müssen die dort definierten Verantwortlichen den PR genehmigen. Mehr dazu später. -
„Require approval of the most recent reviewable push" – Verhindert, dass der Autor des letzten Commits seinen eigenen Code genehmigt (relevant, wenn Maintainer selbst zum PR beitragen).
2. „Require status checks to pass before merging"
Diese Regel stellt sicher, dass automatisierte Prüfungen (wie Tests oder Linting) erfolgreich durchlaufen müssen, bevor ein Merge möglich ist. Das ist dein Qualitätstor.
Unteroptionen:
-
„Require branches to be up to date before merging" – Der Feature-Branch muss auf dem aktuellen Stand des Ziel-Branches sein. Das verhindert, dass Code gemerged wird, der zwar mit einer alten Version von
mainfunktioniert, aber mit den neuesten Änderungen kollidieren könnte.Empfehlung: Aktivieren, auch wenn es manchmal nervig ist, den Branch aktualisieren zu müssen. Es verhindert das „aber auf meinem Branch hat es funktioniert"-Problem.
-
Status Checks auswählen – Du wählst aus, welche Checks bestanden sein müssen. Diese erscheinen erst in der Liste, nachdem sie mindestens einmal gelaufen sind (z.B. durch einen GitHub Actions Workflow).
# Beispiel: Workflow, der als Required Check verwendet werden kann
name: Tests
on: [push, pull_request]
jobs:
phpunit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run PHPUnit
run: vendor/bin/phpunit
3. „Require conversation resolution before merging"
Wenn Reviewer Kommentare oder Änderungswünsche hinterlassen, müssen diese als „resolved" markiert werden, bevor der PR gemerged werden kann. Das stellt sicher, dass kein Feedback ignoriert wird.
Empfehlung: Aktivieren. Es ist frustrierend als Reviewer, wenn deine Anmerkungen einfach übergangen werden.
4. „Require signed commits"
Mit dieser Regel müssen alle Commits kryptografisch signiert sein (GPG, SSH oder S/MIME). Signierte Commits zeigen auf GitHub ein grünes „Verified"-Badge und beweisen, dass der Commit wirklich von der angegebenen Person stammt.
Empfehlung: Für Open-Source-Projekte oder sicherheitskritische Anwendungen empfohlen. Für kleine, interne Projekte optional. Die Einrichtung erfordert etwas Aufwand (siehe Kapitel 10 des Kurses).
5. „Require linear history"
Diese Regel erzwingt eine lineare Commit-Historie, indem sie nur Squash-Merges oder Rebase-Merges erlaubt – keine regulären Merge-Commits mit zwei Parents.
Empfehlung: Geschmackssache. Eine lineare Historie ist übersichtlicher, aber manche Teams bevorzugen explizite Merge-Commits, weil sie zeigen, wann welcher Feature-Branch integriert wurde. Ich empfehle es für Projekte, die eine saubere, leicht lesbare Historie priorisieren.
6. „Do not allow bypassing the above settings"
Normalerweise können Repository-Administratoren alle Schutzregeln umgehen. Mit dieser Option wird auch Admins das Umgehen verboten.
Empfehlung: Für die meisten Projekte nicht aktivieren, da Admins manchmal legitime Gründe haben, Regeln zu umgehen (z.B. bei Notfall-Hotfixes). Bei sehr kritischen Projekten oder aus Compliance-Gründen kann es sinnvoll sein.
7. „Restrict who can push to matching branches"
Du kannst explizit definieren, welche Personen, Teams oder Apps überhaupt auf den Branch pushen dürfen (selbst über PRs). Das ist nützlich, wenn du z.B. nur bestimmten Maintainern erlauben möchtest, PRs zu mergen.
8. „Allow force pushes" und „Allow deletions"
Diese Optionen sind standardmäßig deaktiviert bei Protected Branches – und das sollte auch so bleiben!
- Force Pushes können die gesamte Commit-Historie überschreiben
- Deletions würden erlauben, den Branch komplett zu löschen
Empfehlung: Niemals aktivieren für main oder andere kritische Branches.
Schritt-für-Schritt-Anleitung: Branch Protection einrichten
So richtest du die Schutzregeln für deinen main-Branch ein:
Empfohlene Konfiguration für verschiedene Szenarien
🧑💻 Solo-Entwickler (persönliches Projekt)
Auch als Solo-Entwickler können Protected Branches sinnvoll sein – sie schützen dich vor dir selbst!
| Regel | Empfehlung |
|---|---|
| Require pull request | ❌ Optional (kann Workflow verlangsamen) |
| Require status checks | ✅ Aktivieren, wenn du CI/CD hast |
| Require signed commits | ❌ Optional |
| Allow force pushes | ❌ Deaktiviert lassen |
| Allow deletions | ❌ Deaktiviert lassen |
Als Minimalkonfiguration empfehle ich: Keine Force Pushes, keine Deletions, Status Checks falls vorhanden.
👥 Kleines Team (2–5 Entwickler)
| Regel | Empfehlung |
|---|---|
| Require pull request | ✅ Aktivieren |
| Require approvals | ✅ 1 Approval |
| Dismiss stale approvals | ✅ Aktivieren |
| Require status checks | ✅ Aktivieren |
| Require branch up to date | ✅ Aktivieren |
| Require conversation resolution | ✅ Aktivieren |
| Allow force pushes | ❌ Deaktiviert |
| Allow deletions | ❌ Deaktiviert |
🏢 Größeres Team oder Open-Source-Projekt
| Regel | Empfehlung |
|---|---|
| Require pull request | ✅ Aktivieren |
| Require approvals | ✅ 2 Approvals |
| Dismiss stale approvals | ✅ Aktivieren |
| Require review from Code Owners | ✅ Aktivieren |
| Require status checks | ✅ Aktivieren (mehrere Checks) |
| Require branch up to date | ✅ Aktivieren |
| Require conversation resolution | ✅ Aktivieren |
| Require signed commits | ✅ Aktivieren |
| Require linear history | ⚠️ Team-Entscheidung |
| Do not allow bypassing | ⚠️ Bei hohen Compliance-Anforderungen |
CODEOWNERS – Automatische Review-Zuweisung 📋
Die CODEOWNERS-Datei ist ein mächtiges Feature, das perfekt mit Protected Branches zusammenarbeitet. Du definierst darin, wer für welche Teile des Codes verantwortlich ist. Diese Personen werden automatisch als Reviewer zu PRs hinzugefügt, die „ihre" Dateien betreffen.
So erstellst du eine CODEOWNERS-Datei
Die Datei muss an einem dieser Orte liegen:
- Repository-Root:
CODEOWNERS .github/CODEOWNERS(empfohlen)docs/CODEOWNERS
Syntax und Beispiele
# Dies ist ein Kommentar
# Standard-Owner für alles, was nicht anders definiert ist
* @standard-reviewer
# Bestimmte Dateien/Ordner einem Team zuweisen
/src/api/ @backend-team
/src/frontend/ @frontend-team
# Bestimmte Dateitypen
*.js @javascript-expert
*.css @design-team
# Kritische Konfigurationsdateien
/config/ @team-lead @senior-developer
.github/workflows/ @devops-team
# Bestimmte Dateien mehreren Reviewern zuweisen (alle müssen reviewen)
/security/ @security-team @team-lead
CODEOWNERS in Kombination mit Branch Protection
Wenn du in den Branch Protection Rules „Require review from Code Owners" aktivierst, müssen die definierten Code Owners den PR genehmigen. Das stellt sicher, dass z.B.:
- Änderungen an der Datenbank-Schicht immer vom DBA geprüft werden
- Sicherheitskritischer Code vom Security-Team abgesegnet wird
- Frontend-Änderungen vom Frontend-Lead reviewed werden
Rulesets – Die moderne Alternative 🆕
GitHub hat 2023 Rulesets eingeführt, eine modernere und flexiblere Alternative zu den klassischen Branch Protection Rules. Rulesets bieten einige Vorteile:
- Auf Organisations-Ebene definierbar – Eine Regel für alle Repositories
- Bessere Wildcards – Komplexere Muster möglich
- Tag Protection – Nicht nur Branches, auch Tags schützen
- Bypass-Listen – Feingranulare Kontrolle, wer Regeln umgehen darf
Rulesets vs. klassische Branch Protection
| Feature | Branch Protection | Rulesets |
|---|---|---|
| Repository-spezifisch | ✅ | ✅ |
| Organisations-weit | ❌ | ✅ |
| Tag Protection | ❌ | ✅ |
| Bypass-Ausnahmen | Eingeschränkt | Flexibel |
| API-Steuerung | ✅ | ✅ |
Empfehlung: Für einzelne Repositories sind die klassischen Branch Protection Rules einfacher. Für Organisationen mit vielen Repositories lohnt es sich, Rulesets zu evaluieren.
Visualisierung: Typischer PR-Workflow mit Branch Protection
flowchart TD
A["Entwickler erstellt\nFeature-Branch"] --> B["Entwickler pusht\nCommits"]
B --> C["Pull Request\nwird erstellt"]
C --> D{"Status Checks\nerfolgreich?"}
D -->|Nein| E["Entwickler fixt\nProbleme"]
E --> B
D -->|Ja| F{"Code Review\nbestanden?"}
F -->|Nein| G["Entwickler arbeitet\nFeedback ein"]
G --> B
F -->|Ja| H{"Branch\naktuell?"}
H -->|Nein| I["Branch mit main\naktualisieren"]
I --> D
H -->|Ja| J{"Alle Conversations\nresolved?"}
J -->|Nein| K["Offene Diskussionen\nklaeren"]
K --> J
J -->|Ja| L["Merge in main\nmoeglich"]
L --> M["Feature-Branch\nloeschen"]
style A fill:#e1f5fe,color:#000000
style L fill:#c8e6c9,color:#000000
style M fill:#c8e6c9,color:#000000
style E fill:#ffcdd2,color:#000000
style G fill:#ffcdd2,color:#000000
Häufige Fragen und Probleme 🤔
„Ich bin Admin, kann aber nicht pushen – warum?"
Wenn „Do not allow bypassing the above settings" aktiviert ist, gelten die Regeln auch für dich. Du musst dann ebenfalls einen PR erstellen.
„Mein Status Check taucht nicht in der Auswahlliste auf"
Status Checks erscheinen erst, nachdem sie mindestens einmal gelaufen sind. Erstelle einen Test-PR oder pushe auf einen Testbranch, um den Workflow auszulösen.
„Wie kann ich in einem Notfall trotzdem direkt pushen?"
Wenn du Admin bist und „Do not allow bypassing" nicht aktiviert ist, kannst du die Regeln umgehen. Alternativ:
- Die Regel temporär deaktivieren
- Den Notfall-Push machen
- Die Regel sofort wieder aktivieren
- Dokumentieren, warum das nötig war
Besser: Auch Notfall-Hotfixes über PRs, aber mit minimaler Review-Zeit und einem speziellen hotfix-Label.
„Wie schütze ich mehrere Branches gleichzeitig?"
Nutze Wildcards im Branch-Pattern:
release/*– Schützt alle Release-Branchesmainunddevelop– Erstelle zwei separate Regeln- Mit Rulesets kannst du auch
mainunddevelopin einer Regel kombinieren
Checkliste für deine Branch Protection 📝
Nutze diese Checkliste, um deine main-Branch-Protection einzurichten:
- Branch Protection Rule für
mainerstellt - „Require pull request before merging" aktiviert
- Anzahl der erforderlichen Approvals festgelegt
- „Dismiss stale approvals" aktiviert
- Status Checks definiert (Tests, Linting)
- „Require branch to be up to date" aktiviert
- „Require conversation resolution" aktiviert
- Force Pushes deaktiviert (Standard)
- Branch-Deletion deaktiviert (Standard)
- CODEOWNERS-Datei erstellt (optional)
- Team über die neuen Regeln informiert
Fazit
Protected Branches sind kein bürokratisches Hindernis, sondern ein essentielles Werkzeug für professionelle Softwareentwicklung. Sie schützen nicht nur vor böswilligen Änderungen, sondern vor allem vor Versehen und menschlichen Fehlern. Die initiale Einrichtung dauert nur wenige Minuten, kann aber stundenlange Debugging-Sessions oder sogar Datenverlust verhindern.
Selbst als Solo-Entwickler empfehle ich dir, zumindest Force Pushes und Deletions für main zu verbieten. Sobald du im Team arbeitest, sollten Pull Requests mit mindestens einem Review zur Pflicht werden. Die Zeit, die du in Reviews „verlierst", gewinnst du mehrfach zurück durch früh entdeckte Bugs und bessere Code-Qualität.