Feature Branch Workflow
Der einfachste Ansatz — jedes Feature bekommt einen eigenen Branch. main bleibt immer deploybar.
$ git switch -c feature/login # … arbeiten, committen … $ git switch main $ git merge --no-ff feature/login $ git branch -d feature/login $ git push origin main
Git Flow (klassisch)
Strukturiert mit main, develop, release/, hotfix/ und feature/ Branches.
# Feature starten $ git switch -c feature/xyz develop # Release vorbereiten $ git switch -c release/1.2.0 develop # Hotfix $ git switch -c hotfix/crash main $ git merge hotfix/crash # → main + develop # Release mergen + taggen $ git merge release/1.2.0 $ git tag -a v1.2.0 -m "Release 1.2.0"
Trunk-Based Development
Alle committen direkt auf main — Feature Flags statt Long-Lived Branches. Bevorzugt von Google, Meta, Shopify.
# Sehr kurze Feature-Branches $ git switch -c feat/btn # max. 1-2 Tage, dann merge # Feature Flags im Code # if (featureFlags.newUI) { … } # Direkt auf main mit CI $ git push origin main # → CI läuft, Deploy wenn grün
Merge vs. Rebase
Merge bewahrt die komplette History — ein Merge-Commit dokumentiert wann Branches zusammengeführt wurden. Rebase schreibt die History um — linearer, sauberer Log, aber Commits bekommen neue Hashes.
# Merge: bewahrt History, extra Commit $ git switch main $ git merge feature/xyz # → Merge commit: "Merge branch 'feature/xyz'" # Rebase: linearer Log, neue Hashes $ git switch feature/xyz $ git rebase main $ git switch main $ git merge feature/xyz # Fast-Forward # Goldene Regel: # Niemals public/shared Branches rebases!
Interaktiver Rebase — Commits aufräumen
Vor dem Merge: History squashen, Messages verbessern, Commits neu ordnen.
# Letzten 4 Commits bearbeiten $ git rebase -i HEAD~4 # Im Editor erscheint: # pick abc1234 WIP # pick def5678 fix typo # pick ghi9012 more fixes # pick jkl3456 feat: login done # → ändern zu: # pick abc1234 WIP # f def5678 fix typo (fixup) # f ghi9012 more fixes (fixup) # r jkl3456 feat: login done (reword) # Ergebnis: 1 sauberer Commit $ git push --force-with-lease origin feature/login
Lightweight vs. Annotiert
Ein Lightweight Tag ist nur ein Zeiger auf einen Commit — kein eigenes Objekt. Ein Annotierter Tag ist ein vollständiges Git-Objekt mit Autor, Datum, Message und optional GPG-Signatur.
# Lightweight — nur ein Pointer $ git tag v1.0.0 # Annotiert — eigenes Objekt, empfohlen! $ git tag -a v1.0.0 -m "Release v1.0.0" # Details vergleichen: $ git show v1.0.0 # Annotiert zeigt: Tagger, Datum, Message # Lightweight: direkt der Commit # Signiert (GPG-Key nötig) $ git tag -s v1.0.0 -m "Signed release" $ git tag -v v1.0.0 # verifizieren
Semantic Versioning mit Tags
Tags folgen typischerweise SemVer: v{MAJOR}.{MINOR}.{PATCH}. Pre-release und Build-Metadata sind auch möglich.
# Stabile Releases v1.0.0 → Initial Release v1.1.0 → Neues Feature (backward compat.) v1.1.1 → Bugfix v2.0.0 → Breaking Change # Pre-releases v2.0.0-alpha.1 v2.0.0-beta.3 v2.0.0-rc.1 # Letzte Version ermitteln $ git describe --tags --abbrev=0 # v1.1.1 # Commits seit letztem Tag $ git describe --tags # v1.1.1-14-gabcd123
Tags als CI/CD Trigger
Das Pushen eines Tags ist das Signal, dass eine Version release-ready ist. Die meisten CI/CD-Systeme können auf Tag-Pushes reagieren und so automatisch Pipelines, Deployments oder GitHub Releases auslösen — komplett separat vom normalen Branch-Push.
# GitHub Actions — on tag push # .github/workflows/release.yml on: push: tags: - 'v*' # v1.0.0, v2.3.1 … # GitLab CI — only on tags deploy: only: - tags # Tag erstellen + pushen → löst Pipeline aus $ git tag -a v1.2.0 -m "Release v1.2.0" $ git push origin v1.2.0 # → CI baut, testet, deployed automatisch
git push gepusht — immer explizit git push origin <tagname> oder git push --tags verwenden. Annotierte Tags sind für Releases vorzuziehen, da sie Metadaten und einen sauberen Changelog ermöglichen.
Nützliche Aliases (.gitconfig)
[alias] # Übersichtlicher Log-Graph lg = log --oneline --graph --all \ --decorate --color # Letzten Commit rückgängig (soft) undo = reset --soft HEAD~1 # Aktuellen Branch + Status st = status -sb # Alle Branches nach letztem Commit recent = branch --sort=-committerdate -v # Staged Diff anzeigen staged = diff --staged # Leeren Commit (Pipeline triggern) trigger = commit --allow-empty -m "chore: trigger CI" # Alle gemergten Branches löschen cleanup = "!git branch --merged | \ grep -v 'main\\|master\\|develop' | \ xargs git branch -d"
Release-Workflow (End-to-End)
# 1. Feature fertig, History aufräumen $ git rebase -i HEAD~5 $ git push --force-with-lease # 2. Nach PR-Merge: main updaten $ git switch main $ git pull --rebase # 3. Changelog aus Commits generieren $ git log v1.1.0..HEAD --oneline # 4. Tag erstellen und pushen $ git tag -a v1.2.0 -m "$(cat CHANGELOG.md)" $ git push origin v1.2.0 # → GitHub Actions Release-Pipeline startet # → Docker Image wird gebaut & gepusht # → Deployment auf Production # 5. GitHub Release aus Tag (CLI) $ gh release create v1.2.0 \ --title "v1.2.0" \ --notes-file CHANGELOG.md