How to automatically reflect vim-plug changes on all hosts

I use the excellent vim-plug to manage my VIM plugins. In this setup, whenever you want to add or remove plugins, you have to do three things:

  1. Edit (and source) vimrc.
  2. Call a command to install the new plugins, :PlugInstall.
  3. Optionally, call a command to clean the removed ones, :PlugClean.

If you use VIM on a single machine, this is straightforward enough. However, if you implement your dotfiles across multiple hosts, simply pulling the changes on vimrc doesn’t make new plugins work. Every time you change your VIM plugins list, you need to remember to also do 2 and 3 on each host.

This was painful to me and I wanted to automate the process. My goal was that whenever I pulled my dotfiles on any device and my vimrc had changed, those commands would run automatically.

The solution: Git hooks

My dotfiles are under source control, so I immediately thought of Git hooks. They reside under your repo’s .git/hooks directory and allow you to do exactly that: before or after a certain Git event, run a certain command.

I wrote a post-merge script (name is important), made it executable, and dropped it into this directory.

#!/bin/sh
# Author: Rafael Cavalcanti

exec </dev/tty >/dev/tty

readonly changed_files="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)"

has_changed() {
echo "$changed_files" | grep --quiet -E "$1"
}

# Update VIM plugins
if has_changed .vimrc; then
echo "\n[Hook] vimrc changed: Installing/cleaning VIM plugins..."
# PlugInstall doesn't proceed with GIT_DIR set (https://github.com/junegunn/vim-plug/issues/506).
(unset GIT_DIR && vim +PlugInstall +PlugClean! +qa!)
fi

Whenever I git pull, this script will be automatically run by Git. As it is pretty clear, it checks if .vimrc has changed from the previous HEAD, and if so, calls PlugInstall and PlugClean!.

So far so good. However, the hooks directory is not tracked and, thus, not distributed to all installations. Let’s tackle it.

Tracking the hook

One way to track your hook script is to create a directory in your repo, say git-hooks and move it there. Then, anytime you clone the repo, you should configure Git to look that directory for hooks: git config core.hooksPath THE_PATH_TO_HOOKS_DIR.

My dotfiles have a bootstrap script, so I placed this command there, to be run automatically on new clones. For the systems with my dotfiles already installed, I just run the command manually once.

Done! Now every time I pull my repo and there are changes to my vimrc, my list of plugins is conveniently checked for new installs and removals.


See all posts