Dotfiles mit GNU stow verwalten

Christian “strcat” Schneider

01-02.2025

Vorwort

Solltet ihr Fragen haben oder einen Fehler entdeckt haben, dann schickt mir einfach eine Mail (eigenes@strcat.de) und ich werde mich darum kuemmern. Gleiches gilt auch wenn ihr Verbesserungsvorschlaege oder Distrubtions-spezifische Hinweise habt.

Einleitung

GNU stow bezeichnet sich selbst als Symlink-Farm-Manager, was bedeutet es werden symbolische Links erstellt und diese dann verwaltet. In diesem Tutorial wird er dazu verwendet um Dotfiles zu verwalten und bei GitHub hochzuladen, wobei bestimmte Dateien ausgeschlossen werden (weil sie z. B. private Informationen enthalten).

Installation

Die Installation ist “stow” ist relativ einfach; entweder man installiert es mit dem Paketmanager seiner Distribution oder man besorgt sich die Sourcen. Übersetzt werden die Sourcen dann mit dem allseits bekannten

$ ./configure
$ make
$ make install

Wenn man sich die Quelltexte vom GIT-Archive holt, läuft das etwas anders ab.

$ git clone https://git.savannah.gnu.org/git/stow.git
$ cd stow
$ autoreconf -iv
$ ./configure && make && make install

Genaueres steht aber in der INSTALL.md. Wenn man “stow” in Verbindung mit einer VCS1 einsetzen will, muss man diese ebenfalls noch installieren. Da das aber den Rahmen dieses Tutorials sprengen würde, gehe ich darauf nicht ein, sondern setze jetzt einfach mal voraus, das git(1) installiert ist.

Dateien/Verzeichnisse hinzufügen

“stow” legt wie schon erwähnt, symbolische Links an und diese brauchen zum einen eine Quelle, zum zweiten ein Ziel das noch nicht existiert und zum dritten eine exakte Verzeichnisstruktur. Das hört sich kompliziert an, aber ist relativ simpel. Zuerst erstellt man ein leeres Verzeichnis; ich verwende dazu einfach

$ mkdir dotfiles

In dieses Verzeichnis kopiert bzw. verschiebt man dann alle Dateien, die von “stow” verwaltet werden sollen. Wenn man sie lediglich kopiert, muss man die ursprüngliche Datei umbenennen, da sonst kein symbolischer Link erstellt werden kann. Ein

$ mv ~/.zshrc ~/dotfiles/

ist völlig ausreichend. Wenn stattdessen

$ cp ~/.zshrc ~/dotfiles/

verwendet, funktioniert es nicht. Hier muss man die ~/.zshrc erst verschieben, umbenennen oder löschen.

Verzeichnisse hinzufügen

Weiterhin muss man auch die Hierarchie beachten und diese exakt in das Verzeichnis ~/dotfiles/ übertragen. Wenn man z. B. die Datei ~/.config/nvim/lua/plugins/mini.lua an “stow” übergeben will, muss man zuerst das entsprechende Verzeichnis in ~/dotfiles/ erstellen. Also

$ mkdir -p ~/dotfiles/.config/nvim/lua/plugins/
$ mv ~/.config/nvim/lua/plugins/mini.lua ~/dotfiles/.config/nvim/lua/plugins

Kurz gesagt, so wie die Dateien im Homedirectory lagen, müssen sie auch im Verzeichnis ~/dotfiles/ liegen. Das wiederholt man dann für alle Dateien, die man von “stow” verwaltet haben will. Wenn man das erledigt hat, kann man “stow” initiieren. Das geschieht mit dem Aufruf stow . im Verzeichnis ~/dotfiles/ und fertig.

Versionsverwaltung mit git

Es macht Sinn wenn man “stow” mit git(1) kombiniert, was sehr einfach geht. Zuerst machen wir aus ~/dotfiles/ ein Git-Repo, in dem wir

$ cd ~/dotfiles/
$ git init .
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint:   git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint:   git branch -m <name>
Initialized empty Git repository in /home/user/dotfiles/.git/

eingeben. Danach muss man noch die Dateien hinzufügen, was mit

$ git add .zshrc
$ git commit -m "Initial commit"
# Alternativ dazu kann man auch alle Dateien auf einmal hinzufügen, 
# indem man einfach folgendes Kommando verwendet
$ git add --all

gemacht wird. Wenn man jetzt ein ls -a eingibt, wird man ein neues Verzeichnis Namens .git vorfinden, welches von git(1) erstellt wurde. Im Normalfall würde auch dieses Verzeichnis bei Github hochgeladen werden, aber dafür besitzt “stow” eine Ignore-List, die wie folgt aussieht und aufgebaut ist:

