endot

eschew obfuscation (and espouse elucidation)

Git Submodules vs. Subtrees for Vim Plugins

After a few months of managing my dotfiles with git, I felt the need to organize my vim plugins a little better.  I chose to use pathogen (created by Tim Pope), which allows me to keep each plugin in its own subdirectory.  It fits well with using git to manage dotfiles because git has two ways of tracking content from other repositories.  The first is submodules, which keep a remote URL and commit SHA1 so that the other content can be pulled in after cloning. The second is subtrees, which merges the history of the outside repository directly into a subdirectory.  Here’s a quick list of the pros and cons of each strategy:

Submodules

Pros:

  • Each plugin is self contained and segregated from the rest of your files.
  • It’s easy to see what files came from other sources.

Cons:

  • Making tweaks to plugins is difficult; requires managing a fork of the upstream repo.
  • Submodules are difficult to update.
  • Initial clone takes longer as each submodule needs to be initialized and fetched.

Subtrees

Pros:

  • Only one command to get all your files.
  • Making tweaks to plugins is easy, just modify and commit.
  • Initial clone is faster.

Cons:

  • It’s more difficult to extract changes and submit them back upstream.
  • Plugin files are in your repository too, potentially confusing your history
  • Difficult to determine exactly where other content came from

After trying both out on a branch in my repository, I settled on using subtrees.  Using them can be daunting, but there’s a fantastic wrapper called git-subtree that greatly simplifies the whole process.

For instance, to add vim-fugitive (another by Mr. Pope), all I ran was this command:

1
git subtree add --prefix .vim/bundle/vim-fugitive https://github.com/tpope/vim-fugitive.git master --squash

This results in a single commit that contains the squashed history of the vim-fugitive plugin and a merge commit which merges that squashed commit into my repository.  Here’s what that looks like on github: ad19e02.

After that merge, the vim-fugitive files are now part of my repository.  No extra steps are necessary to get those files after an initial clone.

While I was writing this, I noticed that there have been a few more commits on the main fugitive repo, so I ran the following command to merge in those changes:

1
git subtree pull --prefix .vim/bundle/vim-fugitive https://github.com/tpope/vim-fugitive.git master --squash

It all works quite well.  As of this post I have eleven plugins managed with subtrees and it only takes a couple seconds to clone my entire repo onto a new machine or VM.