I am writing this description to remember what to do when I reach the time to release one of my various projects1, that doesn’t happen often enough for me to remember the whole process and it is tedious enough to hate it when I forget a step. So, I decided to describe it.
First I will explain the version numbers I use. At one time I used the Linux version numbers but when I worked in KDE I’ve seen the beauty of their version numbers (if there’s place for beauty in such a thing).
Then I’ll explain how I create and maintain the various branches, tags and tarballs.
Let’s imagine I have been programming for a while and I have released version 1.0.0. For that you have created a branch 1.0 out of you main branch (called HEAD on CVS, trunk on Subversion, it has no particular name on Darcs). Calling it a branch is not just an accident, my project has been branched, it now has two different routes. One route is the main one, where I’ll continue to add new features, improvements, even documentation, until I am satisfied with it and release 1.1.0 (which would have created another branch, 1.1, which would be similar to the previous 1.0).
Now, what happens with branch 1.0 ? As a branch, it can evolve and grow, but unlike main, it doesn’t evolve towards new features, it evolves towards stability. As my users report bugs I solve them (both in 1.0 and main) eventually I consider that enough bugs has been solved and I release 1.0.1. Note that I haven’t made another branch, 1.0.1 is a release and it’s dead; it’ll never change. Unlike 1.0 and main which continue to change.
To be able to keep track of changes at each release I make a tag, the tag contains the number of the release. Branch 1.0 contains tags 1.0.0 and 1.0.1. Eventually it’ll contain 1.0.2, 1.0.3, 1.0.4, etc (until I stop supporting branch 1.0). I also tag each branch, so in main you’ll see tags 1.0, 1.1, 1.2, etc. Each branch is created out of the tag in main.
The last number that still lacks meaning is the first one, when do I release 2.0.0 ? That’s more arbitrary, but I believe that a good rule is to do it when you break compatibility (again, KDE’s way). That means that the libraries of 1.Y.Z are all compatible. If someone makes a program that works with 1.0.0 it’ll work with 1.0.1, 1.0.2, 1.1.0, 1.1.1, etc (the reverse might not be true, if a program uses a feature added in 1.1, then it won’t work with 1.0, but that’s OK). But when 2.0.0 is released, the program might need changes to work. For programs I use the same rule, but regarding configuration files (will it just pick and use old configuration files ?), GUI (is the GUI the same or I’ll have to re-learn it ?), protocol compatibility (will I still be able to connect to GameServer 1.2 with Game 2.0 or I’ll need to update GameServer ?), etc.
The process to jump to 2.0 is the same as jumping between 1.0 and 1.2, or 1.2 and 1.3. It is only the meaning of the change.
One of the main advantages of this method is that you can start to work on the new features of 1.1 while still supporting your users which run 1.0. You can even release 1.1 and still support 1.0. The only thing you can’t do is start to work on 2 while still supporting 1, that is, work towards 2.0.0 while you are at the same time working towards 1.5.0 (from 1.4.4). But that’s not a problem, extraordinary branching might help you in those extraordinary cases.
One last thing, I don’t wait till I have a polished 1.0.0 to release, I prefer the release fast, release often philosophy. My first releases are always 0.1.0 (adding features to 0.0.0, the non-existing project), then I go to 0.1.1, 0.1.2, 0.2.0, etc. as described above. The only difference is that since I don’t have a robust base to work on (like all the 1.Y.Z versions which worked over 1.0.0). So I consider the whole set of 0.Y.Z totally unstable and each version change like from 0.1.0 to 0.2.0 might break compatibility (as I seek the right design to solve the problem). The 0-versions are like pre-releases not ready for general consumption.
So, the meaning of a version with the layout X.Y.Z or Major.Minor.PatchLevel is:
- Major version, changes when a big re-design happened. It might break compatibility or anything (and possible bring hell to the users).
- Minor version, changes when new features have been added without big disruptions.
- Patch level, changes when some bugs have been fixed and it is worth it to make a release.
Doing it with Darcs
Darcs is an awesome version control system. It is the best I have ever tried and my choice every time I can choose. All my repositories are, of course, Darcs repositories and I recommend it to anyone who is interested.
Darcs has an extraordinary manual which both takes you step by step into learning Darcs and still it is a reference manual. Having say that I won’t explain how to use Darcs itself and only explain how to use it for the purpose of this document.
I also would like to take this space to do a bit of advocacy. Darcs can’t be considered mainstream yet. Some years ago all that we have was CVS and nobody ever thought about using something else. Then Subversion appeared and Subversion gave us two good things:
A better CVS, because that is what Subversion really is.
A chance for better designed version control systems to appear and be considered.
What are the main advantages of Darcs then ? First, its simplicity. It is much more simple that other version control systems with the same features (such as GNU Tla) and it is even more simple than CVS and Subversion while containing more features. That simplicity comes from the fact that Darcs does only versioning and nothing else. Much of what you do with complex CVS commands in Darcs it is done with simple common commands, like cp (copy) instead of CVS’s branching.
The arguments of Darcs are not cluttered by years of history and featuritis.
And last, it is de-centralized. That’s very important. Suppose you check out a program from a CVS repository. You made some changes. Until the owner gives you write access to the repository, those changes are not versioned. You go on making changes and they are very big already. You may end making copies just to save checkpoints until the owner gives you access to the repositories. Or you might make a mistake and loose everything. Another case where the problem arise is when you go out to the mountain, do some zen meditation on morning and code on evenings. You don’t have the distracting Internet access typical of a city, now, how do you commit to CVS or Subversion repositories ? Well, you don’t, you are stuck.
With Darcs your own copy, the one you checked out or downloaded, is a repository in itself. Whenever you do a record it happens locally. You can work for a year and make 1000 records without disturbing the world, without making those changes public, without Internet access, without repository write-access. When those changes are ready, you push them to a repository (the opposite is to pull them from a repository) and that’s it. It is actually very simple.
This allow us to have small records (Darcs’ commits) which are atomic2 and yet decide to make or changes public once we accumulated a whole new feature.
After my last usage of Subversion I’ve found another awesome feature of Darcs. You can record (commit or submit) only parts of a file. For example: I am working on a new function, it is so early that it is not even syntactically correct, it won’t compile. While working on I go to see another similar function for reference and while reading it, oh! how terrible. The old function has a nasty bug on it and I told my boss to test my program, how embarrassing. I correct the bug, it was so simple, it is only a typo. But when I am going to record the changes I find out that I have to deal with all that new yet-unfinished code. In Subversion or CVS I am stuck. I have to go and comment out all the new code, revert all changes in the rest of the file and do the commit. In Darcs I just do the record. Darcs asks me interactively about each particular change in the file: ”Do you want this patch ?”. With that I only include that typo and continue working happily.
Darcs uses this interactivity all the way. After you have a set of records you do a push, during push Darcs asks you for each record, so you have a great deal of granularity about what you do, what you record, and what you let the world see. The interactive sections of Darcs are very easy to understand and handle and Darcs knows what patch depends on each other so it won’t let you make nasty mistakes.
Darcs is awesome.
Go to the directory where we have our sources:
~$ cd ~/ProjectName ~/ProjectName$
Don’t forget to do the usual chores of a release, like opening the README and changing the version numbers.
Create the branch
We create the branch by making a tag:
~/ProjectName$ darcs tag What is the version name? 0.1 Finished tagging patch 'TAG 0.1' ~/ProjectName$
Leave that directory and fetch the recently created branch (in this case, it was 0.1):
~/ProjectName$ cd .. ~$ darcs get -tag=0.1 ProjectName ProjectName-0.1 Applying patches to the "working" directory... .................................................................................................................. Finished getting. ~$
Create the release
Create the tag for this release on the branch previously created.
~$ cd ProjectName-0.1 ~/ProjectName-0.1$ darcs tag What is the version name? 0.1.0 Finished tagging patch 'TAG 0.1.0' ~/ProjectName-0.1$
Here the process can be done in two different way. The one described here has the advantage of being decentralized (you don’t have to be connected to the Internet) the other one has another advantage. If you have Internet access go to section sub:Pushing-your-changes, otherwise, do this:
~/ProjectName-0.1$ cd .. ~$ darcs get -tag=0.1.0 ProjectName-0.1 ProjectName-0.1.0 Copying patch 58 of 58... done! Applying patches to the "working" directory... .................................................................................................................... Finished getting. ~$ tar cvfj ProjectName-0.1.0.tar.bz2 ProjectName-0.1.0/ ProjectName-0.1.0/ ProjectName-0.1.0/doc . . . ProjectName-0.1.0/AUTHORS ProjectName-0.1.0/ChangeLog ProjectName-0.1.0/COPYING
And that’s it, we are done, ProjectName-0.1.0.tar.bz2 is ready to be distributed.
Pushing your changes
After we create the tag on section sub:Create-the-branch we can push that tag to the server:
~$ cd ProjectName ~/ProjectName$ darcs push Pushing to firstname.lastname@example.org:/home/pupeno/ProjectName... Mon Apr 17 21:05:15 UTC 2006 José Pablo Ezequiel Fernández Silva <email@example.com> tagged 0.1 Shall I push this patch? (1/1) [ynWvpxqadjk], or ? for help: y Finished applying... ~/ProjectName$ cd .. ~$
We might also want to create the branch on the server the same way we did it on our own workstation:
~$ ssh firstname.lastname@example.org email@example.com:~$ mkdir ProjectName-0.1 firstname.lastname@example.org:~$ cd ProjectName-0.1/ email@example.com:~/ProjectName-0.1$ darcs init firstname.lastname@example.org:~/ProjectName-0.1$ exit logout Connection to software.pupeno.com closed. $
We only do a ”darcs init” and not a get because we are going to push everything from our own workstation after creating the branch on and the release on section sub:Create-the-release:
~$ cd ProjectName-0.1/ ~/ProjectName-0.1$ darcs push email@example.com:/home/pupeno/ProjectName-0.1 Sat Jan 21 00:24:52 UTC 2006 José Pablo Ezequiel Fernández Silva <firstname.lastname@example.org> * Initial record of ProjectName. Shall I push this patch? (1/57) [ynWvpxqadjk], or ? for help: a Finished applying... ~/ProjectName-0.1$ cd .. $
On section sub:Create-the-release it was mentioned that there’s an alternate way to create the release, this is it. When getting the release itself I do it from the public server over http. That is because I am going to package the _darcs directory as well which allows anyone to download version 0.1.0 and after a ”darcs pull” get the latest from the 0.1 branch:
~$ darcs get -tag=0.1.0 http://software.pupeno.com/ProjectName-0.1 ProjectName-0.1.0 Copying patch 58 of 58... done! Applying patches to the "working" directory... ................................................................................................................... Finished getting. ~$ tar cvfj ProjectName-0.1.0.tar.bz2 ProjectName-0.1.0/ ProjectName-0.1.0/ ProjectName-0.1.0/doc . . . ProjectName-0.1.0/AUTHORS ProjectName-0.1.0/ChangeLog ProjectName-0.1.0/COPYING
And that’s it, we are done.
These tasks are not extrictly necessary but since I perform it for every release I make I want to list them here:
Add the project and/or the release to Freshmeat, a popular site for publishing software.
Add the project to the Free Software Directory, it is not very popular, but it is a good statistic tool about how much free software is out there.
Make announcements on the related mailing list. Prefixing it with [ANN] is a good idea. Don’t forget to add the URLs to the web site.
Software is a bit like art, and every developer, like every artist, has his/her own way of doing it and releasing it. The difference is that we end up depending on software and that means that when it is handled in an inconvenient way, it really bother us. I call for the good sense of the community of software developers to pick relatively good release cycles that doesn’t bother the users (like just using dates, or no numbering at all and not being clear about branches).
And if we can all agree on what the numbers of a version mean then it is less problems for the users. I would call for this scheme to be adopted because I’ve seen it is the one more spread.