First post in a while! It's a tech article, too. Moving on....
There's been some discussion on the VDrift forums recently of moving to a distributed version control system, most likely 'git', and speculation that the project should have a concrete workflow to best utilise this technology. Here's my theory on how the project (and others like it) might best work based on the constraints given so far.
Part One deals with how the main development tree could be handled.
The project leaders have expressed that they wish to have three major branches: A "current release" branch, a development branch, and a bugfix/staging branch for the next release — for a DVCS I personally do not see the wisdom in differentiating between development and staging at this level, however this path has the support of consensus. For the purposes of this article, I will refer to these branches as stable, master and staging respectively. The reasons for this are largely related to what an experienced git using developer would expect to see, especially in the case of making master the development branch.
The master Branch
The purpose of master is to provide a centralised source with which developers can synchronise their local repositories and ensure their code applies to the most recent changes. This is the trunk of the project, if you prefer that terminology. The version of the source in this branch is, ultimately, where new features go to be tested by the wider community.
Any changes which land in this branch (normally via a pull request) should first be reviewed by at least one peer for validity and style before being added. Given a pull request like the following (Ignoring GitHub features for the moment):
The following changes since commit d2662ab2f0c70994c7bcef903b08fc5245e5e4a9:
SOME AWESOME SUPER DOOPER CHANGES
are available in the git repository at:
git://git.example.com/~uberman/vdrift.git asdooper
A maintainer could, after making sure their copy of master is up to date, add uberman's tree as a remote with:
$ git remote add uberman git://git.example.com/~uberman/vdrift.git -t asdooper
Then retrieve a copy of the branch:
$ git fetch uberman
$ git checkout uberman/asdooper
Which will land them in a "Detached HEAD" state in which they can review the code. Ideally the code is correctly styled, there are no obvious bugs and the last action that "uberman" performed was to merge the most recent version of master, resolving merge conflicts as he did so. In this case, if the reviewer is happy with "SOME AWESOME SUPER DOOPER CHANGES" (which they probably shouldn't be if that commit message is in there), they can simply switch to the master branch:
$ git checkout master
Then perform a merge:
$ git merge uberman/asdooper
Where uberman's diligent merge conflict resolution will prevent the reviewer from having to do any further surgery before pushing it to the main repository:
$ git push origin master
If there is some major problem with uberman's request, such as a failure to compile, changes being against too old a version of master, or unreadable code, the preferred option should be to refer the matter back to uberman, asking him to address the problem before requesting another pull. However, simple corrections such as to minor formatting issues could be performed by checking out to a new local branch (it may be preferrable to some to perform this step anyway):
$ git checkout -b asdooper uberman/asdooper
Then performing the required changes before merging the newly created branch into master; I recommend this to maintain clarity as to why the changes had to be made, though it is technically just as valid to perform the changes between merging to master and pushing the changes.
The staging Branch
This branch, while not strictly necessary in a DVCS model, facilitates avoiding code freeze during the release process. If you imagine a release cycle where new features are added at the beginning of the cycle, or while the "merge window" is open, a virtual feature freeze is put into effect merely by merging master into staging:
$ git checkout staging
$ git merge master
At this point, development can continue in master, but only bugfixes should be applied to staging (and the majority of those should also be applied to master). Importantly in a given release cycle this branch should work with precisely one revision of the data repository — in fact it should be possible to provide data snapshot to be used for the release at the point of closing the merge window.
Periodically during this phase of the release cycle, candidates for release may be tagged, for example to tag the first release canditate for a hypothetical January 2011 release (this is not a formal recommendation of versioning scheme):
$ git tag 11.1-rc1
It is one of these tagged release candidates which should eventually be merged into stable.
The second role of staging is to hold post-release bug fixes before applying them to stable. Any bug fixes that are applied during the feature freeze phase of the release cycle should thus be retained in staging until the merge window for the next release closes. It is important to note, however, that the commit for any bugfix for which a different solution was applied to master for any reason should be removed from staging with git revert before master is merged into staging for the next release — this reduces the number of dirty merges that are applied to staging and stable.
The stable Branch
There are exactly two types of update to this branch that should occur, and both are performed by merging. Using the hypothetical January 2011 release as above, the "final" release could be derived from 11.1-rc3 by first applying the version tag:
$ git tag 11.1 11.1-rc3
Merging it to the stable branch:
$ git checkout stable
$ git merge 11.1
Post-release bugfixes should be applied using a similar method, with a tag derived from the "final" release. For example, for the first set of bugfixes first tag the contents of staging:
$ git tag 11.1.1 staging
Then merge into stable:
$ git checkout stable
$ git merge 11.1.1
That's pretty much it for maintaining the main tree. The general guidelines are that almost every change to development should be a merge or a minor correction to merged changes, staging should have development merged into it once every release cycle and further changes should be cherry-picked or merged from dedicated bugfixing branches, and every change to master must be perfomed through a merge. NO REBASING EVER!!!