Manage dotfiles with the convenience of git
posted on 24 June 2023 • 6 minute readIn 2019 I discovered the concept of managing your Linux terminal configuration using git. This concept is also known as dotfiles. When I first read this, I liked the idea because I regularly switch between computers. Furthermore, I have the habit of breaking my distro whenever I’m trying out something new π. Luckily it is becoming more and more stable in recent years.
So wouldn’t it be nice if we could reinstall our terminal configuration with the ease of a few commands? It is if you ask me. In this blog post, I’ll take you along my journey of managing dotfiles with the convenience of git.
Background
First start with a little bit of background. I’m working on multiple computers and with multiple operating systems. At the time of writing this post, I’m mainly using Fedora (Linux) and Windows with WSL enabled. That is why I needed to share my terminal configuration across multiple computers and operating systems. I use a minimal set of dependencies, which are Oh My ZSH, vim, and tmux.
So in the most minimal form, my home directory structure will look like:
/home/dylan/
|-- .oh-my-zsh/
|-- .tmux/
|-- .vim/
|-- .zshenv
|-- .zshrc
|-- dotfiles/
Bare git repository
The first article I stumbled upon was about managing dotfiles with a bare git repository. So I tried this out, and I have to say it was working quite well in the beginning. The idea behind the bare git repository is turning your home directory (~/
) into a git repository. You can achieve this by executing the following command:
git init --bare $HOME/.dotfiles.git
Now we have one problem, how to interact with the bare repository created? You can do this by providing the --git-dir
and --work-tree
flags to the git command. This is still not as convenient as I like it to be. As an improvement, we can add an alias in the rc file (e.g. .zshrc
):
echo 'alias dotfiles="/usr/bin/git --git-dir=$HOME/.dotfiles.git/ --work-tree=$HOME"' >> $HOME/.zshrc
Now we can interact with the dotfiles repository using the dotfiles <git-command>
, for example, to commit a file:
dotfiles .zshrc -m "Add dotfiles git alias"
To get started on a different computer. We can simply recreate the alias and clone the repository into the home directory using the following command:
git clone --bare <repo-url> $HOME/.dotfiles.git
Regular git repository is the way to go
The process of managing dotfiles described in the previous section worked quite well for a while, but it has its limitations. The most annoying one is that every file in the home directory could accidentally be checked-in to git. This can be avoided by creating an allowlist using the .gitignore
file. But still, this didn’t work the way I wanted it to. That is why I started to turn my dotfiles repository into a regular git repository.
The start was simple. I created a new directory inside my home directory, called dotfiles
. But then you still have a problem. How to get the files inside the folder installed into the home directory? I ended up using GNU Stow for this. Stow is a symlink farm manager which takes distinct packages of software and/or data located in separate directories on the filesystem and makes them appear to be installed in the same place.
With Stow, I was able to create a makefile, which made it easy to install or uninstall the packages into my home directory. The basic makefile looked like:
|
|
Installing or updating the dotfiles now becomes as easy as running the following command:
make
To delete all symlinks related to the dotfiles just run:
make delete
Now we ended up with an easy way to install or uninstall dotfiles in the home directory. Now we need a way to enhance the configuration with things such as dependencies and computer-specific configuration. The following sections will go deeper into this.
Managing dependencies
Only checking in configuration files doesn’t add any value. Somehow you want to be able to manage dependencies, such as:
- zsh plugins
- tmux plugins
- vim plugins
For managing dependencies I opted for Git submodules. Git submodules allow you to include other projects as a subdirectory in your own project.
It often happens that while working on one project, you need to use another project from within it. Perhaps itβs a library that a third party developed or that youβre developing separately and using in multiple parent projects. A common issue arises in these scenarios: you want to be able to treat the two projects as separate yet still be able to use one from within the other.source: git-scm.com
Adding a git submodule can be done by using the git submodule
command. Let’s say we want to add the bar
plugin to Vim. This can be achieved with the following command:
git submodule add git@github.com/foo/bar.git pack/plugins/start/bar
Updating all the git submodules added to the repository is also easy and can be done with the following command:
git submodule update --recursive --remote
Computer-specific configuration
One last challenge I had was computer-specific configuration. I ended up adding an optional file suffixed with .local
. To load the computer-specific configuration I created a function that will source a file when it exists:
|
|
The most appealing use case of an optional file is the use of aliases. On each computer I want to use different aliases. When I create a aliases.local
file in my home directory it is automatically included once the shell is reloaded. This all works because the .zshrc
file includes the optional aliases using the include function:
include ~/.aliases.local
Conclusion
I’ve tried a few ways to make it easier for me to manage, install/uninstall and share my Linux terminal configuration across multiple computers. For myself, I did find the sweet spot in ease of use versus configurability and extensibility.
I hope you learned something from this post. In case you want to try it yourself or want some inspiration. You can have a look at my dotfiles repository on GitHub.