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, this means that simply pulling the changes on vimrc won’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, after pulling vimrc changes.

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 should run automatically.

The solution: Git hooks

My dotfiles are under source control, so I immediately thought of Git hooks. Git hooks allow you to do exactly that: before or after a certain Git event, run a certain command.

They reside under your repo’s .git/hooks directory, and I placed my executable post-merge (name is important) file there:

#!/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

After a git pull this script is run. 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 the hook is to create a directory in your repo, say REPO/git-hooks, and place it there, instead of directly at .git/hooks. Then, anytime you clone the repo, you should configure that directory for hooks: git config core.hooksPath THE_PATH_TO_HOOKS_DIR.

My dotfiles have a bootstrap script, so I dropped that command there and run it once on each host.

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.