Brandon Invergo

A quick introduction to version control with Fossil

As I previously indicated, I have recently moved all of my programming projects (and a couple non-programming projects) to self-hosted Fossil repositories. I have previously used mainly Git for my projects, due to my usage of Github and Gitorious for project hosting, with the exception of GSRC, which uses Bazaar, the recommended version control system of the GNU project. I've also have had experience with Mercurial, Subversion and CVS, so I have at least some idea of how many version control systems work.

Since almost every project I work on consists of one sole developer, all of these disparate systems largely converge into similar workflows. I primarily switched over to Fossil for the easy self-hosting and integrated wiki and bug-tracking tools, but I quickly found myself at home working with it. Nevertheless, since there are some aspects of it that differ from other VCS's, I thought I would give an introduction to it. This may be especially useful as a quick primer for people coming from Git, which is perhaps the most different from Fossil.

Since this is just an introduction, I will only cover the information needed to get up and running into a normal, everyday workflow. As such, I will not cover any of the special, corner cases or extra options. In all cases, do check the built in help for each command since many take optional arguments that I will not explain here (fossil help [command]).

The fossil command

Like most systems, Fossil consists of a single command, fossil. Unlike most systems, that single command encompasses a wide range of functionality, from basic version control tasks to running a basic web server. Nevertheless, its usage will quickly be familiar to you.

Setting up a repository

Creating a new repository

The first thing that we will want to do is to create a repository. As one might expect, this is done via the fossil new command. However there's a twist: you must specify the name of the repository.

$ fossil new foo.fossil

Now hang on. This has just created a file and it's not even a hidden one. Clutter!, you scream. It's true: Fossil repositories consist of a single file, which is, in fact, an SQLite database (the developer of Fossil created SQLite as well). In the beginning, I was not sure what to do with this file; I disliked having it sitting among my source files. The recommendation that I've seen and that I would pass on is to instead keep a directory somewhere that contains all of your .fossil files. This directory then, for example, allows the easy back-up of all of your repositories.

So let's now assume that we will create all Fossil repositories in the ~/fossils directory:

$ fossil new ~/fossils/foo.fossil
project-id: c17896e4840ecb6d93bb8081f56430a2be8374fe
server-id:  39264b251712a44f63ba756e1b190f44ad6e5885
admin-user: brandon (initial password is "13b63a")

Notice that when the repository is created, a user (with your name) is automatically created and is assigned a password.

Cloning an existing repository

If you wish to clone a repository that already exists, you will not be surprised to learn that you use the fossil clone command. As with fossil new, you must specify the name of your repository:

$ fossil clone http://zeptodb.invergo.net ~/fossils/zeptodb.fossil
Round-trips: 2   Artifacts sent: 0  received: 510
Clone finished with 477 bytes sent, 869785 bytes received
Rebuilding repository meta-data...
  100.0% complete...
project-id: 6a2f6abb93a52fa338a6ae020dc33342574c989e
server-id:  26274af3664a8d191d511c505a18f997d54bc169
admin-user: brandon (password is "c3a421")

If you already have a username and password on the remote repository, you may introduce it in the URL:

$ fossil clone http://user:pass@zeptodb.invergo.net ~/fossils/zeptodb.fossil

Opening the repository

This is a step that is, as far as I am aware, unique to Fossil. Now that you have a repository on your system, you need to open it somewhere. Essentially, you are checking out the contents of the repository in a separate directory. So, first create a directory to contain the project (I keep my projects in ~/Projects):

$ mkdir -p Projects/foo

Due to the way Fossil handles branches, which is more akin to systems like SVN, I also go ahead and create a trunk directory to hold the code. So, let's do that, and then open the repository in there with fossil open:

$ mkdir Projects/foo/trunk
$ cd Projects/foo/trunk
$ fossil open ~/fossils/foo.fossil

The result is a directory under version control that is much closer to what you are used to with other systems. The difference is that it is linked to a local repository database elsewhere on the system. You can actually use fossil close to close the connection to the database. This does not clear out the directory; all the files will remain there. It will simply no longer be under version control. I have not found the occasion to require this.