# Comments and blank lines are allowed.

RCS
.+,v

CVS
\.\#.+       # CVS conflict files / emacs lock files
\.cvsignore

\.svn
_darcs
\.hg

\.git
\.gitignore
\.gitmodules

.+~          # emacs backup files
\#.*\#       # emacs autosave files

^/README.*
^/LICENSE.*
^/COPYING

Alle darin enthaltenen Dateien werden nicht von “stow” in das Repo eingepflegt. Die Datei muss man nicht erstellen, da sie per Default verwendet wird. Es gibt auch noch zwei weitere Dateien die von “stow” abgearbeitet werden. Zum einen die .stow-local-ignore und zum zweiten die ~/.stow-global-ignore. Die stow.-local-ignore wird zuerst abgearbeitet; die Datei kann man einfach erstellen und in dem Verzeichnis ablegen, in dem sie gelten soll. Wenn die nicht vorhanden ist, wird als zweites ~/.stow-global-ignore verarbeitet und erst danach die implementierte Ignore-List.
Wichtig: Wenn man eine von beiden Dateien erstellt, dann gelten nur die darin enthaltenen Angaben. D. h. es bietet sich an die Datei einfach zu erweitern:

# Private Dateien
\.privat

# Passwörter
\.passwords

RCS
.+,v
....

Wenn man anschließend ~/dotfiles/ z. B. auf Github hochlädt, werden alle darin angegebenen Dateien ignoriert. Das hat den großen Vorteil, das man private bzw. geheime Daten nicht öffentlich macht. Ich habe z. B. bei NeoMutt die Datei ~/.mutt/mutt.aliases, in der alle Emailaliase stehen; die will ich selbstverständlich nicht bei Github hochladen. Dazu reicht es die Datei stow-local-ignore um den Eintrag mutt.aliases zu erweiten und die Datei wird ignoriert.

Optionen von “stow”

“stow” hat relativ wenig Optionen zur Auswahl, welche aber letztendlich ausreichend sind.

-d DIR / –dir=DIR
Setzt das Verzeichnis von “stow”; in unserem Beispiel wäre das ~/dotfiles/ und Default ist das aktuelle Verzeichnis
-t DIR / –target=DIR
Hier wird das Zielverzeichnis gesetzt; also das, in welches die symbolischen Links zeigen sollen. In unserem Beispiel wäre das ${HOME}, welches auch Default ist
-S / –stown
Hier kann man die Dateinamen angeben, die hinzugefügt werden sollen
-D / –delete
Hier werden die Dateinamen angegeben, die gelöscht werden sollen
-R / –restow
Ein Shortcut für stow -D && stow -S
–ignore=REGEX
Hiermit werden die Dateien ignoriert, die von dem angegebenen regulärem Ausdruck erfasst werden
–defer=REGEX
Hiermit werden Dateien mit dem regulärem Ausdruck ignoriert, wenn die Datei bereits von “stow” verwaltet wird.
–override=REGEX
Genau das Gegenteil zu --defer. Selbst wenn eine Datei schon von “stow” verwaltet wird, wird sie überschrieben wenn sie von der Regex erfasst wird
–dotfiles
Wenn man diese Option verwendet, wird aus ~/dotfiles/.zshrc ein ~/dotfiles/dot-zshrc
–adopt

Hier sollte man Vorsicht walten lassen. Wenn man eine Datei “stow”en will, deren Ziel bereits existiert, dann erhält man folgende Fehlermeldung:

WARNING! stowing . would cause conflicts:
  * existing target is neither a link nor a directory: ./config/kitty/kitty.conf
All operations aborted.
Mittels --adopt werden alle Problemdateien in das Verzeichnis von “stow” verschoben. Die Datei ~/dotfiles/.config/kitty/kitty.conf wird dann jedoch von der ~/.config/kitty/kitty.conf überschrieben!
-n / -no / –simulate
Sollte klar sein: Hier wird das Kommando ausgeführt, aber keine Änderungen im Dateisystem vorgenommen
-v / –verbose[=N]
Hier kann man das Verbose-Level zwischen 0 bis 5 ändern
-p / –compat
Hiermit werden alle Dateien im Verzeichnis von “stow” gescannt; normalerweise werden nur Verzeichnisse abgearbeitet
-V / –version sowie -h / –help
Das sollte klar sein

  1. Versionsverwaltung aka. Version Control System wie z. B. git, cvs, rcs, svn, ..↩︎