Manage dotfiles using vcsh and mr


Developers are very picky about their working environment. We may consolidate various . files tailed for our tastes over years and track the change in a version control system, preferably git. Unfortunately, git or other VCS is not designed to maintain multiple working copies in a single directory. We may end up copying or linking the dot files to the home directory, like dfm does. The more external projects are added as submodule or subtree, the harder to manage. The last straw to me is that the monolithic dotfiles is not portable: not even across my Linux workstation to the VPS.

LinuxJournal has a great tutorial about vcsh for the rescue. After poking around and some error and trial, it is now dead simple to duplicate the working environment:

brew install vcsh mr
vcsh clone mr
mr update

mr will pull oh-my-zsh, neobundle.vim and my configurations for zsh, git and vim.

The next step is to chsh -s /bin/zsh; then launch vim. NeoBundle will prompt to download and install the vim plugins, voilà.

Behind the scenes

It is quite impressive how concise vcsh is, mere 462 loc in shell as version 1.20130829 is. It leverages the awesome git to do all the heavy-lifting. When you clone a remote repository foo, vcsh does the following behind the scene:

  1. create a folder in .config/vcsh/repo.d/foo.git, and export it as GIT_DIR.
  2. create $VCSH_BASE directory, i.e your home directory by default; and export it as GIT_WORK_TREE
  3. Thus the work tree and git meta data, aka .git folder, are separated. We can use git fetch to pull the remote repository into the GIT_DIR, and merge the changes back to the GIT_WORK_TREE.

This approach allows us to share the $HOME across multiple git repositories, a perfect solution to address the dotfiles problem!

Use cases

With the mechanism in the mind, we can explore other use cases to streamline the dotfiles.

Mount the submodule

If your dotfile depends on an external project, like NeoBundle.vim to .vimrc, you can use mr to mount the external project to the specified destination:

checkout = mkdir -p ~/.vim/bundle; VCSH_BASE=~/.vim/bundle/neobundle.vim vcsh clone neobundle

The trick here is to override the VCSH_BASE environment variable, so the git meta data is cloned in $HOME/.config/vcsh/repo.d/neobundle.git, but the working copy is merged in the specified bundle directory.

Divide and Merge

As the above examples shows, each git repository is an atomic configuration for a specific application. We can checkin the screenrc and tmuxrc, and pick whatever available in the target environment.

Some application supports configuration inclusion, then we can leave the personal credentials to a private git repository and pick them with vcsh clone.


The separation of the git meta data and working copy solves the dotfiles issues elegantly, just as the FTSE claims:

We can solve any problem by introducing an extra level of indirection.