Configuring a repository

As I mentioned above, the fossil command even offers basic web server functionality. In fact, this is the most convenient way to configure your repository. Simply run:

$ fossil ui

This will launch the web server and it will open a local web page for your repository. Feel free to explore. For now, we're just interested in the Admin section. First things first: go to the Users section, then click on your User ID and finally change your password. Next, you'll want to go to the Configuration section to set some basic information like your project name and description. The Settings section might also have some areas of interest if you have special needs regarding command configuration; for me the defaults are normally fine.

Fossil repositories are highly configurable and it is beyond the scope of this tutorial to go into any detail. I find that the defaults are generally sane. Have a look around the settings and read the documentation for more information.

Note that you may also set many settings with the fossil settings command.

Working with files

Adding new files

Adding new files to the repository is simple: just use the fossil add command, which behaves exactly like any other system:

$ fossil add bar.c
ADDED  bar.c

(Re)moving files

Moving or removing files works a bit differently than you might expect. Either one of these commands simply changes the references in the database, however they do not modify the actual file system. This means that you need to follow up the fossil commands with another command to do the actual action.

$ fossil mv bar.c baz.c
$ ls
bar.c
$ mv bar.c baz.c
$ fossil rm baz.c
$ ls
baz.c
$ rm baz.c

Checking the current status

Once you've added some files or made some changes, you'll want to see what the current status in the repository is. This is accomplished with the fossil changes command:

$ touch foo.c bar.c baz.c
$ fossil add *.c
$ fossil changes
ADDED      bar.c
ADDED      baz.c
ADDED      foo.c

Viewing changes

You may view the changes on a file using either the fossil diff or the fossil gdiff commands. The former uses an internal diff mechanism, while the other allows you to select a graphical diff application (set up in the Settings section of the web interface or via the fossil settings command). Normally, this compares a file against its last checked-in version, however you may select other versions using the --from and --to arguments.

Committing changes

Now that you're done making changes, you want to commit them. This is done with fossil commit, which works much the same as in other systems:

$ fossil commit -m 'first commit'

You can, of course, only commit a limited sub-set of changed files if you would like:

$ fossil commit -m 'first commit' foo.c

If you are working from a clone of a remote repository, and you have sufficient access rights on that repository, Fossil will by default behave in a similar manner to CVS by auto-syncing your changes with the remote server. Thus, you commits will automatically be pushed to the remote server. This may be turned off in the settings.

Reviewing commits

You can look at a log of past commits using the fossil timeline command:

$ fossil timeline
22:22:16 [dd33da8b57] *CURRENT* first commit (user: brandon tags: trunk)
22:05:09 [9ec5208d2a] initial empty check-in (user: brandon tags: trunk)

Syncing your repository

Fossil supports the usual push and pull operations for synchronizing your local repository with the remote upstream repo. Additionally, there is the handy sync command, which is equivalent to running both a push and a pull at the same time.

Check-ins

(note: in Fossil, one primarily refers to "check-ins", where in other systems one might talk about "revisions" or "commits")

As with Git, Fossil check-ins are referred to by an SHA1 hash. For commands that require the specification of a particular check-in, you may refer to one by the first four or more characters of the hash.

Tags

You can add a tag to a check-in using the tag command. Note that, unlike in Git, you must explicitly specify the check-in by its hash. So to add a tag called "v1.0":

$ fossil tag add v1.0 9ec520

You can then use that tag name in place of the hash of the check-in.

You can view a list of all existing tags via the tag list command.

$ fossil tag list
v1.0

Finally, you can easily remove a tag from a check-in with tag cancel:

$ fossil tag cancel v1.0 9ec520

Some tags (actually, more precisely, "properties") are automatic. For example, the most recent check-in carries the tag "tip".

Branches and Forks

