My main programming hobby project will soon celebrate its tenth birthday, so I thought a few notes on how I structure my VCS repository might be of interest. The VCS I have been using since the beginning is Subversion. (When I started, Git had already been released, but was really not that popular yet.) So while some details of this article will be specific to Subversion, the general concepts should be applicable elsewhere as well.
When it comes to the structure of a repository, Subversion does not impose anything from a technical point of view. All it sees is a kind-of file system, with all the pros and cons that come with the simplicity of this approach. It makes it easy for people to start using it, which is really good. But it also does not offer help for more advanced use-cases, so that people need to find a way how to map certain requirements onto that file system concept.
As a result, a convention has emerged and been there for many years now. It says that at the top-level of the project there should be only the following folders:
trunk
: Home of the latest version (sometimes called the HEAD revision)branches
: Development sidelines where work happens in isolation fromtrunk
tags
: Snapshots that give meaningful names to a certain revision
You will find plenty of additional information on the subject when searching the Internet. I can also recommend the book “Pragmatic Version Control: Using Subversion“, although it seems to be out of print now.
With these general points out of the way, let me start with how I work on my project. There are only a few core rules and despite their simplicity I can handle all situations.
- The most important aspect for the structure of my SVN repository is that all active development on the coming version happens at
trunk
. See this article for all the important details, why you really want to follow that approach in almost all cases. - Once a new version is about to be released, I need a place where bug-fixes can be developed. So I create a release branch (e.g.
/branches/releases/v1.3
) with major and minor version number but not the patch version (I use semantic versioning). From this release branch I then cut the release (v1.3.0 in this case) by pointing the release job of my CI server to the release branch. - Once the release is done, I create a tag that also includes the patch version. In this example the tag will be from
/branches/releases/v1.3
to/tags/releases/v1.3.0
. - Now I return to working on the next release (v1.4) by switching back to
trunk
. - Bug fixing happens primarily on
trunk
with fixes being back-ported to released versions. There are cases when this is not practical, of course. The two main reasons are that significant structural changes were done ontrunk
(you do refactor, don’t you?) or another change has implicitly removed the bug there already. But that is the exception. - When a bug-fix is needed on a released version, I temporarily switch my working copy to the release branch and do the respective work there. Unless the bug is critical I do not release a new version immediately after that, though. So this may repeat a few times, before the maintenance release.
- The maintenance release is then cut, again, from
/branches/releases/v1.3
. And after that a new tag is created to/tags/releases/v1.3.1
.
Those rules have proven to be working perfectly and I hope they will continue to do so for the next ten years. I have been quite lucky in that, although for me this is still a hobby project, the result is used by many global companies and organizations in a business-critical context. There are at least 11.000 installations in production that I am aware about, so I cannot be casual about reliability of the delivery process.