Git fuer IT-Admins: Workflows, Branching und Merge-Konflikte
Git-Grundkonzepte, Branching-Modelle, Konfigs versionieren und Merge-Konflikte in YAML/JSON sauber loesen – fuer den IT-Alltag im KMU.
Warum Git auch fuer Admins Pflicht ist
Git gilt vielen als “Entwickler-Werkzeug” – dabei landet es in jedem KMU frueher oder later auch auf dem Schreibtisch der IT: Ansible-Playbooks, Terraform-State-Konfigurationen, GPO-Backups, Netzwerk-Configs von Firewalls, PowerShell-Skript-Sammlungen, Dokumentation als Markdown. Sobald mehr als eine Person an denselben Dateien arbeitet oder du nachvollziehen willst, wer wann was geaendert hat, brauchst du Versionierung. Und die beste, am weitesten verbreitete Loesung dafuer ist Git.
Der Unterschied zum Entwickler-Alltag: Du versionierst selten Anwendungscode, sondern Konfigurationsdateien, Skripte und Infrastructure-as-Code (siehe Infrastructure-as-Code im Werkzeug-Vergleich). Das bedeutet andere Schwerpunkte – weniger komplexe Feature-Branches mit hundert Commits, dafuer mehr Fokus auf Nachvollziehbarkeit, saubere Konfliktloesung in strukturierten Dateien (YAML, JSON, INI) und striktes Vermeiden von Secrets im Repository.
Dieser Artikel geht davon aus, dass du git clone, git add, git commit und git push schon mal getippt hast. Wir steigen direkt in Branching-Strategien, Merge-Konflikte und Praxis-Workflows fuer Infrastruktur-Teams ein.
Kurzer Grundlagen-Refresher
Drei Konzepte, die du fuer den Rest des Artikels sicher haben solltest:
- Commit: ein unveraenderlicher Schnappschuss des gesamten Repository-Zustands zu einem Zeitpunkt, identifiziert durch einen SHA-1-Hash (z. B.
a3f5c9e). Jeder Commit verweist auf seinen/seine Elternteil(e) – daraus ergibt sich der Verlauf als gerichteter azyklischer Graph (DAG). - Branch: nichts weiter als ein beweglicher Zeiger auf einen Commit.
mainist kein besonderer Branch-Typ, sondern nur ein ueblicher Name. Das macht Branches in Git extrem billig – Erstellen kostet Millisekunden, kein Kopieren von Dateien. - Remote: ein Verweis auf ein anderes Repository (z. B.
originauf deinem Gitea-Server). Dein lokales Repo und das Remote synchronisieren sich explizit ueberfetch,pullundpush– nichts passiert automatisch im Hintergrund.
# Status und Verlauf schnell im Blick
git status
git log --oneline --graph --all -n 20
# Ein Remote-Repository hinzufuegen
git remote add origin https://git.firma.ch/it/netzwerk-configs.git
# Aktuellen Stand holen, ohne zu mergen
git fetch origin
# Unterschied zwischen lokalem und Remote-Branch
git diff main origin/main
Branching-Modelle: Trunk-based vs. Git-Flow
Die Wahl der Branching-Strategie entscheidet, wie dein Team parallel arbeitet, ohne sich staendig in die Quere zu kommen. Fuer IT-Infrastruktur-Repos ist das nicht nur akademisch – falsche Strategie bedeutet in der Praxis: veraltete Configs landen versehentlich in Produktion, oder Aenderungen haengen wochenlang in nie gemergten Branches fest.
Trunk-based Development
Ein einziger langlebiger Branch (main, historisch auch trunk), auf den alle direkt oder ueber sehr kurzlebige Feature-Branches (Lebensdauer: Stunden bis maximal 1-2 Tage) committen. Kein develop-Branch, keine Release-Branches im klassischen Sinn.
# Trunk-based: kurzer Feature-Branch, schnell zurueck in main
git switch -c fix/dhcp-scope-erweitern
# ... Aenderung an dhcp-config.yaml ...
git add dhcp-config.yaml
git commit -m "DHCP-Scope VLAN20 auf /23 erweitert (Ticket #4521)"
git switch main
git pull origin main
git merge fix/dhcp-scope-erweitern
git push origin main
git branch -d fix/dhcp-scope-erweitern
Vorteile: wenig Merge-Aufwand, standig integrierte Aenderungen, passt hervorragend zu CI/CD-Pipelines, die bei jedem Push auf main automatisch testen und ausrollen. Nachteil: erfordert Disziplin – kaputte Commits landen schnell direkt im Haupt-Branch, wenn niemand Code-Reviews via Pull-Request erzwingt.
Git-Flow
Das klassische Modell von Vincent Driessen (2010): zwei langlebige Branches (main fuer Produktivstand, develop fuer laufende Integration) plus temporaere feature/*-, release/*- und hotfix/*-Branches.
# Git-Flow: Feature von develop abzweigen
git switch develop
git switch -c feature/vpn-split-tunneling
# ... Arbeit ...
git switch develop
git merge --no-ff feature/vpn-split-tunneling
# Release vorbereiten
git switch -c release/2026.3 develop
# ... letzte Fixes, Versionsnummer hochziehen ...
git switch main
git merge --no-ff release/2026.3
git tag -a v2026.3 -m "Release 2026.3"
git switch develop
git merge --no-ff release/2026.3
# Hotfix direkt ab main
git switch -c hotfix/cert-expiry main
# ... Fix ...
git switch main
git merge --no-ff hotfix/cert-expiry
git switch develop
git merge --no-ff hotfix/cert-expiry
Vorteile: klare Trennung zwischen “was laeuft produktiv” und “was ist in Arbeit”, saubere Release-Historie mit Tags. Nachteil: mehr administrativer Aufwand, mehr Merge-Punkte, mehr Potenzial fuer divergierende Branches, die auseinanderlaufen.
Entscheidungstabelle
| Kriterium | Trunk-based | Git-Flow |
|---|---|---|
| Team-Groesse | 1-8 Personen, hohe Vertrautheit | groessere Teams, mehrere parallele Release-Linien |
| Release-Rhythmus | kontinuierlich (mehrmals taeglich moeglich) | geplante Releases, Versionsnummern |
| CI/CD-Reife | hoch – automatisierte Tests zwingend | optional, oft manuelle Freigaben |
| Typischer Anwendungsfall im KMU-IT-Kontext | Ansible-Playbooks, die staendig gegen Live-Inventar laufen | Firewall-/Netzwerk-Configs mit geplanten Wartungsfenstern |
| Rollback-Komplexitaet | einfach (revert des letzten Commits) | mittel (Hotfix-Branch noetig) |
| Lernkurve | niedrig | mittel bis hoch |
Ein dritter Weg: GitHub Flow (leichtgewichtig)
Fuer viele Infrastruktur-Repos ist GitHub Flow der pragmatische Mittelweg: ein main-Branch, jede Aenderung ueber einen Feature-Branch und einen Pull-Request, nach Review und (wo vorhanden) automatischem Test direkt gemergt. Kein develop, keine Release-Branches – aber im Gegensatz zu reinem Trunk-based ist der PR mit Review verpflichtend.
main ──●──●──●──●──●──●──●──►
\ / \ /
feature/x feature/y
Merge-Konflikte in YAML/JSON-Configs loesen
Konflikte in strukturierten Konfigurationsdateien sind der Alltag im Infrastruktur-Kontext – und leider genau die Dateiformate, bei denen ein falsch geloester Konflikt (kaputte Einrueckung, doppelter Key) erst beim naechsten ansible-playbook-Lauf oder kubectl apply auffaellt.
Wie ein Konflikt entsteht und aussieht
Ein Konflikt tritt auf, wenn zwei Branches dieselben Zeilen unterschiedlich geaendert haben und Git nicht automatisch entscheiden kann, welche Version gilt. Git markiert die betroffene Stelle direkt in der Datei:
# inventory/group_vars/webserver.yaml
webserver:
vars:
<<<<<<< HEAD
nginx_worker_processes: 4
max_connections: 2048
=======
nginx_worker_processes: 8
max_connections: 4096
>>>>>>> feature/perf-tuning
timeout: 30
Die Markierungen bedeuten:
- Alles zwischen
<<<<<<< HEADund=======ist der Stand deines aktuellen Branches. - Alles zwischen
=======und>>>>>>> feature/perf-tuningist der Stand des Branches, den du gerade mergst. - Zeilen ausserhalb der Marker sind unveraendert und identisch in beiden Versionen.
Schritt-fuer-Schritt-Ablauf
- Konflikt erkennen:
git statuszeigt “both modified” fuer betroffene Dateien. - Datei oeffnen und die Konfliktmarker verstehen – nicht blind eine Seite loeschen, sondern inhaltlich pruefen, was fachlich richtig ist (hier z. B.: reicht
nginx_worker_processes: 8fuer die Hardware, oder war4bewusst wegen begrenztem RAM gesetzt?). - Entscheiden und bereinigen: die gewuenschte(n) Zeile(n) behalten, alle drei Marker-Zeilen (
<<<<<<<,=======,>>>>>>>) vollstaendig entfernen. - YAML/JSON-Syntax validieren, bevor du committest (siehe unten).
- Staging:
git add inventory/group_vars/webserver.yaml. - Merge abschliessen:
git commit(bei einem laufenden Merge reichtgit commitohne-m, Git generiert eine Merge-Commit-Message).
# Konflikt-Status pruefen
git status
# Alle Dateien mit Konflikten auflisten
git diff --name-only --diff-filter=U
# YAML nach der Bereinigung validieren
python -c "import yaml, sys; yaml.safe_load(open('inventory/group_vars/webserver.yaml'))" && echo "YAML OK"
# JSON validieren
python -m json.tool config/appsettings.json > $null
# Merge abschliessen
git add inventory/group_vars/webserver.yaml
git commit
Merge-Strategien und Tools, die helfen
# diff3-Konfliktstil aktiviert zusaetzlich den gemeinsamen Vorfahren
git config --global merge.conflictstyle diff3
# Ergebnis zeigt zusaetzlich einen |||||||-Block mit dem Original vor beiden Aenderungen
# Externes Merge-Tool oeffnen (z. B. VS Code)
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait $MERGED'
git mergetool
# Bei mehreren gleichartigen Konflikten: eine Seite komplett bevorzugen
git checkout --ours config/netzwerk.yaml # eigene Version behalten
git checkout --theirs config/netzwerk.yaml # eingehende Version behalten
git add config/netzwerk.yaml
Rebase statt Merge – wann das sinnvoller ist
git rebase schreibt deine lokalen Commits auf den aktuellen Stand von main um, statt einen zusaetzlichen Merge-Commit zu erzeugen. Das ergibt eine lineare, leichter lesbare Historie – kostet aber, dass du denselben Konflikt unter Umstaenden mehrfach loesen musst, einmal pro betroffenem Commit.
git switch feature/dns-umzug
git rebase main
# Bei Konflikt: Datei bereinigen, dann
git add dns-records.yaml
git rebase --continue
# Bei Bedarf komplett abbrechen und zuruecksetzen:
git rebase --abort
Git fuer Infrastruktur-Configs: Praxis-Setup
Ein sauberes Infra-Repo unterscheidet sich von einem App-Code-Repo vor allem durch Sensitivitaet der Inhalte und durch die Kopplung an reale Systeme. Ein paar bewaehrte Muster:
Repo-Struktur fuer Config-Verwaltung
netzwerk-configs/
├── .gitignore
├── .pre-commit-config.yaml
├── ansible/
│ ├── inventory/
│ │ ├── production.yaml
│ │ └── group_vars/
│ ├── playbooks/
│ └── roles/
├── terraform/
│ ├── environments/
│ │ ├── prod/
│ │ └── test/
│ └── modules/
├── configs/
│ ├── firewall/
│ │ └── fortigate-branch01.conf
│ └── switches/
│ └── core-switch-01.yaml
└── README.md
Wichtig: environmentspezifische Werte (Prod vs. Test) klar trennen, entweder per Verzeichnis oder per Branch – nie per unkommentiertem Kommentarblock in derselben Datei, den man beim Deploy “schnell mal” umschaltet.
Commit-Konventionen fuer Nachvollziehbarkeit
Bei Infra-Configs ist die Commit-Message oft die einzige Doku dazu, warum etwas geaendert wurde. Ein Format wie Conventional Commits hilft, spaeter automatisiert Changelogs zu bauen oder gezielt zu suchen:
git commit -m "fix(dhcp): Scope VLAN20 auf /23 erweitert (Ticket #4521)"
git commit -m "feat(vpn): Split-Tunneling fuer Aussendienst aktiviert"
git commit -m "chore(ansible): inventory auf neue Filialserver erweitert"
git commit -m "docs(firewall): Regelwerk-Kommentare ergaenzt fuer Audit"
Aenderungen nachvollziehen: blame, bisect, tag
# Wer hat wann welche Zeile geaendert – und warum?
git blame -L 40,55 ansible/playbooks/deploy-webserver.yaml
# Welcher Commit hat einen Fehler eingefuehrt? Git sucht binaer.
git bisect start
git bisect bad # aktueller Stand ist fehlerhaft
git bisect good v2026.1 # dieser Tag/Commit war noch gut
# Git checkt automatisch die Mitte aus, du testest und markierst:
git bisect good # oder: git bisect bad
git bisect reset # Suche beenden, zurueck zum Ausgangs-Branch
# Stabile Konfig-Staende markieren – wichtig vor grossen Wartungsfenstern
git tag -a pre-migration-2026-07 -m "Stand vor Exchange-Hybrid-Migration"
git push origin pre-migration-2026-07
Secrets vermeiden: .gitignore und Pre-Commit-Hooks
Der teuerste Git-Fehler im Infrastruktur-Alltag: ein API-Key, ein Domain-Admin-Passwort oder eine .pfx-Zertifikatsdatei landet versehentlich im Commit – und damit dauerhaft in der Historie, selbst wenn die Datei im naechsten Commit wieder geloescht wird.
.gitignore richtig einsetzen
# Secrets und Zugangsdaten
*.pem
*.key
*.pfx
*.p12
secrets.yaml
.env
.env.*
!*.env.example
# Terraform State (enthaelt oft sensible Werte im Klartext!)
*.tfstate
*.tfstate.backup
.terraform/
*.tfvars
!*.tfvars.example
# Ansible Vault-Passwortdateien
.vault_pass
vault_password.txt
# Editor- und OS-Muell
.vscode/
.idea/
Thumbs.db
.DS_Store
Terraform-State separat behandeln
Ein haeufiger Anfaengerfehler: terraform.tfstate landet im Repo, weil es “ja nur Infrastruktur-Zustand” ist. Tatsaechlich stehen dort oft Passwoerter und Zugriffsschluessel im Klartext. Details zum sauberen Umgang mit State (Remote Backend, Locking, Drift) findest du unter Terraform State Management und Drift.
Pre-Commit-Hooks: Secrets vor dem Commit abfangen
Ein Pre-Commit-Hook laeuft lokal, bevor ein Commit ueberhaupt entsteht – das Secret landet dann gar nicht erst in der Historie. Das Python-basierte Framework pre-commit mit dem Scanner gitleaks ist ein bewaehrter Standard:
# .pre-commit-config.yaml
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.21.2
hooks:
- id: gitleaks
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-yaml
- id: check-json
- id: end-of-file-fixer
- id: trailing-whitespace
- id: detect-private-key
# Installation (einmalig pro Maschine)
pip install pre-commit
pre-commit install # richtet den Git-Hook lokal ein
# Manuell gegen das ganze Repo laufen lassen (z. B. bei Erst-Einrichtung)
pre-commit run --all-files
| Tool | Staerke | Einsatzort |
|---|---|---|
| gitleaks | schnell, breite Musterabdeckung, gute CI-Integration | Pre-Commit + CI-Pipeline |
| git-secrets (AWS) | sehr leichtgewichtig, Fokus auf AWS-Pattern | Pre-Commit, wenn primaer AWS-Keys relevant sind |
| detect-secrets (Yelp) | Baseline-Datei gegen Alert-Flut bei Altbestand | grosse, gewachsene Repos |
| trufflehog | erkennt zusaetzlich, ob ein Secret noch aktiv/gueltig ist | periodischer History-Scan, Incident Response |
Self-hosted (Gitea/Forgejo) vs. GitHub/GitLab
Fuer ein Schweizer KMU ist die Wahl der Git-Plattform auch eine Frage von Datenschutz (nDSG/DSGVO, siehe DSGVO/nDSG in der IT-Praxis), Betriebsaufwand und Budget.
Die Optionen im Ueberblick
- GitHub / GitLab.com (SaaS): kein eigener Betrieb, aber Daten liegen bei einem US-Anbieter (Microsoft/GitHub bzw. GitLab Inc.) – bei sensiblen Infrastruktur-Configs ein Abwaegungspunkt.
- GitLab CE/EE self-hosted: volles DevOps-Paket (CI/CD, Container-Registry, Security-Scanning) inklusive, aber deutlich ressourcenhungriger im Betrieb – mehrere Gigabyte RAM, PostgreSQL, Redis, mehrere Hintergrunddienste.
- Gitea: leichtgewichtige, in Go geschriebene Einzelbinary-Loesung, laeuft problemlos auf einem kleinen VM mit 1-2 GB RAM.
- Forgejo: 2022 als Fork von Gitea entstanden (Reaktion auf die kommerzielle Neuausrichtung der Gitea-Organisation), seit 2024 ein eigenstaendiger Hard Fork unter Foundation-Governance (dieselbe Non-Profit-Struktur wie hinter Codeberg). Technisch sehr nah an Gitea, aber mit eigenem Actions-System (kompatibel zu GitHub-Actions-Syntax) und schnellerer Feature-Uebernahme aus der Community.
Entscheidungstabelle
| Kriterium | GitHub/GitLab SaaS | GitLab self-hosted | Gitea | Forgejo |
|---|---|---|---|---|
| Betriebsaufwand | keiner | hoch (mehrere Dienste, Updates) | niedrig (Einzelbinary) | niedrig (Einzelbinary) |
| Ressourcenbedarf | – | ab ca. 4 GB RAM | ab ca. 512 MB-1 GB RAM | ab ca. 512 MB-1 GB RAM |
| Datenhoheit | beim Anbieter | vollstaendig eigen | vollstaendig eigen | vollstaendig eigen |
| CI/CD integriert | ja (Actions/Pipelines) | ja (sehr umfangreich) | eingeschraenkt (Gitea Actions) | ja (Forgejo Actions, GH-Actions-kompatibel) |
| Governance | kommerziell | kommerziell (Open-Core) | kommerziell (Gitea Ltd.) | Non-Profit/Community |
| Passend fuer | kleine Teams ohne Compliance-Zwang | Unternehmen mit vollem DevOps-Anspruch | schlanke KMU-Loesung, Config-Backup | schlanke KMU-Loesung mit aktiver Community |
Entscheidungshilfe fuer den KMU-Alltag
Brauchst du volles CI/CD, Security-Scanning, Compliance-Reporting out of the box?
├── Ja → GitLab (self-hosted, wenn Daten im Haus bleiben muessen; sonst SaaS)
└── Nein
├── Reicht ein schlanker Git-Server fuer Configs + einfache Pull-Requests?
│ ├── Community-Governance wichtig / zukunftssicher? → Forgejo
│ └── Etablierte Marke, viele Third-Party-Integrationen? → Gitea
└── Kein eigener Betrieb gewuenscht, Cloud-Bindung akzeptabel? → GitHub/GitLab SaaS
Haeufige Stolperfallen im Admin-Alltag
| Problem | Ursache | Loesung |
|---|---|---|
| Zeilenumbrueche aendern sich staendig (CRLF/LF) | Windows-Editor speichert mit CRLF, Linux-Server erwartet LF | git config --global core.autocrlf true (Windows) bzw. .gitattributes mit * text=auto |
| ”detached HEAD” nach Checkout eines Tags | Tag zeigt auf Commit, nicht auf Branch | git switch -c hotfix-aus-tag direkt nach dem Checkout, um weiterzuarbeiten |
| Grosse Binärdateien (Firmware-Images, ISOs) blaehen Repo auf | Git ist fuer Textdiffs optimiert, nicht fuer Binaerdaten | Git LFS nutzen oder Binaerdateien in separatem Artefakt-Speicher (z. B. Nexus, R2-Bucket) halten |
Merge-Konflikt in terraform.tfstate | State wurde versehentlich versioniert statt in Remote Backend gehalten | State nie versionieren – Remote Backend mit Locking einrichten, siehe Terraform State Management |
| Force-Push ueberschreibt Kollegen-Commits | git push --force auf geteiltem Branch nach Rebase | git push --force-with-lease verwenden, Branch-Schutzregeln auf dem Server aktivieren |
Weiterlernen
- Pro Git Buch (Git-SCM, deutsch) – die offizielle, vollstaendige Git-Dokumentation als Buch
- Git-SCM: Fortgeschrittenes Merging – Details zu Merge-Strategien und Konflikttools
- Atlassian: Merge-Konflikte loesen – praxisnahes Tutorial mit Beispielen
- GitLab Docs: Rebase und Merge-Konflikte – Rebase-Workflow im Detail
- Gitleaks auf GitHub – Dokumentation zum Secret-Scanner
- Forgejo-Dokumentation – offizielle Doku zur self-hosted Git-Plattform
Videos
Kommentare
Frage, Verbesserungsvorschlag oder eigene Erfahrung zu diesem Artikel? Schreib einen Kommentar. Neue Beiträge erscheinen nach kurzer Moderation.
- Lade Kommentare …