Introduction to darcs

The other day I found myself needing to put some relatively minor code under revision control. Traditionally I've used rcs, as it's very simple, not hard to understand, and was available damn near everywhere (on Un*x machines, at least).

This time though, my laptop didn't have it installed, so I looked around for alternatives. cvs and its successor Subversion (svn) both require a central repository to be set up, which was neither simple enough to do on the spur of the moment, nor a convenient architecture for my circumstances, working as I do on a laptop, public server, and (frequently powered down) home machine.

What I found instead was darcs, which has a number of things going for it. Firstly, it has a good manual and plenty of support. Secondly, it has a well thought-out formal semantics underneath it, which (writing as a computer scientist) is always a good thing to see. This is bolstered by the fact that it's written in Haskell, by a physicist who appears to know what he's on about. Finally, it's a decentralised architecture, meaning the options available at one installation are much the same as those available anywhere else, whether they happen to be on the same machine or not.

Anyway, enough of that: here's what it looks like to use.

  • Get things started.
    $ cd ~/SRC
    $ vi foo.lisp        # Create the masterpiece
    $ darcs initialize
    $ ls
    _darcs  foo.lisp     # All darcs' stuff is in _darcs
    
  • Add something to the repository.
    $ darcs add foo.lisp
    $ darcs whatsnew
    {
    addfile ./foo.lisp
    hunk ./foo.lisp 1
    +(format t "Hello, world!")
    }
    
  • Check things in.
    $ darcs record
    Darcs needs to know what name to use as the author so that [...]
    <snip>
    What is your e-mail address? foo@bar.baz
    addfile ./foo.lisp
    Shall I record this change? (1/?)  [ynWsfqadjkc], or ? for help: y
    hunk ./foo.lisp 1
    +(format t "Hello, world!")
    Shall I record this change? (2/?)  [ynWsfqadjkc], or ? for help: y
    What is the patch name? Initial revision
    Do you want to add a long comment? [yn]n
    Finished recording patch 'Initial revision'
    $ darcs whatsnew
    No changes!
    
  • Make more changes
    $ vi foo.lisp
    $ darcs whatsnew
    {
    hunk ./foo.lisp 1
    +;; Print "Hello, world!" message
    }
    $ darcs record
    hunk ./foo.lisp 1
    +;; Print "Hello, world!" message
    Shall I record this change? (1/?)  [ynWsfqadjkc], or ? for help: y
    What is the patch name? Add "Hello, world!" comment
    Do you want to add a long comment? [yn]n
    Finished recording patch 'Add "Hello, world!" comment'
    
  • See what we've got.
    $ darcs changes
    Mon Nov  6 22:15:12 GMT 2006  foo@bar.baz
    * Add "Hello, world!" comment
    
    Mon Nov  6 21:45:46 GMT 2006  foo@bar.baz
    * Initial revision
    
  • Let's make a new copy to work on different branch — on another machine, if we want to.
    $ cd ~/BRANCHES
    $ ls
    $ darcs get ~/SRC
    Copying patch 2 of 2... done!
    Finished getting.
    $ ls
    SRC
    $ darcs get foo@machine.com:/home/foo/SRC --repo-name=SRC-SSH
    Enter passphrase for key '/home/foo/.ssh/id_dsa':
    Applying patch 2 of 2... done!
    Finished getting.
    $ ls
    SRC SRC-SSH
    $ cd SRC-SSH
    $ darcs changes
    Mon Nov  6 22:15:12 GMT 2006  foo@bar.baz
    * Add "Hello, world!" comment
    
    Mon Nov  6 21:45:46 GMT 2006  foo@bar.baz
    * Initial revision
    $ vi foo.lisp
    $ darcs whatsnew
    {
    hunk ./foo.lisp 3
    +(format t "The End.")
    }
    $ darcs record
    Darcs needs to know what name [...]
    <snip>
    
    What is your e-mail address? foo-ssh@bar.baz
    hunk ./foo.lisp 3
    +(format t "The End.")
    Shall I record this change? (1/?)  [ynWsfqadjkc], or ? for help: y
    What is the patch name? Add ending.   
    Do you want to add a long comment? [yn]n
    Finished recording patch 'Add ending.'
    
  • Now bring the changes back into the original repository. This could just as easily be done over HTTP if the directory were visible on a web server.
    $ cd ~/SRC
    $ darcs pull ~/BRANCHES/SRC-SSH
    
    Mon Nov  6 22:26:32 GMT 2006  foo-ssh@bar.baz
      * Add ending.
    Shall I pull this patch? (1/1)  [ynWvpxqadjk], or ? for help: y
    Finished pulling and applying.
    $ cat foo.lisp
    ;; Print "Hello, world!" message
    (format t "Hello, world!")
    (format t "The End.")
    
  • We're bored of this, so let's e-mail the changes off, create a tarball of the current state, and get rid of all the darcs stuff. Here the web-site has already got the first two patches, so darcs only sends the third.
    $ darcs send http://foo.bar.baz/projects/demo
    
    Mon Nov  6 22:26:32 GMT 2006  foo-ssh@bar.baz
      * Add ending.
    Shall I send this patch? (1/1)  [ynWvpxqadjk], or ? for help: y
    What is the target email address? foo-mail@bar.baz
    Successfully sent patch bundle to: foo-mail@bar.baz
    $ darcs dist
    Created dist as SRC.tar.gz
    $ ls
    _darcs foo.lisp SRC.tar.gz
    $ tar tzf SRC.tar.gz 
    SRC/
    SRC/foo.lisp
    $ rm -rf _darcs
    $ ls
    foo.lisp SRC.tar.gz
    $ darcs changes
    
    darcs failed:  Not a repository
    

The e-mail contains an attachment, which can be saved and applied using darcs apply. You can also sign the e-mail with your GPG key when you send, and have the other end automatically check the signature against its keyring. Oh, and there's a helpful setting that lets you specify a command which runs any tests you might have, for example HTML validators or unit tests; this lets you stop patches which would break things from getting committed to the repository. And of course, you can do lots of other stuff, like back changes out, tag versions with numbers and labels, etc.

It might not be so industrial-strength as a full Subversion installation, but it's just right for setting up in a couple of seconds where its needed and getting things done. And if you later find you need the big guns, don't worry — there's tool support for converting to (and from) most major alternatives.