From a7e87691a7be99b697f3c1a0033612191d8521b1 Mon Sep 17 00:00:00 2001 From: Yves Schumann Date: Mon, 28 Sep 2026 18:24:54 +0200 Subject: [PATCH] Implement sync script and setup documentation --- .sync-home-content.log | 61 +++++ QUICK-START.sh | 150 ++++++++++++ README-SYNC.md | 227 +++++++++++++++++ SYNC-STRATEGY.md | 423 ++++++++++++++++++++++++++++++++ sync-home-content.sh | 539 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1400 insertions(+) create mode 100644 .sync-home-content.log create mode 100755 QUICK-START.sh create mode 100644 README-SYNC.md create mode 100644 SYNC-STRATEGY.md create mode 100755 sync-home-content.sh diff --git a/.sync-home-content.log b/.sync-home-content.log new file mode 100644 index 0000000..dfb3a42 --- /dev/null +++ b/.sync-home-content.log @@ -0,0 +1,61 @@ +[2026-09-28 18:17:00] ═══════════════════════════════════════════════════════════ +[2026-09-28 18:17:00] Modus: status +[2026-09-28 18:17:00] ═══════════════════════════════════════════════════════════ +[2026-09-28 18:17:00] Status: Vergleiche Repo mit Home-Dir... +[2026-09-28 18:17:03] ═══════════════════════════════════════════════════════════ +[2026-09-28 18:17:03] Modus: status +[2026-09-28 18:17:03] ═══════════════════════════════════════════════════════════ +[2026-09-28 18:17:03] Status: Vergleiche Repo mit Home-Dir... +[2026-09-28 18:17:50] ═══════════════════════════════════════════════════════════ +[2026-09-28 18:17:50] Modus: status +[2026-09-28 18:17:50] ═══════════════════════════════════════════════════════════ +[2026-09-28 18:17:50] Status: Vergleiche Repo mit Home-Dir... +[2026-09-28 18:17:53] ═══════════════════════════════════════════════════════════ +[2026-09-28 18:17:53] Modus: status +[2026-09-28 18:17:53] ═══════════════════════════════════════════════════════════ +[2026-09-28 18:17:53] Status: Vergleiche Repo mit Home-Dir... +[DEBUG] ✓ identisch: .bash_aliases +[DEBUG] ✓ identisch: .gitconfig +[DEBUG] ✓ identisch: .gitconfig-architeq +[DEBUG] ✓ identisch: .gitconfig-bc +[DEBUG] ✓ identisch: .gitconfig-nettworks +[DEBUG] ✓ identisch: .gitconfig-private +[DEBUG] ✓ identisch: .gitconfig-ti8m +[DEBUG] ✓ identisch: .gitignore_global +[DEBUG] ✓ identisch: .npmrc +[DEBUG] ✓ identisch: .p10k.zsh +[DEBUG] ✓ identisch: .profile +[DEBUG] ✓ identisch: .testcontainers.properties +[DEBUG] ✓ identisch: .zshrc +[DEBUG] ✓ identisch: getsizes.sh +[OK] Status-Zusammenfassung: +[2026-09-28 18:17:53] ═══════════════════════════════════════════════════════════ +[OK] Abgeschlossen +[2026-09-28 18:18:40] ═══════════════════════════════════════════════════════════ +[2026-09-28 18:18:40] Modus: list +[2026-09-28 18:18:40] ═══════════════════════════════════════════════════════════ +[2026-09-28 18:18:40] Liste alle verwalteten Dateien: +[2026-09-28 18:18:40] ═══════════════════════════════════════════════════════════ +[OK] Abgeschlossen +[2026-09-28 18:18:43] ═══════════════════════════════════════════════════════════ +[2026-09-28 18:18:43] Modus: push +[2026-09-28 18:18:43] DRY-RUN: Keine Änderungen werden vorgenommen +[2026-09-28 18:18:43] ═══════════════════════════════════════════════════════════ +[2026-09-28 18:18:43] Push: Synchronisiere Repo-Dateien ins Home-Dir... +[DEBUG] ⊙ unverändert: .bash_aliases +[DEBUG] ⊙ unverändert: .gitconfig +[DEBUG] ⊙ unverändert: .gitconfig-architeq +[DEBUG] ⊙ unverändert: .gitconfig-bc +[DEBUG] ⊙ unverändert: .gitconfig-nettworks +[DEBUG] ⊙ unverändert: .gitconfig-private +[DEBUG] ⊙ unverändert: .gitconfig-ti8m +[DEBUG] ⊙ unverändert: .gitignore_global +[DEBUG] ⊙ unverändert: .npmrc +[DEBUG] ⊙ unverändert: .p10k.zsh +[DEBUG] ⊙ unverändert: .profile +[DEBUG] ⊙ unverändert: .testcontainers.properties +[DEBUG] ⊙ unverändert: .zshrc +[DEBUG] ⊙ unverändert: getsizes.sh +[OK] Push abgeschlossen: 0 kopiert, 0 Backups, 0 ausgelassen +[2026-09-28 18:18:43] ═══════════════════════════════════════════════════════════ +[OK] DRY-RUN abgeschlossen (keine Änderungen) diff --git a/QUICK-START.sh b/QUICK-START.sh new file mode 100755 index 0000000..e4e7479 --- /dev/null +++ b/QUICK-START.sh @@ -0,0 +1,150 @@ +#!/bin/bash +# Quick-Start Guide für sync-home-content.sh + +cat << 'EOF' + +╔════════════════════════════════════════════════════════════════════════════╗ +║ Quick-Start: home-content Synchronisierung ║ +╚════════════════════════════════════════════════════════════════════════════╝ + +📋 KURZVERSION (3 Schritte): + + 1. Status prüfen: + ./sync-home-content.sh status + + 2. Trockentest: + ./sync-home-content.sh sync --dry-run + + 3. Synchronisieren: + ./sync-home-content.sh sync + + +═══════════════════════════════════════════════════════════════════════════════ + +🎯 Welcher Modus passt? + +┌─ Lokal neue .gitconfig erstellt / bearbeitet +│ → ./sync-home-content.sh pull +│ +├─ Git-Pull durchgeführt, Repo hat Updates +│ → ./sync-home-content.sh push +│ +├─ "Weiß nicht, was geändert wurde" (safer) +│ → ./sync-home-content.sh sync +│ +├─ Nur schauen, nicht ändern +│ → ./sync-home-content.sh status +│ +└─ Alte Backups löschen (>30 Tage) + → ./sync-home-content.sh cleanup + + +═══════════════════════════════════════════════════════════════════════════════ + +🛡️ IMMER SO: + + TROCKENTEST vor echter Änderung: + ./sync-home-content.sh [modus] --dry-run + + Dann mit -v (verbose) um Details zu sehen: + ./sync-home-content.sh [modus] --dry-run -v + + +═══════════════════════════════════════════════════════════════════════════════ + +💾 BACKUPS: + + Alle Backups sind in: .sync-backups/ + + Restore eines Backups: + cp .sync-backups/.gitconfig.20260928_150000.backup home-content/.gitconfig + + Alte Backups löschen: + ./sync-home-content.sh cleanup + + +═══════════════════════════════════════════════════════════════════════════════ + +📝 CHECKLISTE für Anfänger: + + ☐ Stehe im Verzeichnis: ~/mac-home + ☐ Führe Status aus: ./sync-home-content.sh status + ☐ Lese die Zusammenfassung sorgfältig + ☐ Führe mit --dry-run aus: ./sync-home-content.sh [modus] --dry-run + ☐ Stimmst du mit den Änderungen überein? (JA → nächster Schritt) + ☐ Führe ohne --dry-run aus: ./sync-home-content.sh [modus] + ☐ Prüfe Logs: tail .sync-home-content.log + ☐ Backups prüfen: ls -la .sync-backups/ + + +═══════════════════════════════════════════════════════════════════════════════ + +⚡ HÄUFIGE SZENARIEN: + + 1️⃣ Neue dotfiles lokal bearbeitet → Repo speichern + + ./sync-home-content.sh status # Was wurde geändert? + ./sync-home-content.sh pull --dry-run # Trockentest + ./sync-home-content.sh pull # Echte Sync + cd home-content && git commit -am "Update" && git push + + 2️⃣ Git-Update von Team → Lokal anwenden + + git pull # Repository updaten + ./sync-home-content.sh push --dry-run # Trockentest + ./sync-home-content.sh push # Echte Sync + source ~/.zshrc # (bei .zshrc) neuladen + + 3️⃣ "Ich weiß nicht was los ist" (safest) + + ./sync-home-content.sh sync --dry-run -v # Ausführlich sehen + ./sync-home-content.sh sync # Bidirektional lösen + + 4️⃣ Ich habe versehentlich was kaputt gemacht + + ls -lh .sync-backups/ # Backup finden + cp .sync-backups/DATEI.*.backup home-content/DATEI + ./sync-home-content.sh push # Restore + + +═══════════════════════════════════════════════════════════════════════════════ + +❓ FRAGEN: + +Q: Verliere ich lokale Änderungen? +A: NEIN! Es wird immer ein Backup erstellt bevor überschrieben wird. + .sync-backups/ enthält die alten Versionen. + +Q: Was ist der Unterschied pull vs push? +A: PULL = Home → Repo (lokale Änderungen ins Repository) + PUSH = Repo → Home (Repository ins lokale Home-Dir) + +Q: Kann ich mehrere Dateien gleichzeitig mit --dry-run testen? +A: JA! --dry-run tut nix, du kannst mehrmals hintereinander testen. + +Q: Wie oft sollte ich cleanup laufen lassen? +A: Monatlich ist sinnvoll. Script löscht nur Backups >30 Tage. + +Q: Kann ich es automatisieren (Cron)? +A: JA! Aber nutze `sync` mode im non-interactive (Standard). + Im .sync-backups musst du selbst aufräumen. + + +═══════════════════════════════════════════════════════════════════════════════ + +🔗 WEITERE INFOS: + + Detaillierte Doku: SYNC-STRATEGY.md + Logs: .sync-home-content.log + Hilfe: ./sync-home-content.sh -h + + +═══════════════════════════════════════════════════════════════════════════════ + +🚀 READY? Let's go: + + cd ~/mac-home + ./sync-home-content.sh status + +EOF + diff --git a/README-SYNC.md b/README-SYNC.md new file mode 100644 index 0000000..2565002 --- /dev/null +++ b/README-SYNC.md @@ -0,0 +1,227 @@ +# mac-home Synchronisierungs-Toolset + +Drei hilfreiche Werkzeuge um deine Dotfiles (`~/.zshrc`, `~/.gitconfig`, etc.) zwischen deinem Home-Directory und diesem Repository zu synchronisieren. + +## 📦 Dateien + +| Datei | Größe | Zweck | +|-------|-------|-------| +| **`sync-home-content.sh`** | 17 KB | Hauptscript mit 6 Synchronisierungs-Modi | +| **`SYNC-STRATEGY.md`** | 10 KB | Ausführliche Dokumentation & Best Practices | +| **`QUICK-START.sh`** | 6 KB | Quick-Start Guide (diesen hier lesen!) | + +--- + +## 🚀 Schnellstart + +```bash +cd ~/mac-home + +# 1. Status anschauen +./sync-home-content.sh status + +# 2. Mit --dry-run testen (KEINE Änderungen) +./sync-home-content.sh sync --dry-run + +# 3. Echte Synchronisierung +./sync-home-content.sh sync +``` + +--- + +## 🎯 Die 6 Modi + +### 1. **STATUS** — Nur Information +Zeigt welche Dateien geändert wurden (ohne zu ändern). +```bash +./sync-home-content.sh status +``` + +### 2. **PUSH** — Repo → Home +Kopiert vom Repository ins lokale Home-Directory. +**Nutze wenn:** Git-Update vorhanden. +```bash +./sync-home-content.sh push +``` + +### 3. **PULL** — Home → Repo +Kopiert vom Home-Directory ins Repository. +**Nutze wenn:** Lokal Dateien bearbeitet. +```bash +./sync-home-content.sh pull +``` + +### 4. **SYNC** — Bidirektional +Intelligente 2-Wege Synchronisierung. +**Nutze wenn:** Du unsicher bist, was verändert wurde. +```bash +./sync-home-content.sh sync +``` + +### 5. **CLEANUP** — Alte Backups löschen +Entfernt Backups älter als 30 Tage. +```bash +./sync-home-content.sh cleanup +``` + +### 6. **LIST** — Verwaltete Dateien +Zeigt alle Dateien, die versioniert sind. +```bash +./sync-home-content.sh list +``` + +--- + +## 🛡️ Sicherheitsfeatures + +✅ **Backups:** Vor jeder Änderung wird ein Backup erstellt (`.sync-backups/`) +✅ **Dry-Run:** Mit `--dry-run` testen ohne zu ändern +✅ **Checksummen:** Content-basierter Vergleich (md5sum, nicht Timestamp) +✅ **Logs:** Alles protokolliert in `.sync-home-content.log` +✅ **Smart Conflict-Handling:** Neuere Version gewinnt automatisch (oder interaktiv) + +--- + +## 💡 Typische Workflows + +### Scenario 1: Lokal `.zshrc` bearbeitet +```bash +./sync-home-content.sh status # "≠ geändert im Home: .zshrc" +./sync-home-content.sh pull --dry-run # Test +./sync-home-content.sh pull # Sync ins Repo +git -C home-content commit -am "Update .zshrc" +git -C home-content push +``` + +### Scenario 2: Git-Pull → Home updaten +```bash +git pull # Repository updaten +./sync-home-content.sh push --dry-run # Test +./sync-home-content.sh push # Sync ins Home +source ~/.zshrc # (bei .zshrc reload) +``` + +### Scenario 3: "Weiß nicht was los ist" +```bash +./sync-home-content.sh sync --dry-run -v # Ausführlich sehen +./sync-home-content.sh sync # Bi-direktional sync +``` + +--- + +## 📋 Optionen + +| Flag | Effekt | Beispiel | +|------|--------|---------| +| `-d, --dry-run` | Zeige nur, ohne zu ändern | `./sync status --dry-run` | +| `-v, --verbose` | Detaillierter Output | `./sync status -v` | +| `-i, --interactive` | Frage bei Konflikten | `./sync sync --interactive` | +| `--no-backup` | Keine Backups erstellen | `./sync push --no-backup` | +| `-h, --help` | Zeige Hilfe | `./sync -h` | + +--- + +## 📚 Dokumentation + +- **Quick-Start:** `./QUICK-START.sh` (ausgeben) +- **Detaillierte Docs:** Lies `SYNC-STRATEGY.md` +- **Logs:** `cat .sync-home-content.log` + +--- + +## 🆘 Fehlerbehandlung + +### Backup versehentlich gelöscht? +```bash +# Schaue in .sync-backups nach +ls -lh .sync-backups/ + +# Restore +cp .sync-backups/.gitconfig.20260928_150000.backup home-content/.gitconfig +``` + +### Falsche Synchronisierung? +```bash +# Checksum-Mismatch? Das sind die gültigen Einträge: +cd home-content && git ls-files +``` + +### Script zu langsam? +- Normal für ~14 Dateien < 1 Sek (md5sum overhead minimal) +- Bei vielen Dateien: `seq-Verarbeitung` ist bewusst, nicht parallelisierbar + +--- + +## ⚙️ Erweiterte Nutzung + +### Alias für schnellen Zugriff +```bash +# In ~/.zshrc hinzufügen: +alias synchome="~/mac-home/sync-home-content.sh" +alias synchome-status="~/mac-home/sync-home-content.sh status" +alias synchome-pull="~/mac-home/sync-home-content.sh pull --dry-run && ~/mac-home/sync-home-content.sh pull" +``` + +### Cron-Job für regelmäßigen Sync +```bash +# Alle 2 Stunden +0 */2 * * * cd ~/mac-home && ./sync-home-content.sh sync >> /tmp/sync-home.log 2>&1 +``` + +--- + +## 🔍 Strategie: Inhalt vs. Timestamp? + +Das Script nutzt **md5sum für Erkennung** + **Timestamp für Konflikt-Auflösung**: + +| Aspekt | Methode | Grund | +|--------|---------|-------| +| Änderung erkennen | md5sum (Content) | Zuverlässig, Zeitzone-unabhängig | +| Konflikt lösen | Timestamp (mtime) | Welche Version ist neuerer? | +| Nur in Repo? | git ls-files | Tracking nur versionierter Dateien | + +--- + +## ✅ Best Practices + +**IMMER TUN:** +- ✓ Vor kritischen Änderungen `status` checken +- ✓ Mit `--dry-run` testen bevor echte Sync +- ✓ Logs regelmäßig prüfen: `tail .sync-home-content.log` +- ✓ Alte Backups mit `cleanup` entfernen (monatlich) + +**NICHT TUN:** +- ✗ `--no-backup` ohne wichtigen Grund nutzen +- ✗ `push` als Standard-Modus (zu aggressiv) +- ✗ Mehrere Sync-Prozesse parallel laufen lassen +- ✗ `.sync-backups/` von Hand löschen + +--- + +## 📖 Weitere Ressourcen + +- Detaillierte Erklärung der Modi: `SYNC-STRATEGY.md` (5 min Lesedauer) +- Troubleshooting: `SYNC-STRATEGY.md` → Abschnitt "Troubleshooting" +- Change-Management Strategie: `SYNC-STRATEGY.md` → Abschnitt "Change-Management" + +--- + +## 🤖 Was das Script tut + +1. Liest alle in `git ls-files` registrierten Dateien +2. Vergleicht deren Checksum zwischen Repo und Home +3. Erstellt automatisch Backups bei Änderungen +4. Synchronisiert basierend auf gewähltem Modus +5. Protokolliert alles in `.sync-home-content.log` +6. Entfernt alte Backups (> 30 Tage mit `cleanup`) + +--- + +## 💬 Fragen? + +Lesen Sie `SYNC-STRATEGY.md` - fast alle Fragen sind dort beantwortet! + +--- + +**Viel Erfolg beim Synchronisieren! 🎉** + diff --git a/SYNC-STRATEGY.md b/SYNC-STRATEGY.md new file mode 100644 index 0000000..e998df3 --- /dev/null +++ b/SYNC-STRATEGY.md @@ -0,0 +1,423 @@ +# Synchronisierungs-Strategie für home-content + +## Überblick + +Das Script `sync-home-content.sh` synchronisiert Dateien zwischen dem Git-Repository (`home-content/`) und dem echten Home-Verzeichnis (`$HOME`). + +**Schlüsselprinzipien:** +- ✓ Nur Dateien sync, die in `git ls-files` versioniert sind +- ✓ Content-basierte Erkennung via md5sum (verlässlicher als Timestamps) +- ✓ Automatische Backups bei Änderungen (`.sync-backups/`) +- ✓ Mehrere Modi für verschiedene Szenarien +- ✓ Sicher via Dry-Run vor echten Änderungen + +--- + +## Modi & Anwendungsfälle + +### 1. **STATUS** (Standard) — Nur Information +```bash +./sync-home-content.sh status +./sync-home-content.sh status -v # verbose mit Checksummen +``` + +**Was es tut:** +- Vergleicht alle Dateien im Repo mit Home-Dir +- Zeigt Unterschiede via md5sum +- **Ändert nichts** + +**Ausgabe-Bedeutungen:** +| Symbol | Bedeutung | +|--------|-----------| +| ✓ | Datei ist identisch | +| ≠ | Datei geändert im Home-Dir | +| ✚ | Datei nur im Repo (nicht im Home) | + +**Use-Case:** Am Anfang, um zu sehen, was sich geändert hat + +--- + +### 2. **PUSH** — Repo → Home-Dir +```bash +./sync-home-content.sh push --dry-run # Erst testen +./sync-home-content.sh push # Echte Synchronisierung +``` + +**Was es tut:** +- Kopiert Dateien vom Repo ins Home-Dir +- Überschreibt lokale Änderungen ohne zu fragen +- Erstellt Backup von geänderten lokalen Dateien (`.sync-backups/`) +- Neue Dateien: einfach kopieren ohne Backup + +**Ablauf:** +``` +1. Status 0 (identisch): unverändert +2. Status 3 (geändert): Backup Home → Repo → Home (Repo gewinnt) +3. Status 4 (nur Repo): Kopieren ohne Backup +``` + +**Use-Case:** +- Repo-Dateien sind authoritative Version (z.B. nach Git-Pull) +- Lokale Änderungen waren Fehlgriffe +- Schnelle Synchronisierung nach `.gitconfig` Update + +--- + +### 3. **PULL** — Home-Dir → Repo +```bash +./sync-home-content.sh pull --dry-run # Erst testen +./sync-home-content.sh pull # Echte Synchronisierung +``` + +**Was es tut:** +- Kopiert veränderte Dateien vom Home-Dir ins Repo +- Nur Updates bei geänderten Dateien +- Erstellt Backup der Repo-Dateien vor Update +- Nur im Home existierende Dateien: ignoriert + +**Ablauf:** +``` +1. Status 0 (identisch): unverändert +2. Status 3 (geändert): Backup Repo → Home → Repo (Home gewinnt) +3. Status 4 (nur Repo): ignoriert (nicht im Home) +``` + +**Use-Case:** +- Lokale Änderungen sind wichtig (z.B. neue `.gitconfig`) +- Diese sollen ins Repo → für andere Maschinen verfügbar machen +- Git-Commit erforderlich nach Pull + +--- + +### 4. **SYNC** — Bidirektional (Klug) +```bash +./sync-home-content.sh sync --dry-run # Testen +./sync-home-content.sh sync # Auto-Konflikt-Lösung +./sync-home-content.sh sync --interactive # Fragen bei Konflikten +``` + +**Was es tut:** +- Bidirektionale Synchronisierung +- **Automatisch:** Neuere Datei gewinnt (via Timestamp) +- **Interaktiv:** Benutzer wählt bei jedem Konflikt + +**Konflikt-Handling (Non-Interactive):** +``` +Wenn beide unterschiedlich: +- Home neuerer (mtime) → Home gewinnt (→ Repo) +- Repo neuerer (mtime) → Repo gewinnt (→ Home) +``` + +**Konflikt-Handling (Interactive):** +``` +Optionen für jede konflikt-Datei: +1) Nehme Repo-Version (→ Home überschreiben) +2) Nehme Home-Version (← Repo überschreiben) +3) Behalte beide (Home als Backup aufbewahren) +4) Überspringe diese Datei +``` + +**Beispiel Workflow:** +```bash +# 1. Anschauen, was zu sync ist +./sync-home-content.sh status -v + +# 2. Sicher testen +./sync-home-content.sh sync --dry-run + +# 3. Interaktiv bei Konflikten +./sync-home-content.sh sync --interactive + +# 4. Backups prüfen +ls -lh .sync-backups/ +``` + +**Use-Case:** +- Routine-Sync mit potentiellen lokalen Änderungen +- Sicherheit durch Backups +- Smart: neuere Versionen gewinnen automatisch + +--- + +### 5. **CLEANUP** — Alte Backups löschen +```bash +./sync-home-content.sh cleanup +``` + +**Was es tut:** +- Löscht Backups älter als 30 Tage +- Spart Speicherplatz + +**Backup-Struktur:** +``` +.sync-backups/ +├── .gitconfig.20260928_150000.backup +├── .gitconfig.20260925_140000.backup ← älter als 30 Tage → löschen +├── .zshrc.20260927_180000.backup +└── ... +``` + +--- + +### 6. **LIST** — Zeige verwaltete Dateien +```bash +./sync-home-content.sh list +``` + +Gibt alle Dateien aus, die versioniert sind (`git ls-files`). + +--- + +## Change-Management Strategie + +### Inhalt vs. Timestamp? + +Das Script nutzt **Content-Vergleich (md5sum)** als Primär-Verfahren: + +| Ansatz | Vorteile | Nachteile | +|--------|----------|----------| +| **Checksum (md5)** | Erkennt wirkliche Änderungen, Zeitzone-unabhängig | Minimal langsamer | +| **Timestamp** | Schnell | Falsch-Positive bei cp/rsync, Zeitzone-sensibel | + +**Strategie:** +1. **Erkennung:** md5sum → schnelle, zuverlässige Erkennung echter Änderungen +2. **Konflikt-Auflösung:** Timestamp (`mtime`) → welche Version ist neuerer + +--- + +## Backup-Strategie + +### Automatische Backups +- **Bei Push:** Backup der lokalen (überschriebenen) Datei vor Update +- **Bei Pull:** Backup der Repo-Datei vor Update +- **Bei Sync:** Backup je nach Konflikt-Lösung +- **Deaktivierbar:** `--no-backup` Option + +### Backup-Verzeichnis +```bash +.sync-backups/ +├── .gitconfig.20260928_150000.backup # Timestamp = YYYYMMDD_HHMMSS +├── .gitconfig.20260925_200000.backup +└── .zshrc.20260927_120000.backup +``` + +### Restore aus Backup +```bash +# Beispiel: .gitconfig wiederherstellen +cp .sync-backups/.gitconfig.20260928_150000.backup home-content/.gitconfig +``` + +--- + +## Workflow-Beispiele + +### Scenario 1: Neue `.gitconfig` lokal +```bash +# 1. Bearbeite lokal im Home +vim ~/.gitconfig + +# 2. Check Status +./sync-home-content.sh status +# Output: ≠ geändert im Home: .gitconfig + +# 3. Ins Repo synchronisieren +./sync-home-content.sh pull --dry-run +# Output: ← update: .gitconfig (Home → Repo) + +./sync-home-content.sh pull +# Output: Backup erstellt: .gitconfig (vor Pull) → .sync-backups/.gitconfig.20260928_XXX + +# 4. Git-Commit +cd home-content +git diff .gitconfig +git add .gitconfig +git commit -m "chore: update .gitconfig" +git push +``` + +### Scenario 2: Repo-Update von anderer Maschine +```bash +# 1. Remote fetch (Git Pull) +git pull origin main + +# 2. Check Status +./sync-home-content.sh status +# Output: ≠ geändert im Home: .zshrc + +# 3. Repo-Version ins Home +./sync-home-content.sh push --dry-run +# Output: → update: .zshrc (Repo → Home) [lokale Änderung wird überschrieben] + +./sync-home-content.sh push +# Output: Backup erstellt: .zshrc (vor Push) → .sync-backups/.zshrc.20260928_XXX + +# 4. Neue Datei aktiv (Shell reload bei .zshrc) +source ~/.zshrc +``` + +### Scenario 3: Bidirektional mit Konflikten +```bash +./sync-home-content.sh sync --interactive +# Für jede Konflikt-Datei: interaktiv wählen + +# Oder Auto-Modus (neuere gewinnt) +./sync-home-content.sh sync +``` + +--- + +## Optionen Referenz + +| Option | Effekt | Beispiel | +|--------|--------|---------| +| `-d`, `--dry-run` | Zeige nur, ohne zu ändern | `./sync status --dry-run` | +| `-v`, `--verbose` | Debug-Output (mit Checksummen) | `./sync status -v` | +| `-i`, `--interactive` | Frage bei Konflikten (nur sync) | `./sync sync --interactive` | +| `--no-backup` | Keine Backups erstellen | `./sync push --no-backup` | +| `-h`, `--help` | Zeige Hilfe | `./sync -h` | + +**Kombinierbar:** +```bash +./sync-home-content.sh sync --dry-run --verbose --interactive +``` + +--- + +## Logs & Debugging + +### Log-Datei +Alle Operationen werden protokolliert in `.sync-home-content.log`: +```bash +tail -f .sync-home-content.log +``` + +### Verbose Debug-Output +```bash +./sync-home-content.sh status -v +``` + +Output zeigt: +- Checksummen für jede Datei +- Genaue Änderungstypen +- Schritt-für-Schritt Durchsatz + +--- + +## Best Practices + +### ✓ TUN: +1. **Vor kritischen Änderungen:** `status` checken +2. **Vor echtem Sync:** `--dry-run` testen +3. **Periodisch Backups prüfen:** `ls -lh .sync-backups/` +4. **Nach Push:** Git-Commit & Push (falls Repo-Änderungen) +5. **Verbose nutzen:** Bei Unsicherheiten `-v` Option + +### ✗ NICHT: +1. `--no-backup` ohne Grund nutzen (Backups sind Rettungsanker) +2. `push` Mode als Standard (zu aggressiv) +3. Logs löschen ohne Archivierung +4. Alte Backups manuell löschen (dafür `cleanup`) +5. Mehrere Sync-Prozesse parallel laufen lassen + +--- + +## Troubleshooting + +### Problem: "git: command not found" +```bash +# Prüfe ob in Repository: +pwd +# Sollte: /Users/u241100/mac-home + +# Prüfe git Status: +git status +``` + +### Problem: "Permission denied" auf Backup +```bash +# Prüfe .sync-backups Permissions: +ls -ld .sync-backups +chmod 755 .sync-backups +chmod 644 .sync-backups/* +``` + +### Problem: Datei wurde falsch überschrieben +```bash +# 1. Finde richtiges Backup +ls .sync-backups/ | grep dateiname + +# 2. Restore +cp .sync-backups/dateiname.YYYYMMDD_HHMMSS.backup home-content/dateiname + +# 3. Ggf. manual zurückschreiben nach Home +cp home-content/dateiname ~/dateiname +``` + +### Problem: Script ist zu langsam +- Meist normal (für 14 Dateien < 1 Sek) +- Bei Verzeichniss-Inhalten mit vielen Dateien: md5sum ist minimal-overhead + +--- + +## Integration in Workflow + +### Alias für schnellen Zugriff +```bash +# In ~/.zshrc hinzufügen: +alias synchome="~/mac-home/sync-home-content.sh" +alias synchome-status="~/mac-home/sync-home-content.sh status" +alias synchome-pull="~/mac-home/sync-home-content.sh pull --dry-run && ~/mac-home/sync-home-content.sh pull" +``` + +### Pre-Commit Hook (Optional) +```bash +# .git/hooks/pre-commit +#!/bin/bash +~/mac-home/sync-home-content.sh status +# Bricht ab, wenn Unterschiede existieren +``` + +### Cron-Job für regelmäßigen Sync +```bash +# Jeden Tag um 2 Uhr nachts +0 2 * * * cd ~/mac-home && ./sync-home-content.sh sync >> /tmp/sync-home.log 2>&1 +``` + +--- + +## Zusammenfassung Konflikt-Handling + +| Szenario | Pull | Push | Sync (Auto) | Sync (Interactive) | +|----------|------|------|-------------|-------------------| +| Datei identisch | — | — | — | — | +| Nur Home geändert | Home gewinnt | — | neuere (Home) gewinnt | Benutzer wählt | +| Nur Repo geändert | — | Repo gewinnt | neuere (Repo) gewinnt | Benutzer wählt | +| Beide geändert | — | — | neuere (Timestamp) gewinnt | Benutzer wählt (4 Optionen) | +| Nur im Repo | ignoriert | kopiert | kopiert | kopiert | + +--- + +## Checkliste für neuen Rechner + +```bash +cd ~/mac-home + +# 1. Status prüfen +./sync-home-content.sh status + +# 2. Neue Dateien trocken kopieren +./sync-home-content.sh push --dry-run + +# 3. Echte Synchronisierung +./sync-home-content.sh push + +# 4. Backups überprüfen (sollten leer sein) +ls -la .sync-backups/ + +# 5. Log prüfen +tail .sync-home-content.log +``` + +Fertig! Home-Verzeichnis ist jetzt mit Repository sync. + + diff --git a/sync-home-content.sh b/sync-home-content.sh new file mode 100755 index 0000000..618c029 --- /dev/null +++ b/sync-home-content.sh @@ -0,0 +1,539 @@ +#!/bin/bash + +################################################################################ +# Synchronisierungs-Script: mac-home/home-content <-> Home-Directory +# +# Synchronisiert nur die Dateien, die im Git-Repo versioniert sind. +# Unterstützt mehrere Modi mit Konflikt-Handling. +# +# Strategie bei Changes: +# - Content-basiert via md5sum (verlässlicher als Timestamp) +# - Automatische Backups bei lokalen Änderungen +# - Mehrere Modi für verschiedene Szenarien +################################################################################ + +set -euo pipefail + +# Farben +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Verzeichnisse +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_DIR="$SCRIPT_DIR/home-content" +HOME_DIR="$HOME" +BACKUP_DIR="$SCRIPT_DIR/.sync-backups" +LOG_FILE="$SCRIPT_DIR/.sync-home-content.log" + +# Konfiguration +TIMESTAMP=$(date +%Y%m%d_%H%M%S) +DRY_RUN=false +VERBOSE=false +INTERACTIVE=false +AUTO_BACKUP=true + +################################################################################ +# Hilfsfunktionen +################################################################################ + +log() { + echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $*" | tee -a "$LOG_FILE" +} + +error() { + echo -e "${RED}[ERROR]${NC} $*" >&2 | tee -a "$LOG_FILE" +} + +success() { + echo -e "${GREEN}[OK]${NC} $*" | tee -a "$LOG_FILE" +} + +warn() { + echo -e "${YELLOW}[WARN]${NC} $*" | tee -a "$LOG_FILE" +} + +verbose() { + [[ "$VERBOSE" == true ]] && echo -e "${BLUE}[DEBUG]${NC} $*" | tee -a "$LOG_FILE" +} + +################################################################################ +# Berechne md5sum für eine Datei (mit Fallback für verschiedene OSs) +################################################################################ + +get_checksum() { + local file="$1" + if [[ -f "$file" ]]; then + if command -v md5 &> /dev/null; then + # macOS + md5 -q "$file" + elif command -v md5sum &> /dev/null; then + # Linux + md5sum "$file" | awk '{print $1}' + else + error "Weder md5 noch md5sum verfügbar" + return 1 + fi + fi +} + +################################################################################ +# Erstelle Backup einer Datei +################################################################################ + +backup_file() { + local src="$1" + local desc="$2" + + [[ ! -f "$src" ]] && return 0 + + mkdir -p "$BACKUP_DIR" + local backup_file="$BACKUP_DIR/$(basename "$src").$TIMESTAMP.backup" + cp "$src" "$backup_file" + log "Backup erstellt: $desc → $backup_file" +} + +################################################################################ +# Status einer Datei prüfen +################################################################################ + +# Vergleicht Datei im Repo mit Home-Dir +# Returns: +# 0 = identisch +# 1 = nur in Repo +# 2 = nur im Home +# 3 = unterschiedlicher Inhalt +# 4 = nur im Repo, nicht im Home + +check_file_status() { + local repo_file="$1" + local home_file="$2" + local repo_name=$(basename "$repo_file") + + if [[ ! -f "$repo_file" ]]; then + return 1 # nicht im Repo + fi + + if [[ ! -f "$home_file" ]]; then + return 4 # nur im Repo + fi + + local repo_sum=$(get_checksum "$repo_file") + local home_sum=$(get_checksum "$home_file") + + if [[ "$repo_sum" == "$home_sum" ]]; then + return 0 # identisch + else + return 3 # unterschiedlich + fi +} + +################################################################################ +# Modus: STATUS +# Zeigt Unterschiede zwischen Repo und Home +################################################################################ + +mode_status() { + log "Status: Vergleiche Repo mit Home-Dir..." + + local identical=0 + local modified=0 + local repo_only=0 + + while IFS= read -r file; do + local repo_file="$REPO_DIR/$file" + local basename=$(basename "$file") + local home_file="$HOME_DIR/$basename" + + check_file_status "$repo_file" "$home_file" + local status=$? + + case $status in + 0) + verbose "✓ identisch: $basename" + ((identical++)) + ;; + 3) + warn "≠ geändert im Home: $basename" + if [[ "$VERBOSE" == true ]]; then + echo " Repo: $(get_checksum "$repo_file")" + echo " Home: $(get_checksum "$home_file")" + fi + ((modified++)) + ;; + 4) + warn "✗ nur im Repo: $basename (nicht im Home)" + ((repo_only++)) + ;; + esac + done < <(cd "$REPO_DIR" && git ls-files) + + echo "" + success "Status-Zusammenfassung:" + echo " Identisch: $identical" + echo " Geändert: $modified" + echo " Nur im Repo: $repo_only" +} + +################################################################################ +# Modus: PULL +# Home-Dir → Repo (kopiere geänderte Dateien von Home ins Repo) +################################################################################ + +mode_pull() { + log "Pull: Synchronisiere geänderte Dateien von Home-Dir ins Repo..." + + local updated=0 + local skipped=0 + + while IFS= read -r file; do + local repo_file="$REPO_DIR/$file" + local basename=$(basename "$file") + local home_file="$HOME_DIR/$basename" + + check_file_status "$repo_file" "$home_file" + local status=$? + + case $status in + 0) + verbose "⊙ unverändert: $basename" + ;; + 3) + warn "← update: $basename (Home → Repo)" + if [[ "$DRY_RUN" == false ]]; then + [[ "$AUTO_BACKUP" == true ]] && backup_file "$repo_file" "$basename (vor Pull)" + cp "$home_file" "$repo_file" + ((updated++)) + fi + ;; + 4) + verbose "⊘ ausgelassen: $basename (nicht im Home)" + ((skipped++)) + ;; + esac + done < <(cd "$REPO_DIR" && git ls-files) + + success "Pull abgeschlossen: $updated aktualisiert, $skipped ausgelassen" +} + +################################################################################ +# Modus: PUSH +# Repo → Home (kopiere Repo-Dateien ins Home-Dir) +################################################################################ + +mode_push() { + log "Push: Synchronisiere Repo-Dateien ins Home-Dir..." + + local updated=0 + local backed_up=0 + local skipped=0 + + while IFS= read -r file; do + local repo_file="$REPO_DIR/$file" + local basename=$(basename "$file") + local home_file="$HOME_DIR/$basename" + + check_file_status "$repo_file" "$home_file" + local status=$? + + case $status in + 0) + verbose "⊙ unverändert: $basename" + ;; + 3) + warn "→ update: $basename (Repo → Home) [lokale Änderung wird überschrieben]" + if [[ "$DRY_RUN" == false ]]; then + if [[ "$AUTO_BACKUP" == true ]]; then + backup_file "$home_file" "$basename (vor Push)" + ((backed_up++)) + fi + cp "$repo_file" "$home_file" + ((updated++)) + fi + ;; + 4) + verbose "✚ neu: $basename (Repo → Home)" + if [[ "$DRY_RUN" == false ]]; then + cp "$repo_file" "$home_file" + ((updated++)) + fi + ;; + esac + done < <(cd "$REPO_DIR" && git ls-files) + + success "Push abgeschlossen: $updated kopiert, $backed_up Backups, $skipped ausgelassen" +} + +################################################################################ +# Modus: SYNC (bidirektional mit Konflikt-Handling) +################################################################################ + +mode_sync() { + log "Sync: Bidirektionale Synchronisierung..." + + local updated=0 + local backed_up=0 + local conflicts=0 + + while IFS= read -r file; do + local repo_file="$REPO_DIR/$file" + local basename=$(basename "$file") + local home_file="$HOME_DIR/$basename" + + check_file_status "$repo_file" "$home_file" + local status=$? + + case $status in + 0) + verbose "⊙ unverändert: $basename" + ;; + 3) + # Konflikt: beide unterschiedlich + if [[ "$INTERACTIVE" == true ]]; then + echo "" + warn "KONFLIKT: $basename unterscheidet sich" + echo " Repo Checksum: $(get_checksum "$repo_file")" + echo " Home Checksum: $(get_checksum "$home_file")" + echo "" + echo "Optionen:" + echo " 1) Nehme Repo-Version (→ Home überschreiben)" + echo " 2) Nehme Home-Version (← Repo überschreiben)" + echo " 3) Behalte beide (Home-Version wird gebackupt)" + echo " 4) Überspringe diese Datei" + read -p "Wahl [1-4]: " choice + + case $choice in + 1) + [[ "$DRY_RUN" == false ]] && { + backup_file "$home_file" "$basename (Konflikt-Lösung)" + cp "$repo_file" "$home_file" + ((updated++)) + } + ;; + 2) + [[ "$DRY_RUN" == false ]] && { + backup_file "$repo_file" "$basename (Konflikt-Lösung)" + cp "$home_file" "$repo_file" + ((updated++)) + } + ;; + 3) + [[ "$DRY_RUN" == false ]] && backup_file "$home_file" "$basename (Konflikt-Aufbewahrung)" + ((backed_up++)) + ;; + 4) + ((conflicts++)) + ;; + esac + else + # Non-interactive: Home gewinnt (lokal neuere Version wird bevorzugt) + local repo_mtime=$(stat -f%m "$repo_file" 2>/dev/null || stat -c%Y "$repo_file") + local home_mtime=$(stat -f%m "$home_file" 2>/dev/null || stat -c%Y "$home_file") + + if (( home_mtime > repo_mtime )); then + verbose "← konflikt (Home neuerer): $basename → Repo wird aktualisiert" + if [[ "$DRY_RUN" == false ]]; then + backup_file "$repo_file" "$basename (Konflikt)" + cp "$home_file" "$repo_file" + ((updated++)) + fi + else + verbose "→ konflikt (Repo neuerer): $basename → Home wird aktualisiert" + if [[ "$DRY_RUN" == false ]]; then + backup_file "$home_file" "$basename (Konflikt)" + cp "$repo_file" "$home_file" + ((updated++)) + fi + fi + fi + ;; + 4) + verbose "✚ neu: $basename (Repo → Home)" + if [[ "$DRY_RUN" == false ]]; then + cp "$repo_file" "$home_file" + ((updated++)) + fi + ;; + esac + done < <(cd "$REPO_DIR" && git ls-files) + + success "Sync abgeschlossen: $updated synchronisiert, $backed_up Backups, $conflicts Konflikte" +} + +################################################################################ +# Modus: CLEANUP +# Entferne alte Backup-Dateien (älter als 30 Tage) +################################################################################ + +mode_cleanup() { + log "Cleanup: Entferne alte Backups (älter als 30 Tage)..." + + if [[ ! -d "$BACKUP_DIR" ]]; then + success "Kein Backup-Verzeichnis vorhanden" + return 0 + fi + + local removed=0 + while IFS= read -r backup_file; do + rm -f "$backup_file" + verbose "Gelöscht: $backup_file" + ((removed++)) + done < <(find "$BACKUP_DIR" -type f -mtime +30) + + success "Cleanup abgeschlossen: $removed alte Backups gelöscht" +} + +################################################################################ +# Modus: LIST +# Liste alle verwalteten Dateien auf +################################################################################ + +mode_list() { + log "Liste alle verwalteten Dateien:" + echo "" + cd "$REPO_DIR" && git ls-files | while read -r file; do + echo " • $file" + done +} + +################################################################################ +# Hilfefunktion +################################################################################ + +show_help() { + cat << EOF +Verwendung: $0 [MODE] [OPTIONS] + +MODI: + status Zeige Unterschiede zwischen Repo und Home-Dir (Standard) + pull Synchronisiere von Home-Dir ins Repo (Home → Repo) + push Synchronisiere vom Repo ins Home-Dir (Repo → Home) + sync Bidirektionale Synchronisierung mit Konflikt-Handling + cleanup Entferne alte Backups (älter als 30 Tage) + list Liste alle verwalteten Dateien auf + +OPTIONEN: + -d, --dry-run Zeige nur Änderungen, ohne zu modifizieren + -v, --verbose Verbose Output (DEBUG) + -i, --interactive Interaktive Konflikte lösen (nur für sync) + --no-backup Keine automatischen Backups erstellen + -h, --help Diese Hilfe anzeigen + +BEISPIELE: + # Status anzeigen + $0 status + + # Von Home ins Repo synchronisieren (Trockentest) + $0 pull --dry-run + + # Bidirektional mit Backup synchronisieren + $0 sync + + # Interaktiv mit Konflikt-Handling + $0 sync --interactive + +STRATEGIE: + • Content-basiert via md5sum (zuverlässiger als Timestamps) + • Automatische Backups in .sync-backups/ vor Änderungen + • Nur Dateien synchronisieren, die in git versioniert sind + • Logs in .sync-home-content.log + +EOF +} + +################################################################################ +# Hauptprogramm +################################################################################ + +main() { + local mode="status" + + # Parse Argumente + while [[ $# -gt 0 ]]; do + case $1 in + status|pull|push|sync|cleanup|list) + mode="$1" + shift + ;; + -d|--dry-run) + DRY_RUN=true + shift + ;; + -v|--verbose) + VERBOSE=true + shift + ;; + -i|--interactive) + INTERACTIVE=true + shift + ;; + --no-backup) + AUTO_BACKUP=false + shift + ;; + -h|--help) + show_help + exit 0 + ;; + *) + error "Unbekannte Option: $1" + show_help + exit 1 + ;; + esac + done + + # Validierungen + if [[ ! -d "$REPO_DIR" ]]; then + error "Repo-Verzeichnis nicht gefunden: $REPO_DIR" + exit 1 + fi + + if ! git -C "$REPO_DIR" rev-parse --git-dir > /dev/null 2>&1; then + error "$REPO_DIR ist kein Git-Repository" + exit 1 + fi + + # Header + log "═══════════════════════════════════════════════════════════" + log "Modus: $mode" + [[ "$DRY_RUN" == true ]] && log "DRY-RUN: Keine Änderungen werden vorgenommen" + [[ "$INTERACTIVE" == true ]] && log "INTERAKTIV: Bei Konflikten wird nachgefragt" + log "═══════════════════════════════════════════════════════════" + + # Führe Modus aus + case $mode in + status) + mode_status + ;; + pull) + mode_pull + ;; + push) + mode_push + ;; + sync) + mode_sync + ;; + cleanup) + mode_cleanup + ;; + list) + mode_list + ;; + *) + error "Unbekannter Modus: $mode" + show_help + exit 1 + ;; + esac + + log "═══════════════════════════════════════════════════════════" + [[ "$DRY_RUN" == true ]] && success "DRY-RUN abgeschlossen (keine Änderungen)" || success "Abgeschlossen" +} + +# Starte Script +main "$@" +