In the past, I already attempted to manage some of my configuration files, and failed twice:
I made it a public github repository, and added too much - creating an unmanageable mess of sometimes constantly changing files, with the few really useful ones strewn in. I also forgot that some files revealed information about SSH ports and the like. Ouch. Quickly deleted all traces of it, hopefully.
Tried a homebrew synching mechanism with symlinks, some of them to folders that contained not only the interesting config files but also caches and the like. I quickly lost sight of what is where and the process of adding files (moving the file to the backup location, creating a symlink for it) and removing files (tracing symlinks, copying back the original files...) was too complex.
So what do I actually need/want?
- I want to share dotfiles across multiple installs of varying types/distros
- Centralized? Probably yes. Server/client model, but:
- It is possible to both push & pull (to use git vocabulary), meaning: changes on all machines can be incorporated back into the central repository, and local repositories can be updated.
- I do not want a public repository, but I want to access it remotely.
- Minimal (preferably none) impact on the local machines' existing file/folder structures, meaning: no symlinks please.
This still leaves me with the most important question:
What are dotfiles?
Configuration files that took a while to set up, don't change regularly, don't depend on software or hardware versions, and are less pain to copy over and edit, than to recreate them from scratch.
Also scripts (my complete
~/bin) and other pieces of valued code.
More often than not, these files aid personal habits, like keyboard shortcuts or custom menus.
There can't be a finite definition, and that's why it's so important that
- the impact on the system's file/folder structure is minimal (no symlinks!)
- it is easy to add/remove files
Implementation: Store Dotfiles in a Bare Git Repository
Most of my requirements and musings seem to be supported by the scheme described in this article - at least theoretically. Let's see how I can put it into practice.
git init --bare $HOME/.dotfiles alias dotfiles='/usr/bin/git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME' dotfiles config --local status.showUntrackedFiles no
and don't forget to:
echo "alias dotfiles='/usr/bin/git --git-dir=$HOME/.dotfiles/ --work-tree=$HOME'" >> $HOME/.bashrc
Now start adding some files:
dotfiles status dotfiles add .vimrc dotfiles add .bashrc # etc... dotfiles commit -m "Initial commit"
Please take your time to read the original article; I did not fully document the process here.
The Additional Step: Creating a Central Remote Repository
But I cannot push these changes anywhere yet.
Unlike the writer of the article, I don't want to put an ssh daemon on my main machine, instead I want to have the dotfile repository on my server, which is a different machine than those that are going to benefit from the shared dotfiles. How do I get it there? I have some experience with both github and local git repositories, but this is new. Do I need to set up a git server? It seems no, I can do it all over SSH, which is already working on all ends. This actually aids my wish for remote but private access.
So I already have everything I need to remotely access a git repository.
Nevertheless, the official git guide recommends setting up a separate git user. Done. Setting up (the git user's) SSH keys is a piece of cake by now. For those who don't think so, this is the ultimate set of instructions. Now initialize a bare repository:
git@localhost:~$ mkdir dotfiles && cd dotfiles git@localhost:~/dotfiles$ git init Initialized empty Git repository in /home/git/dotfiles/.git/
But how to get the original repository on my home machine onto the server, so it becomes push- and pullable? After some trial and error, I ended up using the exact method described in this answer:
- locally clone --bare the repo (i used the previous created alias for that, so the command was
dotfiles clone --bare ~/.dotfiles /temp/path/to/dotfiles)
- scp the local clone to the server, right where you want to pull pull from
From there onwards, I clone the repo to another machine much like described in the next step of the article.
Back on the originating machine I can finally start pushing out changes:
dotfiles push origin master
Then to the remote machine:
But I can also push changes from some other machine back into the repository, and then incorporate them into all my other machines.
In other words: say I have an improvement to one of my scripts while working on my laptop. I push the improvement out via my dotfiles repo. Later I can pull it into my desktop computer.
It's more like a big fat warning sign:
If you're not professionally familiar with git, you will experience pitfalls, due to the special nature of this git repository.
I had to
- remove files that will always be different on different machines
- take care to remove them only from the git repo, and not physically from the filesystem
- rewrite some scripts to cater differently to different machines (making use of
- make sure I always immediately push out changes, and pull them back in on another machine where before I make any changes there (I will have to learn about branches I guess)
After some trial and error (and one major f*ck-up where I almost had to start from scratch) I think I have it working to my satisfaction.
Here's just one problem I managed to solve:
How to delete a file from a Git repository, but not other users' working copies
So, I always need to be aware of which files are part of my dotfiles repo.
This implies less is more.
I am also considering to replace the previously described aliases with a bash function that only allows for a limited subset of git commands, as a security precaution.
What About System Files
What about files that do not reside under my
If I try to add them to the repo I get:
dotfiles add /etc/default/grub fatal: /etc/default/grub: '/etc/default/grub' is outside repository
date="$(date +%Y%m%d%H%M)" backup="/home/username/.local/share/etc.7z" # 7z will fail if this doesn't end in 7z because of mhe=on mv "$backup" "$backup.$date" 7z a -mhe=on -x'!etc/hosts' -p"$(cat /root/etc.zip.pwd)" "$backup" /etc && rm "$backup.$date"
Since I am backing up all of this anyway with borg, this can be a little sloppy.
/root/etc.zip.pwd is only readable by root.
Let's try again:
dotfiles add etc.7z # commit, push etc.
No errors. Done!
Sep 9th, 2017