diff --git a/Linux/BASH Basics/BASH Basics.md b/Linux/BASH Basics/BASH Basics.md index c653252..1f711be 100644 --- a/Linux/BASH Basics/BASH Basics.md +++ b/Linux/BASH Basics/BASH Basics.md @@ -16,6 +16,7 @@ Sammlung wichtiger BASH-Befehle und Konzepte für die Linux-Konsole. ## Dateiverwaltung - [[IT-Know-How/Linux/BASH Basics/ls]] - Dateien auflisten und Größen anzeigen +- [[IT-Know-How/Linux/BASH Basics/fd]] - Dateien schnell suchen und gesammelt verarbeiten - [[IT-Know-How/Linux/BASH Basics/find]] - Dateien suchen, filtern und verarbeiten (inkl. Datum, Löschen) - [[IT-Know-How/Linux/BASH Basics/Symlinks]] - Symbolische Links erstellen und verwalten diff --git a/Linux/BASH Basics/fd.md b/Linux/BASH Basics/fd.md new file mode 100644 index 0000000..13ca827 --- /dev/null +++ b/Linux/BASH Basics/fd.md @@ -0,0 +1,392 @@ +# fd - Dateien und Verzeichnisse schnell suchen + +`fd` ist ein moderner, benutzerfreundlicher Ersatz für `find`. Die Standards +sind für die meisten interaktiven Suchen praktischer: Die Suche ist rekursiv, +Groß-/Kleinschreibung wird automatisch berücksichtigt, versteckte Dateien und +von Git ignorierte Dateien werden standardmäßig ausgelassen und die Ausgabe ist +farbig formatiert. + +`fd` ist kein vollständiger Ersatz für jede komplexe `find`-Abfrage. Für +interaktive Dateisuche ist es aber oft kürzer und übersichtlicher. + +## Installation + +Debian/Ubuntu: + +```bash +sudo apt update +sudo apt install fd-find +``` + +Bei Debian und Ubuntu heißt das Programm je nach Distribution `fdfind` statt +`fd`: + +```bash +fdfind --version +alias fd=fdfind # nur für die aktuelle Shell +``` + +Arch Linux: + +```bash +sudo pacman -S fd +``` + +Fedora: + +```bash +sudo dnf install fd-find +``` + +openSUSE: + +```bash +sudo zypper install fd +``` + +Installation prüfen: + +```bash +fd --version +fd --help +``` + +## Grundsyntax + +```bash +fd [OPTIONEN] [MUSTER] [SUCHPFAD...] +``` + +Ohne Argumente zeigt `fd` die Dateien und Verzeichnisse unterhalb des +aktuellen Verzeichnisses: + +```bash +fd +fd README.md # nach README.md suchen +fd README ~/Projekte # in ~/Projekte suchen +fd 'backup.*' /var/tmp # Suchmuster angeben +``` + +Das Muster wird standardmäßig als regulärer Ausdruck interpretiert. Für eine +wortgetreue Suche kann `-g` (Glob) verwendet werden. + +## Nach Namen suchen + +```bash +fd report # enthält "report" im Namen +fd -i report # ausdrücklich ohne Beachtung der Großschreibung +fd -g '*.pdf' # Glob-Muster, alle PDF-Dateien +fd -e pdf # nach der Endung pdf +fd -e jpg -e png # mehrere Endungen +fd -t f -e log # nur Dateien mit der Endung log +fd -t d config # nur Verzeichnisse mit "config" im Namen +fd -H '.env' # versteckte Dateien einbeziehen +``` + +Bei Globs müssen Wildcards quotiert werden, damit die Shell sie nicht vorab +expandiert. `-e pdf` ist meist angenehmer als `-g '*.pdf'`, weil die Endung +ohne Punkt angegeben wird. + +### Reguläre Ausdrücke + +```bash +fd '^README' # Namen, die mit README beginnen +fd 'v[0-9]+\.tar\.gz$' # z. B. v1.tar.gz +fd -I 'node_modules|target' # auch ignorierte Dateien suchen +fd -g 'bild-??.jpg' # Glob: genau zwei Zeichen vor .jpg +``` + +Mehrere Suchpfade sind möglich: + +```bash +fd -t f -e md ~/Dokumente ~/Projekte +``` + +## Suchbereich einschränken + +```bash +fd -d 1 # nur direkt unterhalb des aktuellen Ordners +fd -d 3 -e log # höchstens drei Ebenen tief +fd -E node_modules # node_modules ausschließen +fd -E '*.min.js' # Dateien per Glob ausschließen +fd -E .git -E build # mehrere Ausschlüsse +fd -p 'src/lib' # Muster als vollständigen Pfad behandeln +fd -a report # absolute Pfade ausgeben +``` + +Standardmäßig werden versteckte Pfade, `.gitignore`-Treffer und `.git` +Verzeichnisse ausgelassen. Das Verhalten lässt sich mit diesen Optionen ändern: + +```bash +fd -H pattern # versteckte Dateien einbeziehen +fd -I pattern # ignorierte Dateien einbeziehen +fd -u pattern # unbeschränkte Suche: versteckt + ignoriert +``` + +## Dateitypen und Eigenschaften + +```bash +fd -t f # reguläre Dateien +fd -t d # Verzeichnisse +fd -t l # symbolische Links +fd -t x # ausführbare Dateien +fd -t e # leere Dateien oder Verzeichnisse +fd -S +100M -t f # Dateien größer als 100 MB +fd -S -10k -t f # Dateien kleiner als 10 KB +fd --changed-within 7d # in den letzten sieben Tagen geändert +fd --changed-before 30d # vor mehr als 30 Tagen geändert +``` + +Größen-Suffixe sind unter anderem `k`, `M`, `G` und `T`. Zeitangaben können +beispielsweise `10m`, `2h`, `7d` oder `1w` sein. Die genaue Unterstützung hängt +von der installierten `fd`-Version ab; `fd --help` zeigt die verfügbaren +Optionen. + +## Ausgabe und Zählen + +```bash +fd -l pattern # Long-Format wie bei ls +fd --color never pattern # ohne ANSI-Farbcodes +fd --print0 -e jpg # NUL-separiert, sicher für Sonderzeichen +fd -e log | wc -l # Treffer zählen +fd -e log | sort # Treffer sortieren +fd -e log | xargs -r grep -n ERROR +``` + +Für robuste Pipelines mit beliebigen Dateinamen immer `--print0` zusammen mit +`xargs -0` verwenden: + +```bash +fd -0 -t f -e txt | xargs -0 -r wc -l +``` + +## Befehle auf Treffer anwenden + +`-x` führt einen Befehl für jeden Treffer aus. `{}` ist der Platzhalter für den +aktuellen Pfad: + +```bash +fd -t f -e sh -x chmod +x '{}' +fd -t f -e log -x gzip '{}' +fd -t f -e txt -x grep -nH 'TODO' '{}' +``` + +`-X` übergibt alle Treffer gemeinsam an den Befehl. Das ist für Programme +geeignet, die mehrere Dateien in einem Aufruf verarbeiten können: + +```bash +fd -t f -e jpg -X identify +fd -t f -e sh -X shellcheck +``` + +Vor einer Aktion zuerst einen Dry-Run ausführen: + +```bash +fd -t f -e tmp -x echo rm -- '{}' +fd -t f -e jpg -x echo cp -- '{}' ~/Bilder/ +``` + +`--` trennt Optionen des externen Befehls vom Dateinamen. Die Quoting-Regeln +des Beispiels schützen auch Dateinamen mit Leerzeichen. + +## Dateien kopieren, verschieben und löschen + +### Löschen + +```bash +# Treffer prüfen +fd -t f -e tmp + +# Dry-Run +fd -t f -e tmp -x echo rm -- '{}' + +# Tatsächlich löschen +fd -t f -e tmp -x rm -- '{}' +``` + +Leere Verzeichnisse: + +```bash +fd -t d -e old -x echo rmdir -- '{}' +fd -t d -e old -x rmdir -- '{}' +``` + +`rmdir` löscht nur leere Verzeichnisse. Für nichtleere Verzeichnisse wäre +`rm -r` erforderlich und sollte besonders vorsichtig eingesetzt werden. + +### Kopieren + +```bash +mkdir -p ~/Archiv/logs +fd -t f -e log -x echo cp -n -- '{}' ~/Archiv/logs/ +fd -t f -e log -x cp -n -- '{}' ~/Archiv/logs/ +``` + +`cp -n` verhindert das Überschreiben vorhandener Dateien. Für das Beibehalten +von Zeitstempeln und Berechtigungen kann `cp -p` ergänzt werden. + +### Verschieben + +```bash +mkdir -p ~/Archiv/alte-logs +fd -t f -e log --changed-before 30d -x echo mv -i -- '{}' ~/Archiv/alte-logs/ +fd -t f -e log --changed-before 30d -x mv -i -- '{}' ~/Archiv/alte-logs/ +``` + +`mv -i` fragt vor dem Überschreiben nach. Mit `mv -n` werden bestehende +Zieldateien ohne Rückfrage nicht ersetzt. + +## Dateien gesammelt umbenennen + +Für einfache Ersetzungen ist `mv` mit Shell-Parameterexpansion ausreichend. +Zuerst die geplanten Befehle anzeigen: + +```bash +fd -t f -e jpeg -x sh -c 'for f do printf "mv -- %q %q\\n" "$f" "${f%.jpeg}.jpg"; done' sh +``` + +Wenn das Ergebnis stimmt: + +```bash +fd -t f -e jpeg -x sh -c 'for f do mv -i -- "$f" "${f%.jpeg}.jpg"; done' sh +``` + +Weitere Beispiele für Namensänderungen: + +```bash +# Suffix _alt ergänzen +fd -t f -e txt -x sh -c 'for f do mv -i -- "$f" "${f%.txt}_alt.txt"; done' sh + +# Präfix backup- ergänzen +fd -t f -e conf -x sh -c 'for f do mv -i -- "$f" "$(dirname "$f")/backup-$(basename "$f")"; done' sh +``` + +Bei vielen Umbenennungen ist `-x` mit einer kleinen Shell-Schleife sicherer +als eine Pipe, weil Dateinamen nicht an Leerzeichen oder Zeilenumbrüchen +aufgeteilt werden. + +## Bulk-Bearbeitung von Dateien + +### Textdateien mit sed bearbeiten + +```bash +# Erst anzeigen, welche Dateien betroffen sind +fd -t f -e conf -e cfg + +# Dry-Run: Ausgabe prüfen, Dateien bleiben unverändert +fd -t f -e conf -e cfg -x sh -c 'for f do sed "s/alt/neu/g" "$f" > "$f.tmp" && diff -u "$f" "$f.tmp"; rm -- "$f.tmp"; done' sh + +# In-place mit Backup-Datei +fd -t f -e conf -e cfg -x sed -i.bak 's/alt/neu/g' '{}' +``` + +### Dateinamen mit Textsuche kombinieren + +```bash +fd -t f -e md -x rg -nH 'TODO|FIXME' '{}' +fd -t f -e log -x sh -c 'for f do grep -l ERROR "$f"; done' sh +``` + +### Dateien anhand des Namens einsortieren + +```bash +mkdir -p ~/Sortiert/{pdf,bilder} + +# Dry-Run +fd -t f -e pdf -x echo mv -i -- '{}' ~/Sortiert/pdf/ +fd -t f -e jpg -e png -x echo mv -i -- '{}' ~/Sortiert/bilder/ + +# Ausführen +fd -t f -e pdf -x mv -i -- '{}' ~/Sortiert/pdf/ +fd -t f -e jpg -e png -x mv -i -- '{}' ~/Sortiert/bilder/ +``` + +### Robuste Schleife mit NUL-Trennung + +Wenn zusätzlich komplexe Shell-Logik oder mehrere Variablen benötigt werden, +ist eine NUL-separierte Schleife eine gute Alternative: + +```bash +while IFS= read -r -d '' file; do + printf 'Bearbeite: %s\n' "$file" + # command -- "$file" +done < <(fd -0 -t f -e txt) +``` + +## Praktische Anwendungsfälle + +```bash +# Die zehn größten Dateien unterhalb des aktuellen Ordners +fd -t f -S +1M -x du -h '{}' | sort -rh | head -10 + +# Alle ausführbaren Shell-Skripte finden +fd -t x -e sh + +# Backups und temporäre Dateien ausschließen +fd -t f -E '*.bak' -E '*.tmp' -E .cache + +# Markdown-Dateien der letzten Woche archivieren +fd -t f -e md --changed-within 7d -X tar -czf aktuelle-doku.tar.gz + +# Prüfsummen für ISO-Dateien erzeugen +fd -t f -e iso -x sha256sum '{}' +``` + +Bei `fd -X` werden die Treffer als Argumente an `tar` übergeben. Für sehr +große Trefferlisten kann ein Befehl durch die Systemgrenze für Argumente +begrenzt sein; dann besser `fd -0` mit `xargs -0` oder `tar`-Optionen für +Dateilisten verwenden. + +## Vergleich mit find + +| Aufgabe | `find` | `fd` | +|---------|--------|------| +| Alle Logs finden | `find . -type f -name '*.log'` | `fd -t f -e log` | +| Verzeichnis suchen | `find . -type d -name 'src'` | `fd -t d src` | +| Tiefe begrenzen | `find . -maxdepth 2` | `fd -d 2` | +| Verzeichnis ausschließen | `find . -path './node_modules' -prune` | `fd -E node_modules` | +| Befehl ausführen | `find . -type f -exec cmd {} +` | `fd -t f -X cmd` | +| Änderungszeit | `find . -mtime -7` | `fd --changed-within 7d` | + +Komplexe Bedingungen nach Besitzer, Berechtigungen oder exakten Zeitstempeln +bleiben eine Stärke von `find`. + +## Wichtige Optionen + +| Option | Bedeutung | +|--------|-----------| +| `-t f` | nur reguläre Dateien | +| `-t d` | nur Verzeichnisse | +| `-t l` | nur symbolische Links | +| `-e EXT` | Dateiendung suchen | +| `-g` | Glob statt regulärem Ausdruck verwenden | +| `-i` | Groß-/Kleinschreibung ignorieren | +| `-H` | versteckte Dateien einbeziehen | +| `-I` | Git-ignorierte Dateien einbeziehen | +| `-u` | versteckte und ignorierte Dateien einbeziehen | +| `-d N` | maximale Tiefe | +| `-E PATH` | Pfad oder Muster ausschließen | +| `-a` | absolute Pfade ausgeben | +| `-0` / `--print0` | NUL-separierte Ausgabe | +| `-x CMD` | Befehl pro Treffer ausführen | +| `-X CMD` | Befehl mit allen Treffern ausführen | +| `-S SIZE` | nach Dateigröße filtern | + +## Best Practices + +1. Zuerst nur `fd ...` ausführen und die Treffer kontrollieren. +2. Vor Änderungen `-x echo ...` als Dry-Run verwenden. +3. Dateipfade immer quoten: `"$file"` oder `'{}'`. +4. Bei Pipes für beliebige Dateinamen `-0` und `xargs -0` verwenden. +5. Bei `rm` und `mv` `--` vor dem Dateipfad setzen. +6. Für Lösch-, Verschiebe- und Umbenennungsaktionen Backups oder Versionskontrolle nutzen. +7. Mit `fd --help` prüfen, welche Optionen die installierte Version unterstützt. + +## Siehe auch + +- [[IT-Know-How/Linux/BASH Basics/find]] - Klassische Dateisuche mit find +- [[IT-Know-How/Linux/BASH Basics/grep]] - Textsuche in Dateien +- [[IT-Know-How/Linux/BASH Basics/sed]] - Textdateien gesammelt bearbeiten +- [[IT-Know-How/Linux/BASH Basics/awk]] - Textverarbeitung und Datenextraktion +- [[IT-Know-How/Linux/BASH Basics/Dateiberechtigungen]] - chmod und chown +- [[IT-Know-How/Linux/BASH Basics/rsync]] - Dateien synchronisieren diff --git a/Linux/SSH Verbindung in Linux.md b/Linux/SSH Verbindung in Linux.md index 9734014..7ca9fa0 100644 --- a/Linux/SSH Verbindung in Linux.md +++ b/Linux/SSH Verbindung in Linux.md @@ -98,4 +98,59 @@ ssh benutzername@192.168.178.42 --- -👉 Soll ich dir ein **kurzes Beispiel-Setup mit zwei Testbenutzern** schreiben, das du direkt im Heimnetz nachmachen kannst? \ No newline at end of file +👉 Soll ich dir ein **kurzes Beispiel-Setup mit zwei Testbenutzern** schreiben, das du direkt im Heimnetz nachmachen kannst? + +--- + +## 5. SSH-Tunnel für lokale Datenbankzugriffe + +Mit einem lokalen SSH-Tunnel (`-L`) wird ein Port auf dem eigenen Rechner an +einen Zielhost und Zielport weitergeleitet, die aus Sicht des SSH-Servers +erreichbar sind: + +```bash +ssh -L :: @ +``` + +Beispiel für PostgreSQL: + +```bash +ssh -L 15432:127.0.0.1:5432 benutzername@vm.example.org +``` + +Danach kann ein lokaler Client über `127.0.0.1:15432` auf PostgreSQL zugreifen. +Die Verbindung verläuft dabei so: + +```text +Datenbank-Client auf dem eigenen Rechner + | + | 127.0.0.1:15432 + v +SSH-Tunnel zur VM + | + | 127.0.0.1:5432 aus Sicht der VM + v +PostgreSQL auf der VM +``` + +`` wird aus Sicht der VM aufgelöst. Wenn PostgreSQL in einem +Docker-Container läuft, kann das je nach Setup beispielsweise `127.0.0.1`, +ein Docker-Service-Name oder eine Container-IP sein. Eine Container-IP wie +`172.19.0.2` sollte nicht dauerhaft vorausgesetzt werden, weil sie sich nach +dem Neuerstellen des Containers ändern kann. Siehe auch +[[DBeaver PostgreSQL via SSH-Tunnel]] und [[Docker]]. + +### Adressen nicht verwechseln + +`hostname -I` gibt alle Adressen des Rechners aus. Darunter können sich auch +Docker-Bridge-Adressen befinden. Die Ausgabe zeigt nicht, welche Adresse für +einen bestimmten Dienst die richtige ist: + +```text +10.2.0.4 Adresse der VM im Azure-Netzwerk +172.19.0.1 Docker-Bridge-Gateway +172.19.0.2 möglicherweise eine Container-IP +``` + +Die tatsächlich passende Adresse und der passende Port sollten mit `ss`, +`docker inspect`, `nc` oder `psql` geprüft werden. diff --git a/Linux/psql.md b/Linux/psql.md index 9bc4ae7..806a790 100644 --- a/Linux/psql.md +++ b/Linux/psql.md @@ -27,4 +27,61 @@ docker exec -i psql -U -d -c "SELECT psql -U -d -A -F, --csv -c "SELECT FROM ;" > path/tp/file.csv ``` Erklärung: ->The `-A` flag (unaligned output), `-F,` (field separator comma), and `--csv` together produce CSV \ No newline at end of file +>The `-A` flag (unaligned output), `-F,` (field separator comma), and `--csv` together produce CSV + +## Verbindung zu einem bestimmten Host und Port + +Für eine Verbindung zu einem PostgreSQL-Server auf einem bestimmten Host und +Port werden `-h` und `-p` verwendet: + +```bash +psql -h -p -U -d +``` + +Beispiel: + +```bash +psql -h 127.0.0.1 -p 15432 -U -d +``` + +Das ist beispielsweise der lokale Endpunkt eines SSH-Tunnels. Bei einem +Docker-Setup kann der Host dagegen eine Container-IP, ein Docker-Service-Name +oder die Loopback-Adresse der VM sein. Entscheidend ist, von wo aus die +Verbindung hergestellt wird. + +## PostgreSQL-Verbindung diagnostizieren + +Eine schrittweise Prüfung hilft dabei, Netzwerk-, Tunnel- und +Authentifizierungsprobleme voneinander zu unterscheiden: + +```bash +# Ist der TCP-Port erreichbar? +nc -vz + +# Lauscht auf der VM ein Prozess auf dem PostgreSQL-Port? +sudo ss -ltnp | grep 5432 + +# Funktioniert die PostgreSQL-Verbindung mit denselben Parametern? +psql -h -p 5432 -U -d +``` + +Wenn `psql` direkt auf der VM funktioniert, ist damit nur bewiesen, dass die +VM diesen Endpunkt erreichen kann. Es ist noch nicht bewiesen, dass ein +externer Client denselben Weg über den SSH-Tunnel verwendet. Zu prüfen sind +deshalb zusätzlich der lokale Tunnel-Port, der entfernte Zielhost, der +entfernte Zielport und die SSL-Einstellung des Clients. + +Für eine Docker-Datenbank sind außerdem hilfreich: + +```bash +docker ps +docker inspect +docker logs --tail 100 +``` + +Eine Container-IP kann sich ändern, wenn der Container neu erstellt wird. Für +dauerhafte Konfigurationen sind veröffentlichte Host-Ports oder Docker- +Service-Namen meist besser geeignet als eine fest eingetragene Container-IP. + +Siehe auch [[SSH Verbindung in Linux]], [[Docker]] und +[[DBeaver PostgreSQL via SSH-Tunnel]]. diff --git a/datenbanken/DBeaver PostgreSQL via SSH-Tunnel.md b/datenbanken/DBeaver PostgreSQL via SSH-Tunnel.md new file mode 100644 index 0000000..51e3e54 --- /dev/null +++ b/datenbanken/DBeaver PostgreSQL via SSH-Tunnel.md @@ -0,0 +1,144 @@ +# PostgreSQL mit DBeaver über einen SSH-Tunnel + +Ein SSH-Tunnel erlaubt den Zugriff auf eine Datenbank, die nur von einer +entfernten VM aus erreichbar ist. Der Tunnel löst dabei nicht automatisch alle +Netzwerkprobleme: Der Zielhost und Zielport werden aus Sicht der VM bewertet. + +## Verbindungsweg + +```text +DBeaver auf dem eigenen Rechner + | + | lokaler Tunnel-Endpunkt + v +SSH-Client ───────────────► SSH-Server auf der VM + | + | Zielhost:Zielport + v + PostgreSQL auf der VM + oder PostgreSQL-Container +``` + +Ein möglicher manueller Tunnel sieht so aus: + +```bash +ssh -L 15432:127.0.0.1:5432 @ +``` + +Danach ist die Datenbank vom eigenen Rechner aus typischerweise über +`127.0.0.1:15432` erreichbar. Bei DBeaver hängen die genauen Felder von der +verwendeten SSH-Tunnel-Konfiguration ab. Wichtig ist, die lokale +Datenbankadresse nicht mit dem entfernten Ziel der Weiterleitung zu +verwechseln. + +## Docker als Datenbank-Host + +Wenn PostgreSQL in Docker läuft, gibt es mehrere mögliche Ziele: + +| Situation | Typisches Ziel aus Sicht der VM | +| --- | --- | +| PostgreSQL läuft direkt auf der VM | `127.0.0.1:5432` | +| PostgreSQL-Port ist veröffentlicht | `127.0.0.1:` | +| Zugriff aus einem anderen Container | Compose-Service-Name, z. B. `postgres:5432` | +| Direkter Zugriff auf einen Container | aktuelle Container-IP, z. B. `172.19.0.2:5432` | + +Eine Container-IP sollte nur zum Testen verwendet werden. Sie kann sich nach +dem Neustart oder Neuerstellen des Containers ändern. Für eine dauerhafte +Konfiguration sind ein Compose-Service-Name innerhalb des Docker-Netzwerks +oder ein veröffentlichter Host-Port vorzuziehen. + +## Schrittweise Diagnose + +### 1. Den aktuellen Container und seine Ports prüfen + +Auf der VM: + +```bash +docker ps +docker inspect +sudo ss -ltnp | grep 5432 +``` + +Bei `docker ps` auf die Spalte `PORTS` achten. Eine Ausgabe wie +`127.0.0.1:5432->5432/tcp` bedeutet, dass PostgreSQL auf der VM lokal +veröffentlicht ist. Eine Ausgabe wie `0.0.0.0:15432->5432/tcp` bedeutet, dass +der Host-Port `15432` zum Container-Port `5432` weiterleitet. + +### 2. Den Zielendpunkt von der VM aus testen + +```bash +nc -vz +psql -h -p -U -d +``` + +Diese Tests müssen mit dem Host und Port durchgeführt werden, die der +SSH-Tunnel aus Sicht der VM erreichen soll. Ein funktionierendes `psql` ohne +`-h` kann beispielsweise über einen lokalen Unix-Socket verbinden und sagt +nichts über die TCP-Verbindung aus. + +### 3. PostgreSQL-Logs beobachten + +```bash +docker logs --tail 100 -f +``` + +Dann in DBeaver den Verbindungstest ausführen: + +- **Kein neuer Logeintrag:** DBeaver erreicht PostgreSQL nicht. Tunnel, + Zielhost, Zielport oder lokale/entfernte Portzuordnung prüfen. +- **Eintrag mit Authentifizierungsfehler:** Netzwerkweg funktioniert; Benutzer, + Passwort, Datenbank oder `pg_hba.conf` prüfen. +- **Verbindung wird sofort beendet:** SSL-Modus, PostgreSQL-Treiber und + Tunnelziel vergleichen. + +## Typische Fehler + +### `hostname -I` liefert mehrere Adressen + +`hostname -I` zeigt alle Adressen der VM. Dazu können die Azure-Adresse und +mehrere Docker-Bridge-Adressen gehören. Die Ausgabe ist keine Liste von +gleichwertigen Datenbankadressen: + +```text +10.2.0.4 VM-Adresse im privaten Azure-Netzwerk +172.19.0.1 Docker-Bridge-Gateway +172.19.0.2 möglicherweise PostgreSQL-Container +``` + +Mit `docker network inspect ` lässt sich prüfen, welchem +Container eine Docker-Adresse aktuell zugeordnet ist. + +### SSH funktioniert, DBeaver meldet `EOF Exception` + +Ein erfolgreicher SSH-Aufbau beweist nur, dass der SSH-Server erreichbar ist. +Der weitergeleitete TCP-Stream kann trotzdem am falschen Ziel oder Port enden. +Prüfen: + +1. Welcher lokale Port wird von DBeaver verwendet? +2. Welcher entfernte Host und Port werden durch den Tunnel angesprochen? +3. Ist der PostgreSQL-Container noch unter derselben IP erreichbar? +4. Lauscht PostgreSQL auf TCP und nicht nur auf einem Unix-Socket? +5. Verwendet DBeaver denselben SSL-Modus wie `psql`? +6. Erscheint der Test in den PostgreSQL-Logs? + +Die Verbindung sollte zunächst mit einer expliziten TCP-Verbindung geprüft +werden: + +```bash +psql -h 127.0.0.1 -p -U -d +``` + +Dabei muss `` dem tatsächlich veröffentlichten Port oder dem lokalen +Port des SSH-Tunnels entsprechen. + +## Sicherheits- und Wartungshinweise + +- Keine Passwörter oder produktiven Zugangsdaten in Notizen oder + Verbindungsdefinitionen im Klartext speichern. +- Container-IPs nicht als langfristige Infrastrukturkonfiguration verwenden. +- Für dauerhafte Zugriffe DNS-Namen, Compose-Service-Namen oder stabile + Host-Port-Zuordnungen bevorzugen. +- Für einen Diagnosefall die verwendeten Hosts, Ports, SSL-Einstellungen und + den Verbindungsweg dokumentieren, aber sensible Werte anonymisieren. + +Siehe auch [[SSH Verbindung in Linux]], [[psql]] und [[Docker]]. diff --git a/git/Gestapelten Feature-Branch nach einem Squash-Merge aktualisieren.md b/git/Gestapelten Feature-Branch nach einem Squash-Merge aktualisieren.md new file mode 100644 index 0000000..52572d9 --- /dev/null +++ b/git/Gestapelten Feature-Branch nach einem Squash-Merge aktualisieren.md @@ -0,0 +1,473 @@ +# Spickzettel: Gestapelten Feature-Branch nach einem Squash-Merge aktualisieren + +Ausgangssituation: + +```text +main: A----------------S + \ / +feature1: B------------C + \ +feature2: D---E +``` + +- `feature1` wurde von `main` abgezweigt. +- `feature2` wurde von `feature1` abgezweigt. +- `feature1` wurde anschließend per **Squash-Merge** in `main` integriert. +- `feature2` enthält sowohl die Änderungen von `feature1` als auch eigene Änderungen. +- Jetzt soll `feature2` auf dem aktuellen `main` basieren und nur noch seine eigenen Änderungen zusätzlich enthalten. + +Ziel: + +```text +main: A----------------S + \ +feature2: D'---E' +``` + +Die Commits `D` und `E` werden dabei als neue Commits `D'` und `E'` auf die neue Basis gesetzt. + +--- + +## 1. Arbeitsstand prüfen + +Zuerst sicherstellen, dass keine ungesicherten Änderungen vorhanden sind: + +```bash +git status +``` + +Idealerweise lautet die Ausgabe sinngemäß: + +```text +nothing to commit, working tree clean +``` + +Falls noch Änderungen vorhanden sind, entweder committen oder vorübergehend weglegen: + +```bash +git stash push -m "temporäre Änderungen vor Rebase" +``` + +--- + +## 2. Remote-Branches aktualisieren + +```bash +git fetch origin +``` + +Damit werden beispielsweise diese Referenzen aktualisiert: + +```text +origin/main +origin/feature1 +origin/feature2 +``` + +Wichtig: `origin/main` ist der aktuelle Stand des Remote-Branches. Er enthält jetzt den Squash-Commit von `feature1`. + +--- + +## 3. Prüfen, auf welchem Branch man arbeitet + +```bash +git switch feature2 +``` + +Anschließend: + +```bash +git status +``` + +Es sollte erkennbar sein, dass man sich auf `feature2` befindet. + +Optional kann man sich die Historie grafisch ansehen: + +```bash +git log --oneline --graph --decorate --all +``` + +--- + +## 4. Den alten Basis-Commit identifizieren + +Für den Rebase muss Git wissen: + +> Ab welchem Commit beginnen die Änderungen, die ausschließlich zu `feature2` gehören? + +Wenn der Branch `feature1` noch existiert und unverändert auf seinem ursprünglichen Stand steht, ist der Standardfall: + +```bash +git rebase --onto origin/main origin/feature1 feature2 +``` + +Das bedeutet: + +```text +git rebase --onto +``` + +Konkret: + +- `origin/main`: neue Basis +- `origin/feature1`: alte Basis, die nicht erneut übernommen werden soll +- `feature2`: Branch, dessen eigene Commits neu aufgesetzt werden + +Git nimmt also alle Commits, die in `feature2`, aber nicht in `feature1` enthalten sind, und wendet sie auf `origin/main` an. + +Das ist in der Regel genau das gewünschte Verhalten. + +--- + +## 5. Vorher ein Sicherheitsbackup anlegen + +Ein Rebase schreibt die Branch-Historie um. Deshalb ist ein Backup empfehlenswert: + +```bash +git switch feature2 +git branch backup/feature2-vor-rebase +``` + +Falls etwas schiefgeht, kann man jederzeit auf diesen Stand zurückgreifen: + +```bash +git switch backup/feature2-vor-rebase +``` + +Oder den ursprünglichen Stand wiederherstellen: + +```bash +git switch feature2 +git reset --hard backup/feature2-vor-rebase +``` + +--- + +## 6. Rebase durchführen + +Mit weiterhin ausgechecktem `feature2`: + +```bash +git rebase --onto origin/main origin/feature1 +``` + +Die Kurzform funktioniert hier, weil Git den aktuell ausgecheckten Branch verwendet. + +Alternativ explizit: + +```bash +git rebase --onto origin/main origin/feature1 feature2 +``` + +Nach dem erfolgreichen Rebase sieht die Historie ungefähr so aus: + +```text +main: A----------------S + \ +feature2: D'---E' +``` + +Die Änderungen aus `feature1` werden nicht noch einmal als eigene Commits übernommen. Sie sind bereits im Squash-Commit auf `main` enthalten. + +--- + +# Falls Konflikte auftreten + +Konflikte sind bei diesem Vorgang möglich, insbesondere wenn: + +- der Squash-Merge in GitLab manuell angepasst wurde, +- `feature2` dieselben Dateien wie `feature1` weiter verändert, +- sich `main` seit dem Merge weiterentwickelt hat. + +Git zeigt die betroffenen Dateien an: + +```bash +git status +``` + +Die Konfliktmarkierungen sehen beispielsweise so aus: + +```text +<<<<<<< HEAD +Stand von main +======= +Stand aus feature2 +>>>>>>> +``` + +Die Datei muss manuell in den gewünschten Endzustand gebracht werden. Danach: + +```bash +git add pfad/zur/datei +``` + +Wenn alle Konflikte des aktuellen Rebase-Schritts gelöst sind: + +```bash +git rebase --continue +``` + +Git kann anschließend weitere Konflikte melden. Dann wiederholt man: + +```bash +git status +# Dateien bearbeiten +git add +git rebase --continue +``` + +## Einen Rebase abbrechen + +Wenn der Rebase nicht sinnvoll fortgesetzt werden kann: + +```bash +git rebase --abort +``` + +Damit wird `feature2` auf den Zustand vor Beginn des Rebases zurückgesetzt. + +--- + +# 7. Ergebnis überprüfen + +Nach dem Rebase zunächst die Historie ansehen: + +```bash +git log --oneline --graph --decorate -20 +``` + +Außerdem prüfen, welche Änderungen `feature2` gegenüber `main` enthält: + +```bash +git diff origin/main...feature2 +``` + +Das sollte im Wesentlichen nur die Änderungen des zweiten Features zeigen. + +Auch die Commit-Liste kann geprüft werden: + +```bash +git log --oneline origin/main..feature2 +``` + +Dort sollten nur die eigenen Commits von `feature2` auftauchen, nicht mehr die ursprünglichen Commits von `feature1`. + +Zusätzlich: + +```bash +git status +``` + +--- + +# 8. Den umgeschriebenen Branch pushen + +Da ein Rebase die Commit-IDs ändert, ist ein normaler Push meist nicht möglich. Verwenden Sie: + +```bash +git push --force-with-lease origin feature2 +``` + +Nicht bevorzugt: + +```bash +git push --force origin feature2 +``` + +`--force-with-lease` ist sicherer, weil Git den Push ablehnt, wenn zwischenzeitlich jemand anderes neue Änderungen auf `origin/feature2` veröffentlicht hat. + +Danach sollte der Merge Request von `feature2` nach `main` aktualisiert sein. Im Diff sollten nun normalerweise nur noch die Änderungen des zweiten Features erscheinen. + +--- + +# Der wichtigste Standardbefehl + +Wenn `feature1` noch existiert und auf dem alten Stand steht: + +```bash +git fetch origin +git switch feature2 +git branch backup/feature2-vor-rebase +git rebase --onto origin/main origin/feature1 +git push --force-with-lease origin feature2 +``` + +Dabei sollte man den Rebase-Schritt nicht blind überspringen, sondern Konflikte und den anschließenden Diff prüfen. + +--- + +# Wenn `feature1` bereits gelöscht wurde + +Wenn der Remote-Branch `feature1` nach dem Merge gelöscht wurde, funktioniert dieser Befehl nicht mehr: + +```bash +git rebase --onto origin/main origin/feature1 +``` + +Dann braucht man den alten Stand von `feature1`, also den Commit, auf dem `feature2` ursprünglich basierte. + +## Möglichkeit A: Lokale Referenz vorhanden + +Prüfen: + +```bash +git branch -a +``` + +Vielleicht existiert noch ein lokaler Branch: + +```text +feature1 +``` + +Dann kann man verwenden: + +```bash +git rebase --onto origin/main feature1 feature2 +``` + +## Möglichkeit B: Commit aus der Historie verwenden + +Mit der Historie: + +```bash +git log --oneline --graph --all +``` + +den letzten Commit von `feature1` vor den eigenen `feature2`-Commits suchen. + +Angenommen, dieser Commit heißt: + +```text +abc1234 +``` + +Dann: + +```bash +git rebase --onto origin/main abc1234 feature2 +``` + +Bedeutung: + +> Alle Commits nach `abc1234`, die zu `feature2` gehören, auf `origin/main` setzen. + +## Möglichkeit C: Reflog verwenden + +Wenn der Commit nicht mehr über die normale Historie sichtbar ist: + +```bash +git reflog +``` + +Dort kann man nach dem ursprünglichen Abzweigpunkt oder dem früheren Stand von `feature1` suchen. + +--- + +# Variante ohne `rebase --onto`: eigene Commits per Cherry-Pick übernehmen + +Wenn die Branch-Historie unklar ist, kann man auch einen neuen Branch auf Basis von `main` erstellen und nur die eigenen `feature2`-Commits übernehmen. + +Beispiel: + +```bash +git fetch origin +git switch -c feature2-neu origin/main +git cherry-pick ^.. +``` + +Dabei werden die eigenen Commits von `feature2` einzeln übernommen. + +Danach kann der neue Branch entweder als neuer Merge Request verwendet oder nach sorgfältiger Prüfung an die Stelle von `feature2` gepusht werden. + +Diese Variante ist nützlich, wenn: + +- `feature1` gelöscht wurde, +- die ursprüngliche Branch-Basis nicht mehr eindeutig ist, +- `feature2` nur wenige eigene Commits enthält. + +--- + +# Was man nicht tun sollte + +Nach einem Squash-Merge von `feature1` ist dieser Ablauf problematisch: + +```bash +git switch feature2 +git merge origin/main +``` + +Das kann funktionieren, muss aber nicht. Git sieht die ursprünglichen Commits von `feature1` weiterhin in der Historie von `feature2`, während `main` einen neuen Squash-Commit enthält. Dadurch können Änderungen doppelt betrachtet oder Konflikte erzeugt werden. + +Für diesen speziellen Fall ist besser: + +```bash +git rebase --onto origin/main origin/feature1 +``` + +--- + +# Alternative: `feature1` nicht squashen + +Wenn häufig gestapelte Branches verwendet werden, kann es sinnvoll sein, den übergeordneten Branch `feature1` nicht zu squashen, sondern per normalem Merge zu integrieren. + +Dann bleibt die Commit-Abstammung erhalten: + +```text +main: A---------M + \ / +feature1: B-----C + \ +feature2: D---E +``` + +Git kann in diesem Fall besser erkennen, dass `B` und `C` bereits in `main` enthalten sind. + +Eine mögliche Teamregel wäre daher: + +- unabhängige Feature-Branches: Squash-Merge erlaubt +- voneinander abhängige oder gestapelte Feature-Branches: normaler Merge ohne Squash oder anschließend gezielter Rebase + +--- + +# Zusammenfassung + +Für Ihren Fall ist der typische Ablauf: + +```bash +git status +git fetch origin +git switch feature2 +git branch backup/feature2-vor-rebase +git rebase --onto origin/main origin/feature1 +``` + +Bei Konflikten: + +```bash +# Dateien bearbeiten +git add +git rebase --continue +``` + +Bei Abbruch: + +```bash +git rebase --abort +``` + +Nach erfolgreichem Rebase prüfen: + +```bash +git diff origin/main...feature2 +git log --oneline origin/main..feature2 +``` + +Dann pushen: + +```bash +git push --force-with-lease origin feature2 +``` + +Der entscheidende Punkt ist: **Nach einem Squash-Merge muss ein davon abgeleiteter Branch auf die neue `main`-Basis neu aufgebaut werden.** Ein einfacher Merge von `main` in den alten gestapelten Branch ist dafür nicht die zuverlässigsten Variante. \ No newline at end of file diff --git a/git/Git-History-Truncation.md b/git/Git-History-Truncation.md new file mode 100644 index 0000000..f578a31 --- /dev/null +++ b/git/Git-History-Truncation.md @@ -0,0 +1,164 @@ +# Git-History kuerzen + +Wenn ein Repository zu gross geworden ist und die alte Historie nicht mehr benoetigt wird, kann die Git-History ab einem bestimmten Commit abgeschnitten werden. Dabei wird die Historie **umgeschrieben**: Der Commit-Hash des neuen Anfangs und die Hashes aller folgenden Commits aendern sich. + +> **Achtung:** Das ist eine destruktive Operation fuer die bisherige Historie. Vorher unbedingt ein Backup erstellen und mit allen anderen Personen abstimmen, die mit dem Repository arbeiten. + +## Nicht verwechseln: Shallow Clone + +Ein flacher Clone verbirgt alte Historie nur lokal: + +```bash +git clone --shallow-since="2024-01-01" +``` + +Das Repository selbst wird dadurch nicht gekuerzt. Fuer eine dauerhafte Kuerzung muss die Historie umgeschrieben und anschliessend mit Force-Push auf das Remote uebertragen werden. + +## Vorbereitung: Backup erstellen + +Am sichersten arbeitet man mit einem Mirror-Clone: + +```bash +git clone --mirror repository-backup.git +cd repository-backup.git +``` + +Den urspruenglichen Clone bzw. das Backup erst loeschen, wenn das Ergebnis geprueft wurde. + +## `git-filter-repo` unter Arch Linux installieren + +Das Arch-Paket heisst `git-filter-repo`: + +```bash +sudo pacman -S git-filter-repo +``` + +Pruefen: + +```bash +git filter-repo --version +``` + +Ein systemweites `pip install` funktioniert auf Arch Linux wegen PEP 668 normalerweise nicht. Die Paketinstallation ist vorzuziehen. + +## Einen Commit zum neuen Root machen + +Angenommen, dieser Commit soll der neue Anfang der Historie werden: + +```text +464e39c1234567890abcdef1234567890abcdef +``` + +Dann werden seine Eltern entfernt: + +```bash +git filter-repo --force \ + --commit-callback 'if commit.original_id == b"464e39c1234567890abcdef1234567890abcdef": commit.parents = []' +``` + +`commit.original_id` enthaelt die vollstaendige 40-stellige Commit-ID. Eine kurze ID wie `464e39c` funktioniert mit `==` nicht. Alternativ kann ein eindeutiger Prefix so verwendet werden: + +```bash +git filter-repo --force \ + --commit-callback 'if commit.original_id.startswith(b"464e39c"): commit.parents = []' +``` + +Die vollstaendige ID kann vorher ermittelt werden: + +```bash +git rev-parse 464e39c +``` + +Aus: + +```text +A -- B -- C -- D -- E +``` + +wird dadurch sinngemaess: + +```text +C' -- D' -- E' +``` + +Der Commit `C` bleibt inhaltlich und mit seinen Metadaten erhalten, bekommt aber wegen der entfernten Eltern einen neuen Hash. Auch `D` und `E` erhalten neue Hashes. + +## Den Cutoff-Commit ebenfalls verwerfen + +Wenn der Cutoff-Commit selbst nicht erhalten bleiben soll, kann ein neuer Root-Commit mit seinem Dateistand erstellt und die spaetere Historie darauf rebased werden: + +```bash +git checkout --orphan temp +git commit -m "Start truncated history" +git rebase --onto temp main +git branch -D temp +``` + +Dabei ist `` der **letzte Commit, der verworfen werden soll**. Der neue Root-Commit enthaelt den Dateistand dieses Commits, ist aber ein neuer Commit. Bei Merge-Commits oder komplexen Branches kann die Rebase-Variante Konflikte verursachen. In solchen Faellen ist `git-filter-repo` in der Regel die bessere Wahl. + +## Ergebnis pruefen + +Vor dem Push die neue Historie kontrollieren: + +```bash +git log --all --graph --oneline --decorate +git status +``` + +Der neue Root-Commit darf keine `parent`-Zeile haben: + +```bash +git show --no-patch --pretty=raw +``` + +Auch der aktuelle Dateistand sollte unveraendert sein. Einen Vergleich kann man beispielsweise vor dem Umschreiben speichern oder mit dem Backup pruefen. + +## Remote wieder hinzufuegen + +`git-filter-repo` entfernt das Remote oft absichtlich als Sicherheitsmassnahme. Deshalb zuerst pruefen: + +```bash +git remote -v +``` + +Falls kein `origin` vorhanden ist: + +```bash +git remote add origin +git ls-remote origin +``` + +`origin/main` ist eine Branch-Referenz und kein Remote-Name. Der Push verwendet daher `origin main`, nicht `origin/main`. + +## Umschriebene Historie pushen + +Nur den Hauptbranch pushen: + +```bash +git push --force origin main +``` + +Alle lokalen Branches und Tags pushen: + +```bash +git push --force --all origin +git push --force --tags origin +``` + +Das Remote kann den Push wegen Branch-Schutzregeln ablehnen. In diesem Fall muss der Schutz voruebergehend angepasst werden. + +## Nacharbeiten und wichtige Folgen + +- Alle Commit-Links, Pull Requests und Tags koennen durch die neuen Hashes ungueltig werden. +- Andere Clone enthalten weiterhin die alte Historie und muessen neu geklont oder manuell angepasst werden. +- Andere Branches oder Tags koennen die alte Historie weiterhin erreichbar machen. +- Ein Force-Push kann nicht ohne Weiteres rueckgaengig gemacht werden; das Backup aufbewahren. + +Alte, nicht mehr erreichbare Objekte koennen nach der Pruefung lokal bereinigt werden: + +```bash +git reflog expire --expire=now --all +git gc --prune=now +``` + +`git gc --aggressive` ist normalerweise nicht notwendig und kann bei grossen Repositories lange dauern. Auf einem Hosting-Dienst werden nicht mehr erreichbare Objekte eventuell erst spaeter durch dessen eigene Garbage Collection geloescht. diff --git a/verschiedene/Docker.md b/verschiedene/Docker.md index 6966733..4b01483 100644 --- a/verschiedene/Docker.md +++ b/verschiedene/Docker.md @@ -777,4 +777,68 @@ docker-compose up --build - ✅ Automatische File-Verarbeitung - ✅ Persistente Datenspeicherung -**Das ist die Macht von Docker! 🐳** \ No newline at end of file +**Das ist die Macht von Docker! 🐳** + +--- + +## Docker-Netzwerk bei Datenbankproblemen untersuchen + +Bei einer Datenbank in einem Docker-Container muss zwischen verschiedenen +Netzwerkperspektiven unterschieden werden: + +- **Innerhalb eines Docker-Netzwerks:** Container erreichen sich normalerweise + über den Compose-Service-Namen, zum Beispiel `postgres:5432`. +- **Auf dem Docker-Host:** Ein veröffentlichter Port wie `5432:5432` ist über + den Host erreichbar, häufig über `127.0.0.1:5432`. +- **Von außerhalb des Hosts:** Ein SSH-Tunnel kann einen lokalen Port auf den + Host-Port oder auf einen aus Sicht des Hosts erreichbaren Container-Endpunkt + weiterleiten. + +### Docker-Bridge-Adressen + +Adressen wie `172.19.0.1` und `172.19.0.2` gehören häufig zu einem Docker- +Bridge-Netzwerk: + +- `172.19.0.1` ist typischerweise das Gateway des Netzwerks auf dem Host. +- `172.19.0.2` kann die aktuelle IP eines Containers sein. + +Container-IPs sind nicht als dauerhafte Konfiguration geeignet. Nach einem +`docker compose up`, einer Neuerstellung oder einem Deployment kann derselbe +Container eine andere IP erhalten. Innerhalb von Compose-Netzwerken sollte der +Service-Name verwendet werden. Von außerhalb sollte nach Möglichkeit ein +veröffentlichter Host-Port verwendet werden. + +### Nützliche Diagnosebefehle + +```bash +# Laufende Container und veröffentlichte Ports anzeigen +docker ps + +# Netzwerkdetails und aktuelle Container-IP anzeigen +docker inspect + +# Vorhandene Docker-Netzwerke anzeigen +docker network ls + +# Teilnehmer und IPs eines Netzwerks anzeigen +docker network inspect + +# Prüfen, ob der Host lokal auf dem PostgreSQL-Port lauscht +sudo ss -ltnp | grep 5432 + +# TCP-Erreichbarkeit eines bestimmten Ziels testen +nc -vz 5432 + +# PostgreSQL-Logs während eines Verbindungsversuchs beobachten +docker logs --tail 100 -f +``` + +Wenn ein Client eine Verbindung mit `EOF` beendet, sollte zuerst geprüft +werden, ob überhaupt ein neuer Eintrag in den PostgreSQL-Logs erscheint. Ohne +Logeintrag liegt das Problem wahrscheinlich vor PostgreSQL, beispielsweise am +Tunnel, am Zielhost oder am Zielport. Ein Logeintrag zeigt, dass PostgreSQL +erreicht wurde; dann kommen unter anderem SSL- oder +Authentifizierungsparameter als Ursache infrage. + +Siehe auch [[psql]], [[SSH Verbindung in Linux]] und +[[DBeaver PostgreSQL via SSH-Tunnel]].