Skip to main content

Dotfiles Reloaded

About a year and a half ago, I converted to using GNU stow and git for managing my dotfiles. The most recent version of that effort is available here.

The scheme worked well but the OCD part of me grew tired of seeing symbolic links to files instead of the actual files themselves. The symlinks weren't unexpected, after all, the whole point of stow is easily managing lots of links. Then I ran across a post on Hacker News about using a bare git repo to manage dotfiles in place. I very much liked the idea and settled on using vcsh and myrepos to manage my dotfiles going forward.

I should mention that I've known about vcsh for a good while. My impression had been that it was more complicated than necessary for my purposes. I've since changed my mind completely; it's as simple as what I ended up doing with stow, if not simpler. Minor tweaks to dotfiles are a pleasure when using myrepos.

These are my brief notes about the process.

I've lost the original HN link, however, I've included the relevant links it led me to [1] [2] [3] [4] [5] [6].

Installing vcsh and myrepos

# Will also install myrepos
$ sudo port install vcsh

Using vcsh

Add Existing Dotfiles to vcsh

Assume you are just getting started with vcsh and wish to control your existing dotfiles. Do the following for each logical group of dotfiles. This example will put my emacs related files in a vcsh controlled repo named emacs. It assumes that I have created an empty repo on Bitbucket named vcsh_emacs, which it will be synced to.

# Not necessary, but it keeps paths in this example clean
$ cd ~
$ APP=emacs

# Create the repo in vcsh
$ vcsh init $APP

# Add emacs related configuration and other files
$ vcsh $APP add .emacs
$ vcsh $APP add .emacs.d/lisp/os-x.el
$ vcsh $APP add .emacs.d/lisp/virtualenvwrapper.el
$ vcsh $APP add bin/ec     # script to call emacsclient
$ vcsh $APP add bin/ecw    # script to call emacsclient
$ vcsh $APP add bin/emacs  # script to call emacs

# Will be sourced by .bashrc to set env variables
$ vcsh $APP add .bash.d/init/90-emacs

$ vcsh $APP commit -m 'Initial commit of dotfiles'

If you run vcsh emacs status, you will notice that all files in your home directory will be shown as untracked, except those that were controlled. If you'd like to avoid this, perform the following optional step:

$ vcsh write-gitignore $APP
$ vcsh $APP add -f .gitignore.d/$APP
$ vcsh write-gitignore $APP
$ vcsh $APP add .gitignore.d/$APP
$ vcsh $APP commit -m 'Added gitignore for package'

The above isn't a mistake; the write-gitignore and add are run twice. One of the links I provided explains why.

Now it's time to push the repo out to Bitbucket.

# Push the files out to Bitbucket
$ vcsh $APP remote add origin
$ vcsh $APP push -u origin master

Perform the above for each set of dotfiles you wish to control.

Note that w.r.t. vcsh, I have named repos using the application the files are associated with (e.g., 'emacs'). That same name, with 'vcsh_' prepended to it is used for the remote repository name (e.g., 'vcsh_emacs'). This is done to the remote repository names to make it easy to distinguish the vcsh repositories from other repositories I keep on Bitbucket.

All of my vcsh repos on Bitbucket are publicly accessible here.

Using myrepos

If you are happy with the above, you can stop reading now. On the other hand, if you have a lot of collections of dotfiles and would like a way to deal with them all at once, keep reading.

Obtain an Initial myrepos Template

myrepos is configured through a set of files. We'll get a template from the author of vcsh and myrepos to use as our starting point. This will also create a vcsh repo named mr for the template.

$ vcsh clone mr

The template will create the following files (and one symbolic link):

├── .config
│   └── mr
│       ├── available.d
│       │   ├── mr.vcsh
│       │   └── zsh.vcsh
│       └── config.d
│           └── mr.vcsh -> ../available.d/mr.vcsh
└── .mrconfig

Create an empty repository named vcsh_mr on Bitbucket where we'll sync our mr repository.

Update the mr repo in vcsh to use my Bitbucket repository:

$ vcsh mr remote set-url origin

Update the myrepos Configuration for mr

In the default template, available.d/mr.vcsh will contain:

checkout = vcsh clone git:// mr

We'll need to update this to use our bitbucket repo for storage. Mine ended up looking like this:

checkout = vcsh clone mr

Additionally, you can remove mr/available.d/zsh.vcsh.

Now the changes can be committed and pushed out via:

$ vcsh mr add ~/.config/mr
$ vcsh mr commit -m 'initial configuration'
$ vcsh mr push

Everything up to this point need only done once - during the installation and initial configuration of myrepos.

Add vcsh Repositories to myrepos

Each time a new vcsh repository is created, it must be provisioned into myrepos so that myrepos can operate on it. This is done by creating a file in mr/available.d for the repository and, optionally, creating a symbolic link to it under mr/config.d. See this note's links for more info.

To add the vcsh emacs repo to myrepos:

$ cd ~/.config/mr/available.d
$ sed 's|mr|emacs|g' mr.vcsh > emacs.vcsh
$ cd ~/.config/mr/config.d
$ ln -s ../available.d/emacs.vcsh emacs.vcsh

NOTE: The trivial sed substitution above works in my situation, but it's super fragile and could easily go wrong, given difference contents of mr.vcsh.

Commit and push the changes:

$ vcsh mr add ~/.config/mr
$ vcsh mr commit -m 'added emacs repo'
$ vcsh mr push

Because myrepos is now configured, the commit/push could also have been done using myrepos. See the next step to see what I mean.

Ongoing Use of vcsh and myrepos

What has all this work to setup myrepos bought us? Assume that you've done a fair amount of work on your system and application configurations. The changes comprise a dozen file across five different vcsh repos. Without myrepos, one would have to perform the necessary vcsh commands on each repo individually. With myrepos, things are much simpler:

# Show status and uncommitted changes for each repo
$ mr status

# Commit and push all changes in all repos.
# New files must be added via 'vcsh <repo> add <file>'
$ mr commit

Using vcsh and myrepos on a New System

Ah, the best for last... You have a fresh install of macOS and you want to bring all your dotfiles over. Here's what to do.

  1. Install vcsh and myrepos as already described. Ensure your ssh config allows access to your repo (in my case,
  2. Clone the myrepos repository.
$ vcsh clone mr
$ mr up

Grab a beer and enjoy the fruits of all of your hard work.