I basically grew up using git, which means that the way my brain thinks about version control is fundamentally git-shaped and I don't find its command set as confusing as most people do. It's been pretty comfortable for me for a long time, and although I've tried a handful of other version control systems (mostly out of curiosity), I've never run into one that I liked more than git. Over the years, I've tried:
So I'd basically been stuck with git. A couple of years back, I came across Fossil:
Had a quick read over it, mentally filed it as Yet Another Distributed Version Control System, and forgot about it and kept using git... but a couple of things about git kept bothering me. One big one is that git, despite being a distributed VCS, has quite a centralized ecosystem. Your source tree is in this very pleasant replicated versioned store, but your bugs, your wiki, your project website, your discussion groups - those are all still stored in one or many centralized services. While it's easy to move a source repo from github to sourcehut, moving the issue tracker and wiki isn't so straightforward. Another is that because git doesn't actually store that ancillary "project stuff", it isn't versioned at all, and changes to it don't show up in history - you can't, say, mark a bug as fixed in the commit that fixes it without some kind of external automation.
This is one of the things that I really liked about Monotone, back when it existed. Monotone stored, versioned, and signed all changes to all of your project state as part of its repository. Neat! Doing that also meant Monotone had to solve the harder UI problem of how to present all of that stuff, and impose at least some policy decisions about what shape it was. Git stays away from that and just versions source code, which makes it simpler to understand (I guess) at the price of every real use of git having a lot of other stuff stapled to it.
So, into Fossil. Fossil is an integrated system like Monotone was, but also does a bunch of stuff differently from both Monotone and any other VCS I've ever used. This post is about my experiences with it.
In git, the default place for your repository to live is inside the source
tree itself, in a directory usually named .git
. That's
convenient for some things, like being able to move code around with its
history, but inconvenient for other things, like being able to check that
all of your git repos have been recently pushed. Fossil instead keeps its
repository state in a separate file, which can live anywhere you like; the
documentation advises you to keep all your fossil repos in one directory,
which they call the "museum" (get it? that's where valuable fossils
go!). Each fossil repo is a single sqlite database, rather than a tree of
files in bespoke formats, which has some interesting consequences, like
it being very easy to make encrypted fossil repos. The fossil tooling also
keeps track of all the repos you've ever opened or used on your system,
and there's a command called fossil all
which runs a
subcommand on every fossil repo you have, so you can do something like:
fossil all sync
Git has this idea that your repository is a clone or fork of some existing
upstream repository, which you periodically push and pull changes to or
from. That upstream repository is generally a public one hosted somewhere
like github, and it is a different kind of thing from your local checkout:
it has a web frontend, it has remote access via ssh or https, and it
probably doesn't have an actual working tree, only the contents of the
.git directory. In Fossil, all repos are fundamentally of the same type -
they are all "just" sqlite databases conforming to a specific schema that
know how to bidirectionally sync changes with each other. The fossil repo
you have on your laptop is one fossil serve
away from being
public. In fact, fossil ui
(the fancy local ui for fossil)
works by just starting that server bound to localhost and with auth turned
off. Syncing is done by either hitting a web API exposed by that server,
or by sshing to it, running the fossil
program, and feeding
it changes - which is exactly what fossil sync
does. Fossil's
default behavior is actually to automatically sync changes bidirectionally
whenever you make them, which I think is quite nice.
One of the things new users often find difficult about git is its concept
of "staging". In a given git repo, you can have changes in your work tree
that are either "staged" for commit or not, and if you run git
commit
, only those changes that were staged are
incorporated. Subcommands like git add
stage changes, but a
lot of people just use git commit -a
, which automatically
stages every unstaged change before committing. Personally I think this is
a bad default behavior - occasionally one wants to commit only a subset of
changes, but it's way more common to accidentally forget to stage a change
(like adding a new file) and produce a broken commit. Fossil's default is
to commit all changes in the working tree, although it isn't so aggressive
as to automatically add new files to the repo. If you really want to
commit only certain files, you have to specify them, like fossil
commit file1 file2 file3
. Fossil has no concept of staging changes
in the git sense, which makes it easier to understand what's going on (imo).
Fossil's UI is nicer in most ways. I know this is semi-subjective, but I
am a longtime git user and I still think fossil's is better
:). Specifically, fossil stays away from the unpopular git-ism of having
the same command serve multiple purposes, like git checkout
does, and instead generally has commands that do a single thing. It also
has good and concise help texts for every command, whereas git help
whatever
tends to open a combination manual page and PhD thesis
that can only be understood by someone who has meditated deeply on the
fundamental nature of directed graphs.
Fossil is also a lot chattier than git, which is either good or bad
depending on your tastes. For example, fossil status
tells
you stuff like "the path to the fossil config database" every time you run
it, which seems excessive. Still, it's kind of nice that (for
example) fossil sync
tells you how many artifacts it's
sending and how much network bandwidth it's using.
Here's how I use Fossil, in case you find yourself wanting to try it. The documentation on the Fossil wiki is excellent so go have a look if you're curious - there are many possible ways to use it.
First, we make a new repository:
~/museum/project.fossil
. Next, we'll want to make a
checkout of the repo somewhere so we can actually work on it (note that
it's perfectly file to have multiple checkouts of the same repo). We do
that like so:
fossil config
instead. Anyway, once we're happy with
the repo state, we can do some creating:
fossil timeline
we'll see
it shows up in there. If we happened to want to go back to an old version,
or see an old version of any specific file, we'd use fossil
update
or (more rarely) fossil checkout
to do that.
So far so good for the local workflow, but we want to make copies and push changes to them! Ideally remote copies, in case of a local disaster. To do that, we'll install fossil on some convenient remote machine, then on our local machine, add it as a remote:
fossil serve
on the remote machine to expose the repo to
the internet :)
I like Fossil. It makes a bunch of different choices than git, and in general I've found it a much better fit for the kind of small-scale project work I like doing. Git is fast and performs well even on massive repos, but it gets that speed by being complicated, arcane, and clever. Fossil feels simple and straightforward in comparison, and has the features I want for my own work. I've switched to it for all my own project work already, although I'm sure I will still be using git at work forever. Thanks for reading!
Postscript: this is my first HTML blog post, rather than plain text. What do you think? Is the CSS too much?