I'm fairly fearless when coding, which means that about once a week, I delete a huge chunk of something I should've kept, or change something into something unrecognizable, thereby inadvertently breaking a dozen unit tests. When I discover the problem, usually about 4 hours later, I no longer have any idea what I did that made the bad thing happen. Then I spend another 2 or 3 hours figuring out what I broke and fixing it. Ugly.
On my personal projects, I check in code every time I get a unit test working. My checkins are something like 15-20 minutes apart. On projects I get paid for, though, checking in means running the whole unit test suite, and that can take 10 minutes (on a good project) or 2 hours (on a bad one)--so I don't do it very often. That's when I get into trouble. I've been meaning to solve that problem for some time, and Joel Spolsky's blog topic last Wednesday (Joel on Software) finally kicked me in the pants. It took 15 minutes to solve the problem; here's how I did it.
The new breed of source code control systems, distributed systems like Git and Mercurial, have had my attention for awhile, as I usually work with a team that's spread out geographically. I regularly need to share code that's not quite ready to be delivered with another developer, and that developer is very likely in a different city. Typically we're reduced to emailing files to each other. A distributed SCC system would resolve this, as we could sync changes between our personal development repositories, but I assumed setting up and learning a new SCC would be painful, as it has been in the past, so I never got around to it. Joel's article on Mercurial, however, got me to thinking about it seriously, and since I had a few hours on my hands, I figured I'd give it a try. What I didn't expect was that I'd be up and functional in 10 minutes.
I downloaded Mercurial and installed it on my main dev box, which is a Windows laptop (I know, I suck). That took about 2 minutes, including googling for the Mercurial web site (http://mercurial.selenic.com/). Then I changed to the main development directory of my current project, and typed (per Joel's tutorial at http://hginit.com/):
The first impressive thing is these commands 'just worked'. The second is that's all that's required to set up a local repository and add an entire project to it. Really. I'm so psyched!
The "add" command was naive, because it added everything, including build output and subversion control directories (**/.svn/**). I spent the next 10 minutes reverting my add, ("hg revert --all"), then building an "ignore" file, then adding again, and finally committing. To shorten your search (Mercurial has great documentation, by the way--it only took me a few minutes to figure this stuff out), here's what I ended up doing.
1. I created an .hhrc file in my home directory (C:\Documents and Settings\Jerry) with the following content:
The "username" entry preempts a request by the "commit" command for a username, and I prefer vi to the default editor, which is notepad.
2. In the root of my project directory, I created a .hgignore file with the following content:
This tells Mercurial to ignore all files or directories named .svn (which is where subversion stores its status), all .class and .log files, the .hgignore file itself, the build.properties file, and anything in the antbuild directory (or its subdirectories). Mercurial ignore files support at least 3 different syntaxes for specifying files; the documentation is available on the Mercurial wiki and it's quite complete.
Now I'm in a position to check in locally every few minutes, but when I have a small feature complete, I can deliver via svn to the project's repository. That's right: I'm using Mercurial locally, and SVN for the project.
On my next project, I hope to have a chance to specify that the whole team uses Mercurial for the whole project; working with another programmer who's not physically nearby just got a whole lot easier; we can exchange our updates directly with Mercurial, then push them up to a central repository independently. Sweet!