This is an old revision of the document!
Note: The first part of this page aims to explain the conventions behind Subversion repository organisation, and use various simplified repository structures to explain this. The recommended repository layout can be found at the end of this page.
In CVS, branches and tags are first class entities – CVS contains several commands for manipulating these. Subversion does not. Instead, Subversion's support for cheap file/directory copies within the repository is used to support the notions of tagging and branching.
A number of conventions have developed to support this. These conventions are not enforced by any of the base Subversion tools, but they can be enforced by pre-commit hooks in the repository (if it is felt that these decisions need to be programmatically enforced).
$REPO and commits
Subversion repositories are referred to with URLs. In the examples that follow, $REPO is assumed to refer to the root URL for the given repository.
Any Subversion command that specifies the full URL operates against the repository, which often means a simultaneous commit. Commands that just specify local files or directories in the working copy change the working copy, but they must be explicitly committed.
For example, this example copies one file to another in the repository – the data in the file does not need to travel to the client, so the operation is quite quick, as it happens entirely on the repository host.
% svn copy $REPO/file1 $REPO/file2
The user is then prompted for the commit message for this commit (or it can be specified with a -m option on the command line) and the commit completes.
Alternatively, the user could do this:
% svn checkout $REPO/file1
% svn copy file1 file2
% svn commit file2}}}
As you can see, that process takes more commands, and involves transferring the file data to the client.
== trunk/, tags/, and branches/ ==
This is the most pervasive convention. Code that corresponds to CVS HEAD is placed in the {{{trunk/}}} directory. So in the ordinary course of operation, if a user
When a tag needs to be made a copy is made of {{{trunk/}}} to an appropriately named directory under {{{tags/}}}.
{{{ % svn copy $REPO/trunk $REPO/tags/name-of-tag}}}
There is nothing in Subversion that directly prevents someone from checking out the {{{tags/name-of-tag/}}} directory, and then committing changes. This would appear to violate the spirit of tags, in that they should be immutable. However, CVS supports this operation too (c.f., "sliding a tag"), and an appropriately written {{{pre-commit-hook}}} script that interrogate the commit, and refuse to allow it if the commit includes a directory under {{{tags/}}}, perhaps with some additional checks (e.g., members of the Release Engineering group would be allowed to create/change files under {{{tags/}}}, but no one else).
It follows, therefore, that a branch is just a tag to which people are allowed to commit. And that is exactly how Subversion operates. To create a branch the command is:
{{{ % svn copy $REPO/trunk $REPO/branches/name-of-branch}}}
The user can then check files out from the {{{branches/name-of-branch/}}} directory, and commit as normal.
Since these are just directories in the repository there is nothing inherent that prevents someone from, for example, creating a branch from a tag. For example,
{{{ % svn copy $REPO/tags/name-of-tag $REPO/branches/name-of-branch}}}
== Multi-project repositories ==
There is nothing that enforces the idea that a repository can contain a single project. In fact, it's commonplace not to. A typical multi-project repository layout might look like this:
{{{
$REPO/project1/trunk/...
$REPO/project1/tags/...
$REPO/project1/branches/...
$REPO/project2/trunk/...
$REPO/project2/tags/...
$REPO/project2/branches/...
$REPO/project3/trunk/...
$REPO/project3/tags/...
$REPO/project3/branches/...}}}
That layout is common if branching and tagging operations are normally applied to one project at a time, or if the projects are somewhat independent. For an example of this see the Apache Subversion repository -- http://svn.apache.org/viewvc/. This contains multiple independent projects that branch and tag at different times. If you drill down to the individual projects you'll see per-project {{{trunk/}}}, {{{tags/}}}, and {{{branches/}}} directories.
An alternative layout, more appropriate for multiple projects that are tightly coupled and that normally branch and tag at the same time would be:
{{{
$REPO/trunk/project1/...
$REPO/trunk/project2/...
$REPO/trunk/project3/...
$REPO/tags/project1/...
$REPO/tags/project2/...
$REPO/tags/project3/...
$REPO/branches/project1/...
$REPO/branches/project2/...
$REPO/branches/project3/...}}}
A good example of this is the KDE Subversion repository at http://websvn.kde.org/. As you can see, there are top level `trunk/`, `tags/`, and `branches/` directories, and every KDE sub-project sits under there. This allows the KDE team to produce a consistent snapshot of the entire project when they plan a new release.
== Vendor code ==
A separate set of conventions exist for managing vendor code that will form part of the main project.
Typically, unmodified vendor code is imported in to sub-directories on a special branch. Instead of being a sub-directory of `branches/` this is commonly called `vendor/`. The very first time the code is imported it is copied to the project's `trunk/`. After subsequent imports the differences between the previous version of the vendor code are merged in to the code on `trunk/`.
A worked example may make this clearer.
Consider a project that includes, as part of it's release, the code to Sendmail.
The project exists in $REPO/trunk.
The source code to Sendmail 8.13.7 is imported in to $REPO/vendor/sendmail/current. This directory is then copied to $REPO/vendor/sendmail/8.13.7.
The Sendmail source code is copied to an appropriate subdirectory of the project's trunk/. For example;
{{{ % svn copy $REPO/vendor/sendmail/8.13.7 $REPO/trunk/contrib/sendmail}}}
The code on $REPO/trunk/contrib/sendmail can then be modified as necessary to suit the needs of the project.
Now suppose that Sendmail 8.13.8 is released. The source code for this version is then imported in to the repository, and placed in $REPO/vendor/sendmail/current. As part of this process any differences between 8.13.7 and 8.13.8 are merged. The `current/` directory is then copied to `8.13.8/`. At this point the `vendor/sendmail` tree contains three subdirectories, for the two different versions of Sendmail, and `current/`.
The `svn merge` command can then be used to merge the changes between the vendor 8.13.7 and 8.13.8 releases on to the project's trunk.
{{{ % svn merge $REPO/vendor/8.13.7 $REPO/vendor/8.13.8 trunk/contrib/sendmail}}}
This takes the changes between 8.13.7 and 8.13.8 and merges them on to `trunk/contrib/sendmail`. The committer can then review these changes, resolve any conflicts between the imported code and any local modifications, and then commit the merged code.
That probably sounds more complicated than it really is. There's a more detailed write up at http://jc.ngo.org.uk/trac-bin/trac.cgi/wiki/ImportingIntoSubversion -- note that that assumes certain conventions that I'm following in my personal repo which we probably wouldn't follow in the FreeBSD Subversion repo.
== Proposed layout ==
So, after all that, here's a proposal for a layout. This does not need to be set in stone, and I'm sure we can bikeshed the directory names to death. Still, it's a start.
{{{ / freebsd /
src / {trunk, branches, tags }
www / " " "
doc / " " "
ports / " " "
/ vendor /
sendmail /
8.13.7 /
8.13.8 /
current /
bind /
/ projects /
trustedbsd / {trunk, branches, tags}
snmpng / " " "
kse / " " "
/ user /
n / nik /
r / rwatson /
Lets walk through the rationale for this.
The core FreeBSD distribution is kept under a single freebsd/ root. This makes it easy for third parties to easily mirror the bits of FreeBSD that they want. For example, mirroring freebsd/src would be equivalent to using CVSup to fetch src-all. Checking out from freebsd/src/trunk would be equivalent to checking out CVS HEAD.
Mirroring freebsd/ would be like fetching src-all, ports-all, doc-all, and www using CVSup.
Now, at the moment we don't branch trees like ports/. That's (I think) more to do with the time and effort this takes to manage branches under CVS than because we couldn't benefit from branched ports. However, even if we decide not to branch ports (or the other trees) right now, we should probably still use the trunk/ subdirectory. It keeps things like directory depth consistent, which makes it much easier to automate things, or describe the repo.
This scheme allows the four component parts of the project to branch and tag at different times. This is common during the release process, where, for example, ports/ will have a change freeze that has different dates to the src/ code freeze.
…
XXX - nik plans to pick this up later