Here we need to be a bit more careful due to some differences in terminology between Fossil and other version control systems. In Fossil, forking in particular takes on a different meaning. Effectively, on a technical level, forking and branching are the same: they both result in multiple "leaf" commits, or, in other words, multiple parallel tips. The difference is in the intent. Branches are desirable and represent parallel development, such as on large new features that are not ready to be merged into the trunk yet. Forks on the other hand, arise when multiple developers are working on the same code base but have not merged their work yet.

Creating a branch

Branches are created with the branch new command. Note that you must specify a check-in as its basis:

$ fossil branch new experimental tip

This will create a new branch called "experimental" based on the current tip.

For Git users, the next step will be unfamiliar. In Git, branching is, as they say, cheap. You can create and switch between branches quickly and easily. In Fossil, you use a workflow more similar to that of, for example, Subversion, in which you keep your branches in separate directories:

$ cd ..
$ mkdir -p branches/experimental
$ cd branches/experimental
$ fossil open ~/fossils/foo.fossil
$ fossil checkout experimental

You may then proceed to edit files and commit; all check-ins will be added to the newly created "experimental" branch.

Merging

Merging a branch back into trunk is simple:

$ cd ../../trunk
$ fossil merge experimental

If the current branch has a fork, you can simply call the merge command without specifying a version. This will attempt to resolve the fork and merge the disparate tips.

Web interface

I will only briefly introduce some key features of the web interface. These features are also accessible from the command line, however it is far more convenient to use the web interface to interact with them.

Wiki

Your Fossil repository contains a built-in wiki that keeps all pages under version control. To create a new wiki page, simply go to the Wiki section of the page and click on "Create a new wiki page". The wiki syntax is mainly a reduced set of HTML, with some extra simplified formatting markup for common tasks.

For example, paragraphs are simply separated by line breaks. Lists are created by putting a '*' (unordered) or '#' (ordered) character at the start of the line, surrounded by two spaces on either side:

This is a list:
  *  item
  *  item

You can create hyperlinks using square brackets. Using this, you can link to other wiki pages by name:

[foo|Link to the Foo wiki page]
[foo]
[http://brandon.invergo.net|Brandon's Page]

You can use the <nowiki> tag to turn off wiki formatting for a block of text. Furthermore, the <verbatim> tag works like a combination of the HTML tag <pre> and the <nowiki> tag, for producing pre-formatted text.

When you create a new repository, you should at least edit the wiki home page. This is a special page that is accessed via the "[project] wiki home page" link on the Wiki page. This allows you to edit the landing page that people see when they first visit your repository.

There is also another special type of wiki page called an Event. These wiki pages show up in the Timeline and can be used as a version-controlled project blog, for example to announce releases. In addition to adding textual information, you can add tags to the event and give it a special background color to make it stand out in the timeline.

Tickets

Fossil contains a built-in bug tracking system, which is found under the Tickets section of the web interface. There you will find a link to create a new ticket or to get a list of all tickets. The bug tracker's basic functionality should be familiar to anyone who's used such a system in the past so I will not go into detail.

Note that you can refer to check-ins in a bug report (and vice versa) by specifying the check-in/bug number in brackets (i.e. [9EC520]).

Documentation

Fossil has a feature to allow any documentation files contained under version control to be viewed from the web interface. Simply point your browser to <webpage_root>/doc/<check-in>/<page>. You can specify any check-in, allowing you to see the documentation as it was at different points in time. If the file is contained in a sub-directory of the repository, include it in the URL after the check-in ID (<webpage_root>/doc/<check-in>/<sub-directory>/<page>)

Summary

As you can see, many of the familiar commands from other version control systems are available. In fact you could probably get up and running with Fossil rather quickly on your own. Nevertheless, hopefully this brief introduction will be useful to you as you become more comfortable with Fossil.

Further reading

I highly recommend reading through the documentation on the Fossil website. In particular, I recommend reading the detailed explanation of the concepts behind the system. I also suggest reading the section on Branching, Forking, Merging and Tagging for a more in-depth discussion on the topic.

Finally, a full-length book on Fossil is freely available and gives a nice introduction to the workflow and a more in-depth overview of the web interface.