Note: yay cache .git root cause, find -prune/-delete gotcha, paccache timer
This commit is contained in:
@@ -324,6 +324,115 @@ find . -type f -name "*.tmp" -delete
|
|||||||
find . -name "*.tmp" -delete
|
find . -name "*.tmp" -delete
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### -delete und -prune sind unvereinbar (wichtig!)
|
||||||
|
|
||||||
|
**`-delete` aktiviert automatisch `-depth`.** Bei `-depth` (Tiefensuche) ist `-prune`
|
||||||
|
aber wirkungslos – der Schutz wird also genau dann aufgehoben, wenn man ihn braucht.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# FALSCH: -prune schützt NICHT, find warnt und führt den Befehl trotzdem aus!
|
||||||
|
find ~/.cache -name .git -prune -o -type f -atime +30 -delete
|
||||||
|
```
|
||||||
|
|
||||||
|
Ausgabe von GNU find:
|
||||||
|
|
||||||
|
```
|
||||||
|
find: Die Option -delete aktiviert -depth automatisch, aber -prune ist wirkungslos,
|
||||||
|
wenn -depth aktiv ist. In diesem Fall muss -depth zusätzlich angegeben werden.
|
||||||
|
```
|
||||||
|
|
||||||
|
Das ist eine **Warnung, kein Fehler** – der Befehl läuft weiter, mit deaktiviertem Schutz.
|
||||||
|
Das hat im April 2026 bei einem AUR-Cache-Cleanup die `.git/HEAD`- und `.git/config`-Dateien
|
||||||
|
von 24 Paketen gelöscht (siehe [[IT-Know-How/Linux/yay Cache reparieren]]).
|
||||||
|
|
||||||
|
**Lösung: `-prune` durch `-not -path` ersetzen.** `-path` funktioniert auch bei `-depth`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# RICHTIG: -not -path statt -prune
|
||||||
|
find ~/.cache -type f -atime +30 -not -path '*/.git/*' -delete
|
||||||
|
```
|
||||||
|
|
||||||
|
Verifiziertes Verhalten:
|
||||||
|
|
||||||
|
| Datei | Ergebnis |
|
||||||
|
|-------|----------|
|
||||||
|
| `normal/old.txt` (alt) | gelöscht |
|
||||||
|
| `.git/HEAD` (alt) | **bleibt erhalten** |
|
||||||
|
| `.git/config` (alt) | **bleibt erhalten** |
|
||||||
|
|
||||||
|
> **Merksatz:** `-prune` funktioniert nur bei der *Breitensuche* (Voreinstellung).
|
||||||
|
> Sobald `-depth` im Spiel ist – etwa durch `-delete` oder `-exec` mit `{} +` –
|
||||||
|
> muss der Ausschluss über `-not -path` oder `-not -name` erfolgen.
|
||||||
|
|
||||||
|
### Ausschlusslisten sicher formulieren
|
||||||
|
|
||||||
|
Mehrere Ausschlüsse kombinieren:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# .git und node_modules ausschließen
|
||||||
|
find . -type f -atime +30 -not -path '*/.git/*' -not -path '*/node_modules/*' -delete
|
||||||
|
|
||||||
|
# statt .git prüfen: nur in echten Cache-Verzeichnissen löschen
|
||||||
|
find ~/.cache -mindepth 2 -type f -atime +30 -not -path '*/.git/*' -delete
|
||||||
|
```
|
||||||
|
|
||||||
|
Immer vorher mit `-print` prüfen, wie viele Dateien betroffen sind:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
find ~/.cache -type f -atime +30 -not -path '*/.git/*' -print | wc -l
|
||||||
|
```
|
||||||
|
|
||||||
|
### noatime: warum -atime auf dem Root-Filesystem täuscht
|
||||||
|
|
||||||
|
Bei einem `noatime`-Mount wird die Zugriffszeit beim **Lesen nicht mehr aktualisiert**.
|
||||||
|
`-atime` misst dann faktisch nur noch das Alter der letzten **Schreiboperation** –
|
||||||
|
also praktisch `mtime`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Mount-Optionen prüfen
|
||||||
|
findmnt -T /home
|
||||||
|
# TARGET SOURCE FSTYPE OPTIONS
|
||||||
|
# / /dev/... ext4 rw,noatime,commit=60
|
||||||
|
|
||||||
|
# Bei noatime ist -atime +30 == "in 30 Tagen nicht mehr geschrieben"
|
||||||
|
```
|
||||||
|
|
||||||
|
Folge: Ein `find -atime +30 -delete` löscht auch Dateien, die **täglich gelesen** werden –
|
||||||
|
sofern sie 30 Tage lang nicht geschrieben wurden. Bei Cache-Bereinigungen ist `-mtime`
|
||||||
|
meist die ehrlichere Wahl, weil Caches ohnehin nur geschrieben, nie gelesen werden.
|
||||||
|
|
||||||
|
`atime` vs. `mtime` im Vergleich:
|
||||||
|
|
||||||
|
| Mount | `-atime +30` bedeutet |
|
||||||
|
|-------|----------------------|
|
||||||
|
| `relatime` (Standard) | wirklich: seit 30 Tagen nicht mehr gelesen |
|
||||||
|
| `noatime` | seit 30 Tagen nicht mehr **geschrieben** |
|
||||||
|
| `strictatime` | wie `relatime`, aber exakter Zeitstempel |
|
||||||
|
|
||||||
|
### Sicheres Aufräumen mit Ausschlussliste
|
||||||
|
|
||||||
|
Kombiniertes Rezept für Cache-Bereinigungen – zuerst prüfen, dann löschen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Was würde gelöscht? (Anzahl + Größe)
|
||||||
|
find ~/.cache -type f -atime +30 -not -path '*/.git/*' -print | wc -l
|
||||||
|
find ~/.cache -type f -atime +30 -not -path '*/.git/*' -printf '%s\n' \
|
||||||
|
| awk '{s+=$1} END {printf "%.2f GB\n", s/1024/1024/1024}'
|
||||||
|
|
||||||
|
# 2. Abhängig vom Ziel:
|
||||||
|
# a) altersbasiert: .git über -not -path ausnehmen
|
||||||
|
find ~/.cache -type f -atime +30 -not -path '*/.git/*' -delete
|
||||||
|
|
||||||
|
# b) nur bestimmte Artefakte: die Endungen begrenzen, .git bleibt außen vor
|
||||||
|
find ~/.cache -type f \( -name '*.pkg.tar.zst' -o -name '*.AppImage' \
|
||||||
|
-o -name '*.tar.gz' \) -not -path '*/.git/*' -delete
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Nicht mit `-prune` löschen:** `-delete` schaltet intern auf `-depth` um, und
|
||||||
|
> `-prune` wird dann wirkungslos. Wer trotzdem `-prune` im selben Befehl nutzt,
|
||||||
|
> verliert den `.git`-Schutz exakt im Moment des Löschens. Ausschlüsse immer über
|
||||||
|
> `-not -path` formulieren.
|
||||||
|
|
||||||
### Zu viele Argumente
|
### Zu viele Argumente
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -360,3 +469,4 @@ fd -e jpg -x convert {} ... # Mit exec
|
|||||||
- [[IT-Know-How/Linux/BASH Basics/rsync]] - Dateien synchronisieren
|
- [[IT-Know-How/Linux/BASH Basics/rsync]] - Dateien synchronisieren
|
||||||
- [[IT-Know-How/Linux/BASH Basics/sed]] - Gefundene Dateien bearbeiten
|
- [[IT-Know-How/Linux/BASH Basics/sed]] - Gefundene Dateien bearbeiten
|
||||||
- [[IT-Know-How/Linux/BASH Basics/awk]] - Ausgabe von find verarbeiten
|
- [[IT-Know-How/Linux/BASH Basics/awk]] - Ausgabe von find verarbeiten
|
||||||
|
- [[IT-Know-How/Linux/yay Cache reparieren]] - Realer Anwendungsfall: Cache-Cleanup hat `.git` zerstört
|
||||||
|
|||||||
@@ -0,0 +1,214 @@
|
|||||||
|
# Pacman Paketcache verwalten (paccache)
|
||||||
|
|
||||||
|
Pacman lädt jedes Paket als `.pkg.tar.zst` nach `/var/cache/pacman/pkg` und **löscht es danach
|
||||||
|
nicht mehr**. Der Cache wächst dadurch unbegrenzt – auf diesem System waren es einmal 66 GB,
|
||||||
|
was den Plattenplatz deutlich stärker belegte als der gesamte AUR-Cache (`~/.cache/yay`).
|
||||||
|
|
||||||
|
Mit `paccache` (Paket `pacman-contrib`) lässt sich das steuern.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Wo liegt der Cache?
|
||||||
|
|
||||||
|
Standardpfad ist `/var/cache/pacman/pkg`. Wird in `/etc/pacman.conf` kein `CacheDir`
|
||||||
|
gesetzt, gilt dieser Pfad:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Konfiguration prüfen
|
||||||
|
grep -E '^CacheDir' /etc/pacman.conf
|
||||||
|
# nicht gesetzt → Standard /var/cache/pacman/pkg
|
||||||
|
|
||||||
|
# Aktuelle Größe (root-owned, daher sudo nötig)
|
||||||
|
sudo du -sh /var/cache/pacman/pkg
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Achtung:** Der Pacman-Cache liegt **nicht** unter `~/.cache/pacman`. Er gehört dem
|
||||||
|
> System und braucht `sudo`. `~/.cache/pacman` existiert auf Arch normalerweise nicht.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## paccache: Optionen
|
||||||
|
|
||||||
|
*paccache v1.13.1*
|
||||||
|
|
||||||
|
**Operationen** (was passieren soll):
|
||||||
|
|
||||||
|
| Option | Bedeutung |
|
||||||
|
| --- | --- |
|
||||||
|
| `-d, --dryrun` | nur Kandidaten finden, **nichts löschen** |
|
||||||
|
| `-r, --remove` | Kandidaten löschen |
|
||||||
|
| `-m, --move <dir>` | Kandidaten nach `<dir>` **verschieben** statt löschen |
|
||||||
|
|
||||||
|
**Optionen** (wie ausgewählt wird):
|
||||||
|
|
||||||
|
| Option | Bedeutung |
|
||||||
|
| --- | --- |
|
||||||
|
| `-k, --keep <num>` | nur die `<num>` neuesten Versionen pro Paket behalten (**Default: 3**) |
|
||||||
|
| `-u, --uninstalled` | nur Pakete, die derzeit **nicht** installiert sind |
|
||||||
|
| `-c, --cachedir <dir>` | zu durchsuchendes Verzeichnis (Default: aus `/etc/pacman.conf`) |
|
||||||
|
| `--min-atime <zeit>` | Pakete behalten, die jünger als `<zeit>` sind – unabhängig von `-k` |
|
||||||
|
| `--min-mtime <zeit>` | dito, anhand des Änderungsdatums (zuverlässiger, siehe unten) |
|
||||||
|
| `-i, --ignore <pkgs>` | Paketnamen ausschließen (Komma-separiert) |
|
||||||
|
| `-v, --verbose` | ausführlicher, bis `-vvv` |
|
||||||
|
| `-q, --quiet` | Ausgabe reduzieren |
|
||||||
|
| `-f, --force` | entspricht `rm -f` / `mv -f` |
|
||||||
|
| `-z, --null` | null-Trennzeichen in der Ausgabe (nur mit `-v`/`-vv`) |
|
||||||
|
| `--nocolor` | keine Farbformatierung |
|
||||||
|
|
||||||
|
> **Achtung, Default:** Ohne `-k` behält `paccache` **3** Versionen pro Paket. Wer
|
||||||
|
> gezielt reduzieren will, muss `-k` explizit setzen.
|
||||||
|
|
||||||
|
Die beiden wichtigsten Aufrufe:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Vorschau: 2 neueste Versionen behalten
|
||||||
|
sudo paccache -rk2 -v
|
||||||
|
|
||||||
|
# Ausführen
|
||||||
|
sudo paccache -rk2
|
||||||
|
```
|
||||||
|
|
||||||
|
### Sicherster Testlauf erst verschieben, dann löschen
|
||||||
|
|
||||||
|
Statt direkt zu löschen kann man Kandidaten in ein Sammelverzeichnis verschieben und
|
||||||
|
erst nach Prüfung endgültig entfernen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo paccache -m /tmp/pacman-cache-backup -rk2
|
||||||
|
du -sh /tmp/pacman-cache-backup
|
||||||
|
# nach Kontrolle:
|
||||||
|
rm -rf /tmp/pacman-cache-backup
|
||||||
|
```
|
||||||
|
|
||||||
|
### `--min-atime` vs. `--min-mtime`
|
||||||
|
|
||||||
|
Diese Optionen durchbrechen das `-k`-Limit: alles, was jung genug ist, bleibt erhalten.
|
||||||
|
Auf einem **`noatime`**-Mount ist `--min-atime` jedoch unzuverlässig, weil Lesezugriffe
|
||||||
|
die Zugriffszeit nicht mehr aktualisieren – dort misst atime faktisch nur den letzten
|
||||||
|
Schreibzugriff. In dem Fall `--min-mtime` verwenden. Siehe
|
||||||
|
[[IT-Know-How/Linux/BASH Basics/find]].
|
||||||
|
|
||||||
|
### Gemessene Einsparung (System dieses Nutzers, 66 GB Cache)
|
||||||
|
|
||||||
|
| Aufruf | Kandidaten | Freier Speicher |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `paccache -rk2` (2 Versionen behalten) | 4332 | **40,3 GiB** |
|
||||||
|
| `paccache -ruk0` (nur installierte behalten) | 99 | 12,8 GiB |
|
||||||
|
| `paccache -rk0` (nichts behalten) | 7153 | 65,7 GiB |
|
||||||
|
|
||||||
|
**Empfehlung: `-rk2`.** Es erhält zwei Versionen pro Paket und damit die Möglichkeit,
|
||||||
|
ein Update zurückzunehmen. `-rk0` ist maximal gründlich, lässt aber kein Downgrade zu –
|
||||||
|
bei einem defekten Update dann nur noch der Weg über das Archiv.
|
||||||
|
|
||||||
|
> `-duk0` entfernt ausschließlich Pakete, die gar nicht mehr installiert sind – z. B.
|
||||||
|
> Rückstände nach `pacman -R`. Das ist die unkritischste Variante.
|
||||||
|
|
||||||
|
### Downgrade statt löschen
|
||||||
|
|
||||||
|
Solange die `.pkg.tar.zst` im Cache liegt, funktioniert ein Downgrade direkt:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo pacman -U /var/cache/pacman/pkg/paket-1.0.0-1-x86_64.pkg.tar.zst
|
||||||
|
```
|
||||||
|
|
||||||
|
Genau deshalb ist `-k 2` sinnvoller als `-k 0`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Automatisch aufräumen: paccache.timer
|
||||||
|
|
||||||
|
`paccache` bringt bereits einen **Systemd-Timer** mit, der auf diesem System
|
||||||
|
vorinstalliert, aber **deaktiviert** ist:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Status prüfen
|
||||||
|
systemctl is-enabled paccache.timer # → disabled
|
||||||
|
systemctl list-timers paccache.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
Einrichtung:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Timer aktivieren (läuft dann wöchentlich)
|
||||||
|
sudo systemctl enable --now paccache.timer
|
||||||
|
|
||||||
|
# 2. Verhalten festlegen: 2 Versionen behalten
|
||||||
|
sudo sed -i 's/^PACCACHE_ARGS=.*/PACCACHE_ARGS="-rk2"/' /etc/conf.d/pacman-contrib
|
||||||
|
|
||||||
|
# 3. Einmalig sofort ausführen
|
||||||
|
sudo systemctl start paccache.service
|
||||||
|
|
||||||
|
# 4. Nächsten Laufzeitpunkt ansehen
|
||||||
|
systemctl list-timers paccache.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
Die Argumente kommen aus der Environment-Datei `/etc/conf.d/pacman-contrib`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Standard: leer
|
||||||
|
PACCACHE_ARGS=
|
||||||
|
```
|
||||||
|
|
||||||
|
Wird sie nicht gesetzt, verwendet der Service nur `paccache -r` (Standardverhalten des
|
||||||
|
Tools). Deshalb lohnt sich das Setzen auf `-rk2`.
|
||||||
|
|
||||||
|
Der Service ist bereits sinnvoll vorkonfiguriert – `Type=oneshot`, `Nice=19`,
|
||||||
|
`CPUSchedulingPolicy=idle` sowie diverse Härtungsoptionen (`ProtectSystem=full`,
|
||||||
|
`ProtectHome=yes`, `NoNewPrivileges=yes`, …). Die Cache-Bereinigung läuft also mit
|
||||||
|
niedrigster Priorität und blockiert keine interaktive Arbeit.
|
||||||
|
|
||||||
|
`Persistent=true` ist im Timer gesetzt: War der Rechner ausgeschaltet, wird die
|
||||||
|
aufgeholte Ausführung beim nächsten Start nachgeholt.
|
||||||
|
|
||||||
|
### Rhythmus ändern
|
||||||
|
|
||||||
|
Standard ist `OnCalendar=weekly`. Für einen selteneren Durchlauf einen Drop-in anlegen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl edit paccache.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=
|
||||||
|
OnCalendar=monthly
|
||||||
|
```
|
||||||
|
|
||||||
|
Die **leere** `OnCalendar=` ist wichtig: sie setzt die vorhandene Liste zurück, sonst
|
||||||
|
hätte das System beide Zeitpläne (weekly *und* monthly) parallel.
|
||||||
|
|
||||||
|
Danach:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl cat paccache.timer # Drop-in prüfen
|
||||||
|
systemctl list-timers paccache.timer
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Warum kein Cron?
|
||||||
|
|
||||||
|
Für diese Aufgabe ist ein Cronjob die schlechtere Wahl:
|
||||||
|
|
||||||
|
1. **Root ohne interaktive Passwortabfrage.** Der Job bräuchte `sudo` im Befehl.
|
||||||
|
Ein `sudo`-Prompt in einer Cron-Umgebung schlägt still fehl – der Job sieht aus, als
|
||||||
|
liefe er, passiert aber nichts. Bei einem User-Cron zusätzlich: Cron läuft ohne
|
||||||
|
volles Terminal, `sudo` kann die Passwortabfrage oft nicht darstellen.
|
||||||
|
2. **Die systemd-Unit existiert bereits.** Sie ist sandboxed, priorisiert und
|
||||||
|
`Persistent`. Das in Cron nachzubauen hieße, es schlechter zu machen.
|
||||||
|
3. **Ausgabe geht verloren.** Bei `journalctl -u paccache.service` ist nachvollziehbar,
|
||||||
|
was gelöscht wurde – bei Cron nur in Logdateien.
|
||||||
|
|
||||||
|
Als Faustregel: **Was eine Aufgabe als Root und/oder regelmäßig braucht → systemd-Timer.
|
||||||
|
Was als normaler User und unregelmäßig läuft → Cron oder `systemd-run --user`.**
|
||||||
|
|
||||||
|
Für den AUR-Cache ist Automation ohnehin unnötig: `yay -Sc` räumt ihn auf, und er
|
||||||
|
belegt nur noch ~100 MB. Siehe [[IT-Know-How/Linux/yay Cache reparieren]].
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Siehe auch
|
||||||
|
|
||||||
|
- [[IT-Know-How/Linux/yay Cache reparieren]] - AUR-Cache, `.git`-Korruption durch Cleanup
|
||||||
|
- [[IT-Know-How/Linux/Paketinstallation in Linux-Distributionen]] - pacman/yay Grundbefehle
|
||||||
|
- [[IT-Know-How/Linux/BASH Basics/find]] - sichere `-delete`-Kommandos für Cache-Aufräumen
|
||||||
@@ -22,11 +22,50 @@ Fehlermeldung (Beispiel `supersonic-desktop`, August 2026):
|
|||||||
## Ursache
|
## Ursache
|
||||||
|
|
||||||
Der lokale Klon des AUR-Repos in yays Cache (`~/.cache/yay/<paket>`) ist **korrupt**.
|
Der lokale Klon des AUR-Repos in yays Cache (`~/.cache/yay/<paket>`) ist **korrupt**.
|
||||||
Typisch: der `.git`-Ordner ist unvollständig (z. B. fehlen `HEAD` oder `config`),
|
Typisch: der `.git`-Ordner ist unvollständig (z. B. fehlen `HEAD` oder `config`).
|
||||||
meist nach einem abgebrochenen Clone/Pull oder einer gestörten Synchronisation.
|
|
||||||
|
|
||||||
Das AUR-Paket selbst ist in Ordnung – nur die lokale Kopie ist kaputt.
|
Das AUR-Paket selbst ist in Ordnung – nur die lokale Kopie ist kaputt.
|
||||||
|
|
||||||
|
### Mögliche Auslöser
|
||||||
|
|
||||||
|
1. Abgebrochener Clone/Pull oder eine gestörte Synchronisation
|
||||||
|
2. **Aggressive Cache-Bereinigung** (realer Fall, September 2026) – siehe unten
|
||||||
|
|
||||||
|
### Ursache 2: Cache-Cleanup hat die `.git`-Metadaten gelöscht
|
||||||
|
|
||||||
|
Der AUR-Cache unter `~/.cache/yay` ist selbst nur ein Build-Cache. Wird er per
|
||||||
|
`find` mit Alterskriterium aufgeräumt, greift die Suche **auch in die `.git`-Ordner**
|
||||||
|
hinein und löscht dort genau die Dateien, die Git zum Erkennen eines Repos braucht:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 2026-04-24 ausgeführt – hat 24 AUR-Klone gleichzeitig zerstört
|
||||||
|
find ~/.cache -type f -atime +30 -delete
|
||||||
|
```
|
||||||
|
|
||||||
|
Ergebnis pro betroffenem Paket:
|
||||||
|
|
||||||
|
| Datei | Status | Warum |
|
||||||
|
|-------|--------|-------|
|
||||||
|
| `.git/HEAD` | **gelöscht** | seit dem Clone (13.02.) nicht mehr geschrieben → >30 Tage alt |
|
||||||
|
| `.git/config` | **gelöscht** | dito |
|
||||||
|
| `.git/description` | **gelöscht** | dito |
|
||||||
|
| `.git/hooks/*.sample` | gelöscht | dito |
|
||||||
|
| `.git/info/exclude` | gelöscht | dito |
|
||||||
|
| `.git/logs/**` (Reflog) | gelöscht | dito |
|
||||||
|
| `.git/index` | überlebt | beim Build am 19.04. neu geschrieben → <30 Tage |
|
||||||
|
| `.git/ORIG_HEAD` | überlebt | dito |
|
||||||
|
| `.git/objects/**` | überlebt | nur gelesen, nicht geschrieben |
|
||||||
|
| `.git/refs/**` | überlebt | dito |
|
||||||
|
|
||||||
|
**Warum genau `HEAD` und `config` fehlen:** Git erkennt ein Repository anhand von
|
||||||
|
`HEAD` + `objects` + `refs`. Fehlt `HEAD`, gilt der Ordner nicht mehr als Repository –
|
||||||
|
daher die Fehlermeldung, obwohl der Ordner und die Objekte noch da sind.
|
||||||
|
|
||||||
|
Der entscheidende Punkt: Auf einem **`noatime`-Mount** aktualisiert Lesen die Zugriffszeit
|
||||||
|
nicht mehr. `-atime +30` bedeutet dort also *"seit 30 Tagen nicht mehr **geschrieben**"* –
|
||||||
|
und nicht *"seit 30 Tagen nicht benutzt"*. Genau deshalb wurden auch Klone gelöscht,
|
||||||
|
die täglich gelesen wurden. Details: [[IT-Know-How/Linux/BASH Basics/find]].
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Diagnose
|
## Diagnose
|
||||||
@@ -39,6 +78,47 @@ git -C ~/.cache/yay/<paket> status
|
|||||||
|
|
||||||
Kommt dieselbe Meldung („Kein Git-Repository"), obwohl der Ordner existiert → Cache-Klon löschen.
|
Kommt dieselbe Meldung („Kein Git-Repository"), obwohl der Ordner existiert → Cache-Klon löschen.
|
||||||
|
|
||||||
|
Schnellcheck des `.git`-Inhalts:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls ~/.cache/yay/<paket>/.git
|
||||||
|
# Erwartet: HEAD config objects refs index ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Warum meldet `yay -Ss` keinen Fehler?
|
||||||
|
|
||||||
|
`yay -Ss` fragt die AUR-RPC-Schnittstelle per HTTP ab und braucht den lokalen Klon
|
||||||
|
dafür nicht. Erst `yay -S`/`-Syu` führt ein `git pull` im Cache-Ordner aus – daher
|
||||||
|
funktioniert die *Suche*, während das *Installieren/Updaten* fehlschlägt.
|
||||||
|
|
||||||
|
### Forensik: eigene Hausarbeit prüfen
|
||||||
|
|
||||||
|
Falls unklar ist, **wann** und **wodurch** die Klons zerstört wurden, hilft der
|
||||||
|
Dateisystem-Zeitstempel. Alle betroffenen `.git`-Ordner tragen denselben mtime,
|
||||||
|
weil ein einziger Prozess sie in einem Durchlauf angefasst hat:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Alle zerstörten Klons finden
|
||||||
|
find ~/.cache/yay -mindepth 2 -maxdepth 2 -type d -name .git \
|
||||||
|
! -exec test -f '{}/HEAD' \; -printf '%h\n'
|
||||||
|
|
||||||
|
# mtime vergleichen (hier: alle exakt 2026-04-24 12:29:16)
|
||||||
|
find ~/.cache/yay -mindepth 2 -maxdepth 2 -type d -name .git \
|
||||||
|
-printf '%T+ %h\n' | sort
|
||||||
|
```
|
||||||
|
|
||||||
|
Der Zeitstempel lässt sich gegen die Shell-History abgleichen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# fish: Epoch-Sekunden der History-Einträge umrechnen
|
||||||
|
grep -n 'atime' ~/.local/share/fish/fish_history
|
||||||
|
date -d @1777026555 # → 2026-04-24 12:29:15 CEST
|
||||||
|
```
|
||||||
|
|
||||||
|
Ein im Nanosekunden-Bereich aufeinanderfolgender Versatz zwischen den Verzeichnissen
|
||||||
|
(`.4006957730`, `.4016957760`, …) bestätigt einen einzelnen `find`-Durchlauf statt
|
||||||
|
manueller Eingriffe.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Lösung
|
## Lösung
|
||||||
@@ -54,6 +134,27 @@ yay klont das PKGBUILD-Repo beim nächsten Lauf frisch – danach funktioniert d
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Alle betroffenen Pakete auf einmal reparieren
|
||||||
|
|
||||||
|
Sind mehrere Klons betroffen (z. B. nach einem pauschalen Cleanup), alle auf einmal
|
||||||
|
finden und entfernen. `-printf '%h'` liefert den übergeordneten Paketordner, `%h\0`
|
||||||
|
plus `xargs -0` ist gegen Leerzeichen in Pfaden sicher:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Erst ansehen – nur anzeigen, nicht löschen
|
||||||
|
find ~/.cache/yay -mindepth 2 -maxdepth 2 -type d -name .git \
|
||||||
|
! -exec test -f '{}/HEAD' \; -printf '%h\n'
|
||||||
|
|
||||||
|
# 2. Dann entfernen
|
||||||
|
find ~/.cache/yay -mindepth 2 -maxdepth 2 -type d -name .git \
|
||||||
|
! -exec test -f '{}/HEAD' \; -printf '%h\0' | xargs -0 rm -rf
|
||||||
|
```
|
||||||
|
|
||||||
|
Betroffen sind nur unbrauchbare Klone. yay klont sie beim nächsten Build neu;
|
||||||
|
installierte Pakete bleiben unberührt.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Allgemein: yay-Cache aufräumen
|
## Allgemein: yay-Cache aufräumen
|
||||||
|
|
||||||
| Vorgehen | Befehl | Folge |
|
| Vorgehen | Befehl | Folge |
|
||||||
@@ -64,3 +165,68 @@ yay klont das PKGBUILD-Repo beim nächsten Lauf frisch – danach funktioniert d
|
|||||||
|
|
||||||
**Faustregel:** Immer erst den einzelnen Paketordner löschen. Den kompletten Cache nur leeren,
|
**Faustregel:** Immer erst den einzelnen Paketordner löschen. Den kompletten Cache nur leeren,
|
||||||
wenn mehrere Pakete betroffen sind – er enthält nichts Kritisches, kostet aber Rebuild-Zeit.
|
wenn mehrere Pakete betroffen sind – er enthält nichts Kritisches, kostet aber Rebuild-Zeit.
|
||||||
|
|
||||||
|
| Befehl | Wirkung |
|
||||||
|
| --- | --- |
|
||||||
|
| `yay -Sc` | Entfernt Build-Artefakte (untracked/gitignored) aus den Paketordnern, fragt zusätzlich nach dem Pacman-Cache |
|
||||||
|
| `yay -Scc` | Wie `-Sc`, leert aber **alle** AUR-Build-Ordner vollständig (inkl. `.git`) |
|
||||||
|
| `paccache -rk2` | Pacman-Cache aufräumen, 2 Versionen behalten – siehe [[IT-Know-How/Linux/Pacman Paketcache verwalten]] |
|
||||||
|
|
||||||
|
> **Vorsicht bei `yay -Scc`:** Nimmt auch die `.git`-Ordner mit, d. h. alles wird beim
|
||||||
|
> nächsten `-Syu` neu geklont. Für einen Cache dieser Größe unkritisch, aber langsam bei
|
||||||
|
> vielen AUR-Paketen mit langen Buildzeiten.
|
||||||
|
|
||||||
|
### Was der AUR-Cache tatsächlich belegt
|
||||||
|
|
||||||
|
Ein Build hinterlässt je Paket teure Dateien im Cache-Ordner: das heruntergeladene
|
||||||
|
Quell-Archiv bzw. AppImage und das fertige `.pkg.tar.zst`. Im September 2026 belegten
|
||||||
|
vier alte LM-Studio-Versionen allein 7,1 GB (4 × AppImage + 4 × fertiges Paket).
|
||||||
|
|
||||||
|
Diese Artefakte sind exakt das, was die `.gitignore` der AUR-Repos ausschließt
|
||||||
|
(`*.AppImage`, `*.pkg.tar.zst`, `src/`, `pkg/`, `*.tar.gz`, …) – deshalb entfernt
|
||||||
|
`yay -Sc` / `git clean -xdf` sie zuverlässig.
|
||||||
|
|
||||||
|
Gezielt aufräumen – Prüfen und Löschen getrennt, weil `-delete` und `-prune` nicht
|
||||||
|
kombinierbar sind:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Prüfen: Anzahl und Größe (darf -prune verwenden, da nur -print)
|
||||||
|
find ~/.cache/yay -name .git -prune -o -type f \
|
||||||
|
\( -name '*.pkg.tar.zst' -o -name '*.AppImage' -o -name '*.deb' \
|
||||||
|
-o -name '*.tar.gz' -o -name '*.tar.xz' \) -printf '%s\n' \
|
||||||
|
| awk '{s+=$1;n++} END {printf "%d Dateien, %.2f GB\n", n, s/1024/1024/1024}'
|
||||||
|
|
||||||
|
# 2. Löschen: OHNE -prune, stattdessen .git über -not -path ausnehmen
|
||||||
|
find ~/.cache/yay -type f \
|
||||||
|
\( -name '*.pkg.tar.zst' -o -name '*.AppImage' -o -name '*.deb' \
|
||||||
|
-o -name '*.tar.gz' -o -name '*.tar.xz' \) \
|
||||||
|
-not -path '*/.git/*' -delete
|
||||||
|
```
|
||||||
|
|
||||||
|
> **Warum zwei Befehle?** `-delete` schaltet intern auf `-depth` um, und `-prune`
|
||||||
|
> ist dann wirkungslos – der `.git`-Schutz fällt weg. Zum Zählen/Lösen ist `-prune`
|
||||||
|
> mit `-print` ok, beim Löschen aber durch `-not -path '*/.git/*'` ersetzen. Details:
|
||||||
|
> [[IT-Know-How/Linux/BASH Basics/find]].
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prävention
|
||||||
|
|
||||||
|
Damit das nicht noch einmal passiert:
|
||||||
|
|
||||||
|
1. **Kein `-delete` über `~/.cache` ohne `.git`-Ausschluss.** Immer `-not -path '*/.git/*'`
|
||||||
|
verwenden, niemals `-prune` (das ist unter `-delete` wirkungslos).
|
||||||
|
2. **Besser gar nicht pauschal aufräumen.** `~/.cache/yay` ist ein reiner Build-Cache und
|
||||||
|
enthält nichts Unwiederbringliches – `yay -Sc` ist der vorgesehene Weg.
|
||||||
|
3. **Auf `noatime`-Mounts `-atime` nicht als „letzte Nutzung" lesen.** Vorher prüfen:
|
||||||
|
`findmnt -T /home`
|
||||||
|
4. **Regelmäßig `paccache -rk2`** – der Pacman-Cache wächst unbegrenzt und war mit
|
||||||
|
66 GB der eigentliche Plattenfresser (siehe [[IT-Know-How/Linux/Pacman Paketcache verwalten]]).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Siehe auch
|
||||||
|
|
||||||
|
- [[IT-Know-How/Linux/BASH Basics/find]] - `-delete`/`-prune`-Konflikt und `noatime`
|
||||||
|
- [[IT-Know-How/Linux/Pacman Paketcache verwalten]] - `paccache` und der Pacman-Cache
|
||||||
|
- [[IT-Know-How/Linux/Paketinstallation in Linux-Distributionen]] - pacman/yay Grundbefehle
|
||||||
|
|||||||
Reference in New Issue
Block a user