Dotfile management with Chezmoi and Vim
I have three computers I use on a regular basis, and my Vim-based shell workflow
is heavily dependent on my configuration. Dropbox was my first port of call,
but after some conflicts caused by automatic syncing, I learned that manual
pulling and pushing with Git is much better suited to this problem. Like other
developers, I created a dotfiles repo and used stow
to symlink all the files
and folders in my configuration. I thought this was a pretty good setup, but
unresolved bugs would still sometimes appear on my systems.
I trialed chezmoi
and found it to be an improvement over my old stow
-based
setup. It takes a slightly more deliberate approach to managing dotfiles.
Instead of directly symlinking files from your Git repo into your home
directory, chezmoi
manages that repo under the hood and gives you a controlled
“staging area” for changes. This staging area lives in your
.local/share/chezmoi
directory and acts as an intermediary between your live
config files and version control. Any time you make changes to your dotfiles,
you explicitly add them to chezmoi
— no more accidental commits of stray
files or awkward .gitignore
rules sprinkled throughout $HOME.
Once you track your files with chezmoi add
, syncing between machines is a
straightforward two-step process. Make local edits, re-add your files with
chezmoi re-add
, go to the chezmoi
directory with chezmoi cd
and commit and
push like you would with any other Git project. Pull remote changes and apply
them with chezmoi update
. Even through the “push” step feels a bit verbose,
this workflow makes it harder to overwrite good configs with bad ones and gives
you a cleaner separation between what’s on your machine and what’s stored in the
repo.
chezmoi
has made my setup feel more predictable and less error-prone. The
final piece of the puzzle was integrating it with my Vim workflow. I wanted to
be able to edit tracked files, keep syntax highlighting consistent, and even
handle syncing without leaving the editor. With a simple Git alias (git sync
)
and a handful of Vim commands, I’ve made chezmoi
fit naturally into how I
already work.
In my ~/.gitconfig
, I have a handy little alias for committing and pushing
whatever unstaged changes I have:
[alias]
sync = "!f() { \
msg=\"sync: $(whoami)@$(hostname) $(date '+%Y-%m-%d %H:%M:%S')\"; \
git add . && \
git commit -m \"$msg\" && \
git push; \
}; f"
Now I can at least push my chezmoi
changes with chezmoi git sync
, nice!
But we can do better than that. How can we integrate this into Vim? Here’s what
I’ve written in ~/.vim/plugins/dotfile.vim
:
" Mappings to jump straight into my configurations:
nnoremap <Leader>; <cmd>edit ~/.vim/vimrc <bar> lcd %:p:h<CR>
nnoremap <Leader>: <cmd>exec 'edit ' . system("chezmoi source-path") <bar> lcd %:p:h<CR>
" Commands to pull/push changes
command! DotPull !chezmoi update
command! DotPush !chezmoi git sync
"" If a tracked file is edited locally, automatically re-add it
"
function! AddChangesToDotfiles()
let current_file = expand('%')
if (system('chezmoi managed ' . current_file) !=? '') && (system('chezmoi diff ' . current_file) != '')
execute 'silent !chezmoi add ' . current_file
echo 'Added to dotfiles'
endif
endfunction
augroup add_changes_to_dotfiles
autocmd!
autocmd BufWritePost * call AddChangesToDotfiles()
augroup END
"" If a tracked file is edited in chezmoi, automatically apply it
"
function! ApplyChangesFromDotfiles()
execute 'silent !chezmoi target-path ' . expand('%') . ' | chezmoi apply'
echo 'Applied from dotfiles'
endfunction
augroup apply_changes_from_dotfiles
autocmd!
autocmd BufWritePost ~/.local/share/chezmoi/* call ApplyChangesFromDotfiles()
augroup END
"" If opening a file in chezmoi, add proper syntax highlighting
" (e.g. highlight `dot_bashrc` as `.bashrc`)
"
function! DotfilesSetFiletype()
let l:realpath = trim(system('chezmoi target-path ' . shellescape(expand('%:p'))))
execute 'setfiletype ' fnameescape(fnamemodify(l:realpath, ':t'))
endfunction
augroup dotfiles_set_filetype
autocmd!
autocmd BufRead,BufNewFile ~/.local/share/chezmoi/* call DotfilesSetFiletype()
augroup END
If you haven’t already, I highly encourage you to check out their documentation and quick start