A kiindulás
Évek óta egy ~/.files mappában tartom a konfigurációimat. Bash, Git, Neovim, Starship, GTK témák — minden itt él, egy git repóban. Eddig egy saját symlink.sh script gondoskodott arról, hogy a rendszer a megfelelő helyeken találja őket.
A script jól működött. Idempotens volt, tudott --remove módot, backupolta a meglévő fájlokat mielőtt felülírta volna őket. Éveken át nem okozott gondot.
De volt néhány dolog, ami zavart.
Minden egyes alkalommal, amikor egy új konfigot akartam kezelni, meg kellett nyitnom a scriptet és hozzáadni egy újabb sort a LINKS tömbhöz. A Hyprland és a Studio konfig külön install scriptekkel rendelkezett, mert az eredeti script nem volt elég rugalmas hozzájuk. Három különböző mechanizmus, három különböző hely — és ha új gépre kellett telepíteni, három különböző parancsot kellett futtatni a megfelelő sorrendben.
Tudtam, hogy létezik a GNU Stow, de sokáig nem volt elég nyomás arra, hogy átálljak. Most viszont a tmux és a mise (programozási nyelvek kezelője) konfigját is verziókezelni akartam, és éreztem, hogy a régi módszer nem skálázódik tovább.
Volt egy másik szempont is, ami felgyorsította a döntést. Az utóbbi időben egyre többet dolgozom LLM-ekkel — konfig fájlok írása, scriptek debugolása, install folyamatok megtervezése. Egy-egy ilyen munkamenet során nagyon gyorsan születnek új fájlok, módosulnak meglévők, kerülnek be új eszközök a rendszerbe. Ha a dotfile-kezelés maga is egy egyedi script, amit minden alkalommal kézzel kell bővíteni, az lassítja a tempót. Egy szabványos, jól ismert eszköz — amit az LLM is ismer, amit a dokumentáció is lefed — sokkal jobban illeszkedik ebbe a munkafolyamatba. A stow pont ilyen: nincs benne egyedi logika amit el kéne magyarázni, nincs rejtett állapot amit karban kéne tartani. A struktúra maga a konfiguráció, és aki ránéz — ember vagy gép — azonnal érti, mi hová kerül.
Mi az a GNU Stow?
A GNU Stow egy egyszerű eszköz, ami symlinkeket hoz létre egy mappából a szülő könyvtárba, tükrözve a belső struktúrát. Ennyi. Nincs benne konfig fájl, nincs daemon, nincs adatbázis.
A trükk az, hogy a dotfile repódon belül minden konfig egy külön „package" mappában él, és azon belül pontosan úgy néz ki a struktúra, ahogy a $HOME-ban kellene. Például:
~/.files/
├── bash/
│ └── .bashrc → ~/.bashrc
├── nvim/
│ └── .config/
│ └── nvim/
│ └── init.lua → ~/.config/nvim/init.lua
├── starship/
│ └── .config/
│ └── starship.toml → ~/.config/starship.toml
A telepítés ennyi:
cd ~/.files
stow bash nvim starship
És kész. A stow végigmegy a package mappákon, és minden fájlhoz létrehozza a megfelelő symlinket a $HOME-ban.
Az eltávolítás ugyanilyen egyszerű:
stow -D bash nvim starship
A tervezés
Mielőtt bármit mozgattam volna, végigmentem azon, hogy mi hová kell kerüljön. Nem minden konfig alkalmas stow-ra — a lényeg az, hogy a stow csak a $HOME-ba tud linkelni. Ami /etc/ alá megy (például a realtime audio jogosultságok), azt továbbra is kézzel kell kezelni.
Három kategóriát azonosítottam:
Stow package-ek — ezek a konfig fájlok, amik a $HOME-ba vagy a ~/.config/-ba kerülnek. Bash, Git, Starship, Neovim, GTK, mise, tmux.
Kézzel kezelt — SSH config (jogosultsági okokból), scriptek (PATH-ba téve a mappa), hledger journal-ök. Ezek a repóban maradnak, de nem a stow linkeli őket.
Külön telepítők — Hyprland és Studio konfig, amiknek saját install scriptjeik vannak. Ezek egyelőre maradtak ahogy voltak.
A tervezésnél az is fontos volt, hogy az átrendezésnél git mv-t használjak mindenhol — így a git history megmarad, és utólag is látom, honnan hová került egy fájl.
A migráció
Az átállás lényegében három lépésből állt.
Először a régi symlinkek eltávolítása. A symlink.sh által létrehozott hat link törlése — .bashrc, .gitconfig, .config/nvim, .config/starship.toml, .config/gtk-3.0, .config/gtk-4.0. Ez önmagában ártalmatlan művelet, hiszen az eredeti fájlok a repóban maradnak.
Másodszor a fájlok átrendezése a stow struktúrába. Például a bashrc fájl átkerült bash/.bashrc-be, a gitconfig a git/.gitconfig-be, a starship.toml a starship/.config/starship.toml-be.
A Neovim mappánál volt egy trükk: a git mv nvim nvim/.config/nvim nem működik, mert nem lehet egy mappát önmagába mozgatni. A megoldás egy ideiglenes név használata — nvim-stow —, majd átnevezés a véglegesre.
A mise konfig máshogy működött, mint a többi. Ez nem volt a régi repóban, hanem valódi fájlként élt a ~/.config/mise/config.toml-ban. Bemásoltam a repóba a mise/.config/mise/config.toml helyre, töröltem az eredetit, és innentől a stow linkeli.
Harmadszor az új install.sh létrehozása. Ez egy egyszerű wrapper a stow körül:
#!/usr/bin/env bash
set -euo pipefail
FILES_DIR="$(cd "$(dirname "$0")" && pwd)"
cd "$FILES_DIR"
ALL_PACKAGES=(bash git starship nvim gtk mise tmux)
for pkg in "$@"; do
# ha van argumentum, csak azokat telepíti
done
# Ha nincs argumentum, mindet telepíti
for pkg in "${ALL_PACKAGES[@]}"; do
stow -v --no-folding -t "$HOME" "$pkg"
done
A --no-folding flag fontos. Enélkül a stow az egész .config mappát egyetlen symlinkre cserélné, ami azt jelentené, hogy más programok a repómba írnának ha a .config-ba akarnak írni. A --no-folding arra kényszeríti a stow-t, hogy fájlonként linkeljen.
Buktatók
Néhány dolog, amire érdemes figyelni.
A git config csapdája. A migráció első lépése a régi symlinkek törlése. De ha a .gitconfig symlinket törlöd, a git nem fogja tudni, ki vagy — nem tud commitolni. Megoldás: a git package-et stow-old először, utána commitolj.
GTK bookmarks. A GNOME fájlkezelő közvetlenül írja a ~/.config/gtk-3.0/bookmarks fájlt. Ha a stow --no-folding módban fut, ez a fájl valódi fájlként megmaradhat, és a stow nem fogja tudni felülírni symlinkkel. Ilyenkor kézzel kell a fájlt törölni (a tartalmat előtte a repóba másolva), majd újra stow-olni.
Az SSH config kimarad. Az SSH mappa jogosultságokra kényes — a ~/.ssh mappának 700-nak, a config fájlnak 600-nak kell lennie. A symlink megoldás működik, de én úgy döntöttem, hogy az SSH config marad kézzel linkelve, a stow-on kívül.
Az eredmény
A végső struktúra:
~/.files/
├── bash/ ← stow package
├── git/ ← stow package
├── starship/ ← stow package
├── nvim/ ← stow package
├── gtk/ ← stow package
├── mise/ ← stow package
├── tmux/ ← stow package
│
├── ssh/ ← kézzel linkelve
├── scripts/ ← PATH-ba téve
├── hyprland-werkstatt/ ← külön telepítő
├── studio/ ← külön telepítő
│
└── install.sh ← stow wrapper
Új package hozzáadása: csinálsz egy mappát a megfelelő belső struktúrával, hozzáadod az ALL_PACKAGES tömbhöz, futtatod a stow packagenév parancsot.
Új gépre telepítés: klónozod a repót, ./install.sh, kész.
Tanulság
A GNU Stow nem old meg bonyolult problémákat, mert a dotfile-kezelés nem bonyolult probléma. Az egyetlen dolog, amit csinál: a mappastruktúrát symlinkekre fordítja. Nincs benne konfig fájl, nincs állapot, nincs mágia.
Pontosan ettől jó.
A régi symlink.sh scriptem jól működött, de minden módosításnál a scriptet kellett szerkeszteni. A stow-val a mappastruktúra maga a konfiguráció. Ha egy fájl a bash/.bashrc helyen van, a stow tudja, hogy ~/.bashrc-nek kell lennie. Nincs mit elrontani, nincs mit elfelejteni.
Ha te is kézzel kezeled a dotfile-jaidat — legyen az symlinkekkel, install scripttel vagy egyszerű másolgatással — a stow megéri a húsz percet, amit az átállás igényel.
Fedora alatt:
sudo dnf install stow
A többit megcsinálja a mappastruktúra.