.TL Managing Dotiles: A Hard Problem .AU tropf .AB notes on thoughts on dotfile management .AE . .NH 1 Intro .NH 2 The Problem .PP I use many programs on many machines, which I customized to a certain extent. These customizations are mostly the same everywhere, but not always. And I want to synchronize those configurations. .PP There are many "solutions" to this synchronization, though not a single one really feels like it covers all my usecases. . .NH 2 There are two types of... .PP ...classifications for changes: By origin (automated vs manual) and scope (global vs local). Automated refers to "by a program", while manual means "by hand"; obviously no configuration change is purely automated. Unfourtunately, all of the combinations exist and occur in my setups: . .TS nospaces tab(|) center; l l l. | local | global automated | cache paths | emacs packages manual | key file paths | keybinds .TE .PP And as all these cases exist, there is no simple way to classify new changes. .PP For Example, most programs allow for a very simple way to separate global and local changes: Just use two files, e.g. \fC.vimrc.local\fR and \fC.vimrc.global\fR. But now we are missing the \fC.vimrc\fR file itself, and therefore automated changes can't be handled. .PP However these classification gets implemented, for some cases the specific implementation will have severe drawbacks (sync everything: too many conflicts; flag changes to sync by hand: too cumbersome...) ultimately making the answers to these theoretical questions less important. . .NH 2 Heterogeneous Systems .PP I use a vastly different systems regularly, each with their own peculiarities (no root access, different software versions, available performance, no GUI available etc.) which I want to address. For a while I had multiple sets of dotfiles, but I've chosen the easy way out and found solutions for each of my problems: .ULS .LI Light- vs. Heavyweight editor: vim for the former, emacs for the latter .LI Installation of companion programs: just add some notes in the config, and let future me figure it out for the specific system .LI If feasable: programmatic detection, e.g. "if this is established via SSH start a tmux session" .ULE .PP As a result I now only have one set of dotfiles which makes things much more simple. .PP But this environment actually places a much more strict restriction on my dotfile distribution: It must be portable (and preferably lightweight). . .NH 1 Approaches .NH 2 Use Git! .PP Do I even need to say this? .NH 2 Symlinking .PP To allow easy global propagation of local (and automated) changes, just symlink the synchronized versions of the files into your home directory. .PP This makes keeping local changes more difficult, as the now global dotfile has to check (1) are there local settings and (2) include those local settings. .NOTE The other way around (symlink real dotfile into git) does not work: git will only sync the link, but not the content. . .NH 2 (Read-Only-) Copy .PP If you don't need automated changes, use read-only copies of your dotfiles. Which really screws with your ability to do anything automated. . .NH 2 Injecting Includes .PP For a long time my preferred way to use dotfiles: Inject a link to your global version into the current dotfile, e.g. \fCsource ~/a/b/c/bashrc.global\fR. .PP This is special for each config format and therefore requires some binary during the installation. . .NH 2 *Just* use git .PP .CB cd ~ echo '*' > .gitignore git init .CE . .PP It works, but it has no additional features whatsoever. . .NH 1 My Requirements .PP After thinking about possible solutions to my theoretical problems, I came down to these requirements: My dotfile management should... .ULS .LI Allow for local AND global changes. .LI Be available on all platforms. .LI Be self-contained (no dependencies unless \fBwidely\fR available). .LI Synchronize via git. .LI Be able to undo all its changes. .ULE . .PP Most dotfile tools have to be installed to apply their changes, which rules them out for my setups. But not using these tools means not using any of their useful features. .PP Ultimately I (and thereby you) have to pick my poison. No matter what, you will have to compromise on some factor. . .NH 1 My Approach .PP I thought long and hard, and because I hate dependencies and myself, I now just use a single git repo as .URL "https://www.atlassian.com/git/tutorials/dotfiles" "described here" . . .PP The basic idea is to initialize \fC$HOME\fR as a git repo. This introduces challenges of its on, but those can be dealt with. . .NH 2 Clever Details .PP One of the main problems I was concerned about when initializing my home as a git repo was that I could accidentally mean to add something to a repo, and maybe even sensitive files. However, this can't happen: Instead of using \fC.git\fR for files provided by git I use \fC.dots\fR – and therefore normal git invocations don't recognize the home directory as a git repo. If I need to interact with this git repo I use \fCgit --git-dir=$HOME/.dots/ --work-tree=$HOME\fR which I created an alias for. .PP Also instead of a \fC.gitignore\fR I use the file \fC~/.dots/info/exclude\fR. This is not under version control itself, but removes the necessity for a \fC.gitignore\fR file in my home. .PP By Setting \fCstatus.showUntrackedFiles\fR to \fCno\fR I prevent git annoying me that not everything inside my home is under version control. . .NH 2 Advantages .PP This is as portable as it gets. If git works, this approach works. .PP Also it is dead simple to understand: Dotfiles are copied as is. No funky magic. .PP Reverting everything is fairly simple as it just requires removing all existing dotfiles. Also git allows for more complex reset procedures. However, removing the managed dotfiles means removing your dotfiles alltogether – w/o additional work also local changes are not respected anymore. . .NH 2 Drawbacks .PP You can't mix global, version-controlled and local sections in the same file. This sometimes becomes a problem when tools automatically try to change your dotfiles. I mitigate this by using pointing to local versions of my dotfiles: E.g. my \fC~/.bashrc\fR contains this snippet: . .CB if test -r ~/.bashrc.local; then source ~/.bashrc.local fi .CE .PP I move all local changes into my \fC~/.bashrc.local\fR and keep my global dotfiles clean. Similar inclusion mechanisms exist for at least ssh, tmux and git config files. However, this requires a manual intervention for each inclusion in every dotfile. . .PP For documentation purposes my dotfiles repo contains a readme file. This means that my home directory has a single file named \fCREADME.md\fR, with all other contents being directories. I consider that not ideal, as this readme file is located in my home but has no connection to it – though that is a drawback I'm willing to accept. . .NH 2 Summary .PP Follow the guide linked above. Create an alias to \fCgit --git-dir=$HOME/.dots/ --work-tree=$HOME\fR. On a new machine use: . .CB git clone --bare $HOME/.dots alias dotscfg='git --git-dir=$HOME/.dots/ --work-tree=$HOME' dotscfg config --local status.showUntrackedFiles no echo '*' >> ~/.dots/info/exclude dotscfg checkout .CE . .PP Use this alias instead of the \fCgit\fR command when interacting with the repo. Use the flag \fC-f\fR (force) to add files. . .NH 1 See also .PP .ULS .LI .URL "https://www.atlassian.com/git/tutorials/dotfiles" "The best way to store your dotfiles" : approach I use .LI .URL "https://drewdevault.com/2019/12/30/dotfiles.html" "Managing my dotfiles as a git repository" : Blog Post by Drew DeVault on using plain git to sync dotfiles .LI .URL "https://github.com/nix-community/home-manager" "nix home manager" : preferred way to manage dotfiles by the nix community .LI .URL "https://dotfiles.github.io/" "dotfiles.github.io" : list of dotfile managers .LI .URL "https://help.github.com/articles/remove-sensitive-data/" "how to remove sensitive data from git" : required after screwups .ULE