Tried CVS, didn't like it. An ancient PITA. Used Subversion lightly for a couple years. Liked it but felt someone could do even better. Looked at Monotone, Darcs, Bazaar, Mercurial. Finally picked one.
I've been using hg the past few weeks to see if I can move off of svn
(which I never really got all that deep into anyway) and I have
decided that I can. Plus it seems the better solution for the project
I am working on. The distributed capabilities will come in very handy.
From a system administration point of view I have been trying to get
in the habit of not making .bak files of important configs
anymore. They just clutter up the system and sometimes you end up with
multiple copies and you forget what each one was for. So instead of
making .bak files I vowed to use version control instead. Plus I
wanted to version control all of the system configs I touch in
general, the dotfiles in my homedir, and my various progamming
projects. Not only does this solve the .bak file organization problem
but it protects my code as a form of remote backup, allows me to
revert unhealthy changes, and makes it easy to check my preferred
environment out into a new home dir on a new machine. My emacs configs
have grown especially large and complicated.
This guy has been at this homedir in version control thing longer than
I have and may be where I got the inspiration, I don't recall:
http://www.onlamp.com/pub/a/onlamp/2005/01/06/svn_homedir.html
With svn I had one main repo on one of my servers which I organized
heirarchically by hostname and then path so something like:
/hosts/mail.copilotco.com/etc/postfix/main.cf
There was also a /projects beside /hosts with a directory for the name
of each of my coding projects (not system configs) which held tags,
trunks, branches, etc.
The real annoyance with this arrangement is that I have to do a series
of svn mkdir's to create that path, especially for a new host. This
turns into a bunch of svn ls and svn mkdir commands until I get what I
want. Then I can checkout that empty svn directory into the current
working directory where the config files are such as main.cf and then
add my files and then commit them. This ends up being just enough work
that I often skip it. Especially for the setup of new hosts and
directories.
Another thing that bugged me about svn is that I tend to move servers
and things around a lot and it always seemed like the svn url of the
local working copy was getting out of sync with where the server
actually was which caused headaches. Having the actual repository
right there where I could commit without having to remember how to do
do "svn switch --relocate old-uri new-uri" to get pointed at the right
place is nice.
Another svn annoyance that has been bugging me for a while is:
http://subversion.tigris.org/issues/show_bug.cgi?id=2580
which was always causing ssh ControlMaster pipes to be left laying
around which would cause ssh to barf and cause the commit to fail
after I had already typed up my nice descriptive commit message. Then
I had to delete the control file. ssh connection multiplexing is nice
because it allows new connections to be set up very quickly by
tunneling over the old one. Especially nice if you use tramp with
emacs for remote editing of files.
Finally, once I have my configs in svn I would often forget to commit
them after I edited them. So eventually I would get a bunch of changes
made over the course of days or weeks. I would realize I had messed up
and forgotten to commit for a long time. Then I do a commit with a
message of "heck if I can remember what all this was about".
So I cooked up a way to be automatically notified if the current dir
has uncommitted changes by putting this in my .bashrc:
function cd {
builtin cd "$@"
if [ -d .svn -a -r .svn ]
then
DIFFS=svn status| egrep "^M"
if [ ! -z "$DIFFS" ]
then
echo "This directory contains uncommitted changes."
So whenever I cd into a directory I get a notification if I have been
lazy and not committing things. But eventually I ran into a problem:
if the directory has lots of files in it (which my home dir always
does) the svn status command can take several seconds to return which
is unbearably annoying since this happens with every cd. I end up
hitting ctrl-c and not seeing the message saying I have uncommitted
changes and the whole thing becomes totally ineffective.
While playing with hg I have discovered that hg has the same problem.
However, hg also has the ability to use the Linux 2.6 kernel's inotify
functionality. It can subscribe to the kernel to be told when files in
the working directory change so it doesn't have to do a brute force
check and stat every single file. So if we put the following in our
.hgrc:
[extensions]
inotify =
[inotify]
autostart = True
we automatically get a little hg daemon running talking to inotify
which will communicate with any hg command we invoke. So I changed my
.bashrc cd function to read like so:
function cd {
builtin cd "$@"
if [ -d .hg -a -r .hg ]
then
DIFFS=hg status -q
if [ ! -z "$DIFFS" ]
then
echo "This directory contains uncommitted changes."
and now my cd function works perfectly and instantaneously even on
huge directories. However, because I have so many files in my homedir
I had to do this to make inotify handle it:
echo 32768 > /proc/sys/fs/inotify/max_user_watches
So I dumped that in my rc.local. The inotify functionality requires
the most recent version of hg to work halfway decently. And even then
occasionally runs into issues. If I do an hg clone with the inotify
turned on the clone never finishes, it just hangs. And if I modify and
then revert a file hg status -q still says that file is modified and
does so until I commit it (but there are no changes so I'm not sure
what it's committing). So inotify still has a couple bugs.
Each hg repo is totally independent and does not rely on any other
server. But one of the great things about having a remote server for
version control is that if my local box catches fire and is destroyed
I have a remote copy of my code. So I need to find a way to easily
push my changes to a repository on a remote server. Mercurial supports
this and you don't have to push your code somewhere else but pretty
much everyone does it because that is the whole point of distributed
version control: being able to push your changes to another
person/place for further work or just another server for storage.
Every time I do an hg init . that creates a new local repository which
needs a corresponding remote repository. This is a bit different from
svn where each local repository fits into a sort of branch or folder
in the big subversion repository on the server. So I set up a remote
account with username hg, installed my public key in
.ssh/authorized_keys and inside ~hg I have a subdir
home.copilotco.com/home/treed inside of which I have done an hg init.
Now if I want to push my home directory from my home workstaion to
this server for safe keeping I can say the following:
hg push ssh://hg@hg.copilotco.com/home.copilotco.com/home/treed
but this uri is rather long. I want to make it the default. So in my
.hgrc I can say:
[paths]
default-push = ssh://hg@hg.copilotco.com/home.copilotco.com/home/treed
So now I can clone my dotfiles and various other settings that I want
to carry around with me from that url. When I make a change on some
machine somewhere I "hg push" the change back up to the
repository. Then I can "hg pull" on any other machine which I want to
have the update. Pretty slick.