15 Tags: Meilensteine und Release-Marker

Tags sind die ortstreu Gegenstücke zu Branches – dauerhafte Markierungen spezifischer Punkte in der Git-Historie. Während Branches die Entwicklung steuern und sich mit jedem Commit bewegen, markieren Tags fertiggestellte Zustände: Releases, Meilensteine, wichtige Versionen. Sie sind das primäre Werkzeug für Release-Management und Production-Steuerung.

Technisch ist ein Tag entweder eine einfache Referenz (Lightweight Tag) oder ein vollwertiges Git-Objekt (Annotated Tag) – der vierte und letzte Objekttyp neben Blob, Tree und Commit. Diese Unterscheidung hat praktische und prozessuale Implikationen, die wir im Detail betrachten.

15.1 Tags als Git-Objekte: Der vierte Objekttyp

Wir haben bereits Blobs (Dateiinhalte), Trees (Verzeichnisstrukturen) und Commits (Snapshots mit Metadaten) kennengelernt. Tags sind der vierte Objekttyp im Git-Objektmodell.

15.1.1 Lightweight Tags: Die einfache Referenz

Ein Lightweight Tag ist technisch identisch zu einem Branch – eine Textdatei unter .git/refs/tags/, die einen Commit-Hash enthält:

$ git tag v1.0.0
$ cat .git/refs/tags/v1.0.0
7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6

# Genau wie eine Branch-Datei
$ cat .git/refs/heads/main
7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6

Der Unterschied liegt im Verhalten: Der Branch bewegt sich, der Tag bleibt. Technisch sind beide nur Pointer, aber semantisch ist der Tag immutabel.

Lightweight Tags sind schnell und einfach, haben aber keine zusätzlichen Metadaten. Sie sind Bookmarks, mehr nicht.

15.1.2 Annotated Tags: Das vollwertige Objekt

Ein Annotated Tag ist ein eigenständiges Git-Objekt mit eigener Struktur:

$ git tag -a v1.0.0 -m "Release 1.0.0: Initial stable version"

$ cat .git/refs/tags/v1.0.0
9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c3b2a1f0

# Dieser Hash identifiziert das Tag-Objekt, nicht direkt den Commit
$ git cat-file -t 9f8e7d6c
tag

$ git cat-file -p 9f8e7d6c
object 7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6
type commit
tag v1.0.0
tagger John Doe <john@example.com> 1704124800 +0100

Release 1.0.0: Initial stable version

This release includes:
- User authentication system
- REST API v1
- Basic admin panel
- Comprehensive test suite

Die Struktur eines Tag-Objekts:

object: Der Hash des Objekts, auf das der Tag zeigt (typischerweise ein Commit)

type: Der Typ des referenzierten Objekts (meist commit, kann auch blob oder tree sein)

tag: Der Name des Tags

tagger: Wer hat den Tag erstellt, wann, und in welcher Zeitzone

Message: Die Tag-Message (nach Leerzeile) – kann beliebig lang sein

Das Tag-Objekt ist eine indirekte Referenz: .git/refs/tags/v1.0.0 zeigt auf das Tag-Objekt, das wiederum auf den Commit zeigt. Diese Indirektion ermöglicht die zusätzlichen Metadaten.

15.1.3 Lightweight vs. Annotated: Wann welcher?

Eigenschaft Lightweight Annotated
Git-Objekt Nein (nur Referenz) Ja (eigenes Objekt)
Metadaten Keine Tagger, Date, Message
Nutzung Private Bookmarks Öffentliche Releases
Verifizierbar Nein Ja (GPG-Signatur möglich)
Best Practice Temporäre Marker Alle Production-Releases

Regel: Lightweight für persönliche, temporäre Marker. Annotated für alles, was geteilt oder dokumentiert werden sollte.

15.1.4 GPG-signierte Tags: Kryptographische Verifikation

