Sunday was the first day in a long time where I only had 600 things to do, instead of 1000, so instead of doing them in the morning, I stayed in bed and migrated my homedirectory from one giant monolithic repository of .dotfiles into multiple repositories. Here's how & why.
I've tracked the config files in my homedirectory for a long time in git. Previously I just had one git repository with everything in it, from my .vimrc and .muttrc, through to the contents of ~/bin and ~/texmf. This was annoying for a number of reasons, mostly that it included private stuff with passwords in it (like my .irssi/config and .offlineimaprc), and so, when I often wanted to share snippets with people, I couldn't just point them at my git repository. I also found that I wasn't very good about committing changes atomically, since I never really made the context switch between editing my .muttrc and someting else, and then I would find a week went by and I had 20 changes that should really have been committed separately, but I was too lazy to do so.
So about 6 months ago, I signed up to the
vcs-home mailing list, and lurked. Finally, on Sunday morning, I once again was experiencing massive packet loss to NZ (it's often above 70%) and, since I run irc in screen on a machine in NZ, irc was a real pain. That was the last straw, and since I have a virtual server here that I've been meaning to migrate to for a long time, I finally decided to do it.
In typical yak shaving way, this meant I first had to set up backups on the machine, which involved editing config files in vim. But of course my vimrc wasn't on the machine. And I didn't want to checkout my entire monolithic repository, so I decided it was time to first split up the homedirectory.
I talked to
my amazing boyfriend, and he suggested a combination of git with "fake bare" repositories (bare in that they're initialised with --bare, so that the repository doesn't contain files, but "fake bare" because core.bare is set to false, and core.worktree is set to ../../), vcsh, and mr. Let's look at each of those in detail.
Fake git repositories
First, I have a .fgits directory in my ~, in which to store all the git repositories. fgits because that's what Martin used and I was copying some of his config, even though he can't remember what the f stands for.
GIT_DIR=~/.fgits/zsh.git git init --bare && cd .fgits/zsh.git && git config core.bare false && git config core.worktree ../../
This creates a new "fake bare" repsotitory for me to commit my zsh related config into. Setting core.worktree means that it actually uses my homedirectory for the worktree, which means that it actually uses the .zshrc file in my homedirectory. This means that I can have multiple .fgits repositories, all pointing to ~/ for their worktree, and commit selective files from ~/ into each of them.
vcsh
However, it seems that I can't actually use it until I use
vcsh. vcsh is a little script that changes into the "context" of one of the fgits repositories, in a new shell, with some extra GIT_ variables set.
It looks like this:
hermia:~% vcsh zsh
hermia:[git/master-]:{vcsh:zsh}~%
And inside there, I can git add, commit & push everything I need, and then exit the sub shell.
mr
mr is a tool used to manage multiple repositories. With it, I configure a list of repositories to be checked out/ updated, and it manages them all for me. I simply made a new .fgits/mr.git and
committed ~/.mrconfig to it
Now mr knows about "git fake bare", so in the checkout command, I tell it git_fake_bare_checkout, and give it the path to the worktree. Running mr update in my homedirectory now updates all of the repositories in .fgits and that means that the actual files in my homedirectory are updated.
zsh prompt
Finally, I had to do some magic to get both the vcs_info stuff I already used, and the vcsh information into my zsh prompt.
Here's the relevant zsh snippet. Notice $PSEXTRA in there, I
slightly modified Martin's vcsh script to set PSEXTRA instead of writing to PS1.
Then, back to the virtual server I was setting up here. I scp'd my .mrconfig to ~/ there, and then installed git and mr, and just ran "mr checkout" and had all of the files in my homedirectory perfectly set up.
So far there are no shortcomings, other than the obvious fact that I copied Martin's vcsh script from his func directory in his zsh git repository , put it in my ~/bin directory and modified it, without being able to track his changes. But other than that, I now have
"all" my dotfiles in seperate repositories and it's working perfectly. "All" is in quotes, because while I've done 8 or so, I still have a lot to do.
Finally, .gitignore doesn't work with this setup
at all. I couldn't even make status.showUntrackedFiles work.
Here's a discussion about this. For now I'm reasonably happy just doing git status -uno to get around it.
After some feedback on irc from
Nigel, I must explain why this helps me commit properly. First, it's a bit of a context switch, like, "oh, I have to edit my zsh config, I better switch to that context by issuing vcsh zsh", and then I have a prompt to remind me. Secondly, if I forget, I actually
have to commit separately because everything is in different repos, where previously the urge to do git commit -a -m "last few weeks config changes" was almost overwhelming.