Annotated Tags können GPG-signiert werden:

$ git tag -s v1.0.0 -m "Release 1.0.0"

$ git cat-file -p v1.0.0
object 7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6
type commit
tag v1.0.0
tagger John Doe <john@example.com> 1704124800 +0100

Release 1.0.0
-----BEGIN PGP SIGNATURE-----
iQIzBAABCAAdFiEE...
-----END PGP SIGNATURE-----

Die Signatur garantiert: 1. Der Tag wurde vom angegebenen Tagger erstellt 2. Der Tag wurde nicht manipuliert 3. Der Tag zeigt auf den verifizierten Commit

Für Security-kritische Releases oder regulierte Umgebungen ist dies essentiell. Ein Angreifer kann einen Branch manipulieren, aber nicht einen signierten Tag (ohne den privaten Schlüssel).

15.2 Tags als Steuerungsmechanismus: Development vs. Production

Branches und Tags haben komplementäre Rollen im Entwicklungsprozess:

15.2.1 Branches: Die Steuerung der Entwicklung

Branches organisieren aktive Entwicklung: - main: Aktuelle stabile Version - develop: Integration neuer Features - feature/*: Isolierte Feature-Entwicklung - hotfix/*: Dringende Fixes

Branches sind mobil – sie bewegen sich mit der Entwicklung. Sie repräsentieren den Prozess, nicht das Ergebnis.

15.2.2 Tags: Die Steuerung der Production

Tags markieren abgeschlossene Zustände: - v1.0.0: Erstes Production-Release - v1.1.0: Feature-Update - v1.1.1: Bugfix-Release - rc-1.2.0: Release Candidate

Tags sind immutabel – sie markieren fixe Punkte. Sie repräsentieren das Ergebnis, nicht den Prozess.

15.2.3 Die Arbeitsteilung

Während der Entwicklung: Branches steuern. Features werden in feature/* entwickelt, in develop integriert, in main gemerged.

Beim Release: Tag setzen. Wenn main release-ready ist: git tag -a v1.0.0. Dieser Tag triggert: - Production-Deployment - Release-Notes-Generierung - Artefakt-Publikation (Docker-Images, Binaries, Packages) - Changelog-Update - Benachrichtigungen an Stakeholder

Nach dem Release: Der Tag bleibt. Branches entwickeln sich weiter, aber v1.0.0 zeigt immer auf exakt diesen Commit. Rollbacks, Hotfixes, Analyse – alles referenziert den Tag.

Analogy: Branches sind wie Baustellen (aktive Arbeit), Tags sind wie fertige Gebäude (fixe Artefakte).

15.2.4 Praktisches Beispiel: Release-Workflow

# Entwicklung auf develop
git checkout develop
git merge feature/new-payment-system
git push origin develop

# Wenn stabil: Prepare Release
git checkout main
git merge develop

# Intensive Testing, QA, Staging-Deployment
# ... alles OK ...

# Release-Tag setzen
git tag -a v1.2.0 -m "Release 1.2.0: Payment System

Features:
- Credit card payment integration
- PayPal support
- Invoice generation

Closes: #234, #245, #256"

git push origin v1.2.0

# Tag triggert CI/CD Pipeline:
# → Build Docker Image mit Tag v1.2.0
# → Run Full Test Suite
# → Deploy to Production
# → Create GitHub Release
# → Publish to Package Registry
# → Send Notifications

Der Tag ist der offizielle Release-Trigger. Nicht ein Branch-Merge, nicht ein Commit – der Tag signalisiert: “Diese Version ist production-ready.”

15.3 Semantic Versioning: Die Sprache der Versionen

Semantic Versioning (SemVer) ist der de-facto Standard für Software-Versionierung. Er kodiert Kompatibilitäts-Informationen in der Versionsnummer.

15.3.1 Das Format: MAJOR.MINOR.PATCH

v2.3.4
│ │ │
│ │ └─ PATCH: Bugfixes, rückwärtskompatibel
│ └─── MINOR: Neue Features, rückwärtskompatibel
└───── MAJOR: Breaking Changes, nicht rückwärtskompatibel

MAJOR: Inkompatible API-Änderungen - Entfernte Funktionen - Geänderte Funktionssignaturen - Breaking Changes im Verhalten

MINOR: Neue Funktionalität, kompatibel - Neue Endpoints, Methoden, Features - Deprecated Warnings für zukünftige Breaking Changes - Performance-Improvements

PATCH: Bugfixes, kompatibel - Fehlerbehebungen - Security-Patches - Kleine Verbesserungen

15.3.2 Zusätzliche Labels

Pre-Release: v1.2.0-alpha.1, v1.2.0-beta.2, v1.2.0-rc.3 - alpha: Frühe Version, instabil, nur für Entwickler - beta: Feature-complete, testing, für Early Adopters - rc (Release Candidate): Potenzielle finale Version

Build-Metadata: v1.2.0+20240101.abc123 - Nach +: Metadaten, haben keine Precedence-Semantik - Typischerweise: Build-Datum, Commit-Hash, Build-Nummer

Vollständige Version: v2.3.4-beta.2+20240101.7e8f9a0

15.3.3 SemVer-Regeln im Detail

Version 0.y.z: Initiale Entwicklung. Alles kann sich jederzeit ändern. Keine Stabilitätsgarantien.

Version 1.0.0: Erste stabile Public API. Ab hier gelten Kompatibilitätsregeln.

Patch: 1.2.3 → 1.2.4

# Nur Bugfixes, keine neuen Features
git tag -a v1.2.4 -m "Fix: Correct password validation regex"

Minor: 1.2.4 → 1.3.0

# Neue Features, alte funktionieren weiter
git tag -a v1.3.0 -m "Feature: Add OAuth2 authentication"

Major: 1.3.0 → 2.0.0

# Breaking Changes
git tag -a v2.0.0 -m "Breaking: Remove deprecated v1 API endpoints

BREAKING CHANGES:
- /api/v1/* endpoints removed
- Use /api/v2/* instead
- See migration guide: docs/v2-migration.md"

15.3.4 SemVer und Dependency Management

SemVer ist essentiell für Package-Manager (npm, pip, cargo, Maven):

npm package.json:

{
  "dependencies": {
    "express": "^4.18.0",   // ^: Minor und Patch updates OK
    "lodash": "~4.17.21",   // ~: Nur Patch updates OK
    "react": "18.2.0"       // Exakte Version
  }
}

^4.18.0 bedeutet: “≥4.18.0 und <5.0.0” – alle Minor/Patch updates sind safe, weil kompatibel (laut SemVer-Versprechen).

Ohne SemVer wäre automatisches Dependency-Management unmöglich. SemVer ist der Vertrag zwischen Library-Autoren und Nutzern.

15.3.5 Prä-1.0-Versionen

Vor 1.0.0 gelten spezielle Regeln: - 0.y.z: Alles instabil, keine Garantien - 0.1.0 → 0.2.0: Kann Breaking Changes enthalten - 0.2.3 → 0.2.4: Sollte kompatibel sein (aber nicht garantiert)

Viele Projekte bleiben jahrelang in 0.x.x, um sich Flexibilität zu bewahren. Das ist legitim, signalisiert aber: “API noch nicht stabil.”

15.4 Tag-Messages: Dokumentation und Kontext

Die Message eines Annotated Tags ist wertvolle Dokumentation. Sie sollte mindestens enthalten:

15.4.1 Release-Notes im Tag

git tag -a v1.5.0 -m "Release 1.5.0: Performance and Security Update

Features:
- Redis caching for API responses (3x faster)
- Rate limiting per API key
- Batch processing for bulk operations

Improvements:
- Database query optimization (40% reduction)
- Frontend bundle size reduced by 200KB

Security:
- Fix XSS vulnerability in comment system (CVE-2024-1234)
- Update dependencies with known vulnerabilities

Breaking Changes:
- None

Migration:
- No action required for existing deployments
- New rate limits apply: 1000 req/hour/key (previously unlimited)

Closes: #345, #356, #367, #389
"

Diese Tag-Message ist: - Selbstdokumentierend: Man versteht sofort, was in dieser Version ist - Referenzierbar: Issues, CVEs sind verlinkt - Actionable: Migration-Schritte sind dokumentiert - Archiviert: Bleibt permanent mit dem Tag verbunden

15.4.2 Tag-Message als Changelog-Quelle

Viele Tools generieren Changelogs aus Tag-Messages:

# Alle Tags mit Messages anzeigen
git tag -n9

# Releases zwischen zwei Tags
git log v1.4.0..v1.5.0 --oneline

# Tag-Message für Changelog extrahieren
git tag -l -n99 v1.5.0

GitHub, GitLab generieren automatisch Release-Pages aus Tag-Messages. Die Message wird zur öffentlichen Dokumentation.

15.5 Conventional Commits: Semantic Messages für Commits

Während SemVer Versionen semantisch macht, macht Conventional Commits Commit-Messages semantisch. Die beiden Standards ergänzen sich.

15.5.1 Das Format

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

Types (standardisiert): - feat: Neues Feature (→ MINOR bump) - fix: Bugfix (→ PATCH bump) - docs: Nur Dokumentation - style: Code-Formatierung (keine Logic-Änderung) - refactor: Code-Umstrukturierung (kein neues Feature, kein Fix) - perf: Performance-Improvement - test: Tests hinzufügen/ändern - build: Build-System-Änderungen - ci: CI-Konfiguration - chore: Maintenance-Tasks

Breaking Change: BREAKING CHANGE: im Footer oder ! nach Type:

feat!: remove deprecated API endpoints

BREAKING CHANGE: All /api/v1/* endpoints are removed.
Use /api/v2/* instead.

15.5.2 Beispiele

Feature (Minor):

feat(auth): add OAuth2 authentication

Implements OAuth2 flow for Google and GitHub providers.
Users can now login using their existing accounts.

Closes: #234

Bugfix (Patch):

fix(api): correct validation error in user registration

Email validation was too restrictive, rejecting valid addresses
with plus signs. Updated regex to RFC 5322 compliance.

Fixes: #456

Breaking Change (Major):

feat(api)!: migrate to REST API v2

BREAKING CHANGE: The v1 API has been removed.

All endpoints have moved from /api/v1/* to /api/v2/*.
Response format has changed from XML to JSON.

See migration guide: docs/v2-migration.md

Chore (No Version Bump):

chore(deps): update dependencies

- express: 4.17.1 → 4.18.2
- lodash: 4.17.20 → 4.17.21

No API changes.

15.5.3 Automatische Versioning

Tools wie semantic-release oder standard-version lesen Commit-Messages und bestimmen automatisch die nächste Version:

# Commits seit letztem Tag:
feat: add payment system        # → MINOR bump
fix: correct email validation   # → PATCH bump
docs: update README             # → no bump

# Nächste Version: 1.2.0 → 1.3.0 (wegen feat)

Ein BREAKING CHANGE in irgendeinem Commit → MAJOR bump.

15.5.4 Automatische Changelog-Generierung

Conventional Commits ermöglichen strukturierte Changelogs:

# Changelog

## v1.3.0 (2024-01-15)

### Features
- **auth**: add OAuth2 authentication (#234)
- **api**: add bulk import endpoint (#245)

### Bug Fixes
- **api**: correct validation error in registration (#456)
- **ui**: fix date picker timezone handling (#467)

### Documentation
- update API documentation for v2 endpoints

Diese Struktur ist automatisch aus Commit-Messages generiert. Keine manuelle Changelog-Pflege.

15.5.5 Best Practices

Commit häufig mit klaren Messages:

git commit -m "feat(api): add user search endpoint"
git commit -m "test(api): add tests for user search"
git commit -m "docs(api): document user search endpoint"

Scope für Kontext: Hilft zu verstehen, welcher Teil betroffen ist: - feat(auth), fix(payment), refactor(database)

Imperative Mood: “add feature”, nicht “added feature” oder “adds feature”

Body für Details: Wenn der Commit komplex ist, nutze den Body für Erklärungen

Footer für Referenzen: Closes: #123, Fixes: #456, Refs: #789

15.6 Tags in CI/CD: Automatisierung und Deployment

Tags sind der primäre Trigger für Production-Deployments in modernen CI/CD-Systemen.

15.6.1 GitLab CI: Tag-basierte Pipelines

stages:
  - build
  - test
  - deploy

# Läuft auf allen Commits
build:
  stage: build
  script:
    - npm run build

test:
  stage: test
  script:
    - npm test

# Läuft nur bei Tags
deploy_production:
  stage: deploy
  script:
    - docker build -t myapp:${CI_COMMIT_TAG} .
    - docker push myapp:${CI_COMMIT_TAG}
    - kubectl set image deployment/myapp myapp=myapp:${CI_COMMIT_TAG}
  only:
    - tags
  except:
    - branches

# Nur bei Release-Tags (nicht Prereleases)
publish_artifacts:
  stage: deploy
  script:
    - npm publish
  only:
    - /^v[0-9]+\.[0-9]+\.[0-9]+$/  # Regex: v1.2.3 format

Environment-Variable: CI_COMMIT_TAG enthält den Tag-Namen (v1.2.0)

15.6.2 GitHub Actions: Release-Workflow

name: Release

on:
  push:
    tags:
      - 'v*'  # Trigger bei allen Tags beginnend mit 'v'

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - name: Build
        run: npm run build
      
      - name: Create Release
        uses: actions/create-release@v1
        with:
          tag_name: ${{ github.ref_name }}
          release_name: Release ${{ github.ref_name }}
          body: |
            See CHANGELOG.md for details
          draft: false
          prerelease: false

15.6.3 Deployment-Strategien mit Tags

Blue-Green Deployment:

deploy_blue:
  script:
    - deploy.sh blue ${CI_COMMIT_TAG}
  only:
    - tags
  when: manual

deploy_green:
  script:
    - deploy.sh green ${CI_COMMIT_TAG}
  only:
    - tags
  when: manual

switch_traffic:
  script:
    - switch.sh ${ENVIRONMENT}
  only:
    - tags
  when: manual

Canary Deployment:

deploy_canary:
  script:
    - deploy.sh canary ${CI_COMMIT_TAG} --traffic=5%
  only:
    - tags

promote_canary:
  script:
    - deploy.sh production ${CI_COMMIT_TAG} --traffic=100%
  only:
    - tags
  when: manual
  needs: [deploy_canary]

15.6.4 Tag-basiertes Rollback

Tags ermöglichen einfache Rollbacks:

# Aktuell deployed: v1.5.0
# Problem entdeckt
# Rollback zu vorheriger stabiler Version

kubectl set image deployment/myapp myapp=myapp:v1.4.0

# Oder: Hotfix-Tag auf alten Commit setzen
git tag -a v1.5.1 v1.4.0^{commit} -m "Rollback: Revert to stable 1.4.0"
git push origin v1.5.1

# Pipeline deployed v1.5.1 (identisch zu v1.4.0)

15.7 Tag-Management: Best Practices

15.7.1 Naming Conventions

Konsistent: Wähle ein Schema und bleibe dabei - ✓ v1.0.0, v1.1.0, v2.0.0 - ✗ 1.0.0, v1.1.0, version-2.0.0

Präfix: v ist Standard-Konvention (obwohl optional)

Pre-Releases: Klare Labels - v1.2.0-alpha.1, v1.2.0-beta.1, v1.2.0-rc.1

Build-Metadata: Nach + - v1.2.0+20240101.abc123

15.7.2 Tag-Hygiene

Keine Lightweight Tags für Releases: Immer Annotated

# ✗ Schlecht
git tag v1.0.0

# ✓ Gut
git tag -a v1.0.0 -m "Release 1.0.0: Initial stable release"

Signierte Tags für kritische Releases:

git tag -s v1.0.0 -m "Release 1.0.0"

Tags dokumentieren:

# Tag-Message sollte Release-Notes enthalten
git tag -a v1.2.0 -F release-notes.md

Tags pushen:

# Einzelnen Tag
git push origin v1.0.0

# Alle Tags
git push origin --tags

# Nur Annotated Tags (sichere)
git push origin --follow-tags

15.7.3 Tag-Korrektur (mit Vorsicht)

Tags sollten immutabel sein, aber Fehler passieren:

# Lokalen Tag löschen
git tag -d v1.0.0

# Remote Tag löschen
git push origin :refs/tags/v1.0.0

# Neuen Tag erstellen
git tag -a v1.0.0 <correct-commit> -m "Release 1.0.0"
git push origin v1.0.0

Warnung: Nur bei Tags tun, die noch nicht veröffentlicht/deployed sind. Veröffentlichte Tags zu ändern, bricht Trust und Reproduzierbarkeit.

15.7.4 Tag-basierte Versionierung Tools

Git Describe: Generiere Versions-String aus Tags

$ git describe
v1.2.0-5-g7e8f9a0
#      │ │
#      │ └─ Short commit hash
#      └─── 5 commits seit v1.2.0

$ git describe --tags --always
v1.2.0-5-g7e8f9a0

# In Build-Scripts nutzen
VERSION=$(git describe --tags --always)
docker build -t myapp:${VERSION} .

Semantic-Release: Automatisches Versioning

# Analysiert Commits seit letztem Tag
# Bestimmt nächste Version (MAJOR/MINOR/PATCH)
# Erstellt Tag und Release
npx semantic-release

15.8 Zusammenfassung: Tags als Fundament des Release-Management

Tags sind mehr als Bookmarks – sie sind das fundamentale Werkzeug für Release-Management und Production-Steuerung:

Technisch: Entweder simple Referenzen (Lightweight) oder vollwertige Git-Objekte (Annotated) mit Metadaten und optionaler Signatur.

Prozessual: Der Trigger für Production-Deployments, die Dokumentation von Releases, die Referenz für Rollbacks.

Semantisch: Durch SemVer kodieren Tags Kompatibilitäts-Information. Durch Conventional Commits werden Tags automatisch generierbar.

Immutabel: Während Branches die Bewegung der Entwicklung sind, sind Tags die Fixpunkte der Historie.

Die Kombination aus: - Branches für Entwicklungs-Steuerung - Tags für Release-Steuerung - SemVer für Versions-Semantik - Conventional Commits für Message-Semantik - CI/CD für Automatisierung

… bildet ein robustes, skalierbares Release-Management-System.

Ein gut getaggtes Repository erzählt seine Release-Geschichte. Jeder Tag ist ein Kapitel: Was wurde released, wann, warum, von wem. Diese Geschichte ist wertvoll – für Debugging, für Compliance, für Verständnis.

Nutze Annotated Tags. Schreibe aussagekräftige Messages. Folge SemVer. Nutze Conventional Commits. Automatisiere mit CI/CD. Deine Future Self (und deine Kollegen, und deine Nutzer) werden es dir danken.

Tags sind nicht optional oder “nice to have”. Sie sind fundamental für professionelles Software-Engineering. Master sie, und Release-Management wird von chaotisch zu systematisch.