Branch Guidelines

From Qt Wiki
Revision as of 08:50, 14 December 2018 by Ossi (talk | contribs) (→‎Closing a branch: deletion procedure)
Jump to: navigation, search

This articles describes the guidelines for branches in Qt repositories. They do apply to most Qt 5 repositories, although some still follow the traditional "master" branch model (or other guidelines).

Currently existing branches are documented in Branches.

Qt branching scheme

Note that the markup is highly relevant to the content of this section. Actual branch names are bold, branch categories are italic.

There is one permanent branch and four main branch categories:

  • dev: the permanent, always unfrozen development branch. It contains alpha-quality code that is ready to go into beta testing at any time, which means that each feature is complete, mostly working, documented, tested, etc. Note that for qt-creator [1] this branch is named master.
  • stable: the current feature-frozen stabilization branch. It corresponds with one minor release series and consequently has a name like 5.3. It is created from the dev branch at feature freeze (at which point it is still referred to as the stabilizing branch), and is expected to reach beta and later release quality (at which point it supersedes the previous stable branch). stable branches are permanent once created, but usually only the latest one is actively maintained.
  • release: the current deep-frozen branch. It corresponds with one actual release and consequently has a name like 5.3.1. It is (usually) created from the stable branch when a release is imminent. It is terminated by a release tag, after which the branch is merged and deleted.
  • lts: the current LTS branch(es). These are named like stable branches, but are cherry-picked to only.
  • lts-release: analogous to release, but branched from lts.

The default branch (the one that gets checked out on a new clone) is stable. The intention is that new developers who clone Qt repositories for the first time are presented with a stable codebase they can begin working with, bugfixing and even developing new features against.

  1. All new features go into dev, possibly via the detour of a feature branch (see below).
  2. All bugfixes go into the "most frozen" maintained branch which they are relevant for. As a general rule, we do not backport. Mismerged fixes for P1+ issues may be cherry-picked (with option -x) in exceptional cases.
  3. The release team guards that only release-critical fixes are applied to the release branch.
  4. The branches are periodically merged from "most frozen" to "least frozen": release => stable, stable => dev, and lts-release => lts
    • Do not force merges - ask an admin for a fast-forward if necessary
  5. We follow a hybrid time- and quality-based schedule:
    • time-based: time between feature freezes should be 6 months in the long-term. In the short term, it might be slightly less or slightly more, if the Qt Project wishes to, in order to align our releases with our downstreams.
    • quality-based: maturation of a dot-oh release
    • hybrid: patch releases should be released timely (e.g. every 2 months), but never in worse quality than a previous release
  6. the Qt Project is the only entity allowed to assign Qt version numbers
  7. with the exception of agreed upon long term support (LTS) versions, the Qt Project will not maintain Qt versions before the one currently designated as the stable branch. Other parties may provide maintenance if so they wish and the Qt Project should welcome those branches in its infrastructure, but the branches should not be in the main Qt repositories. Releases made out of those branches or others outside of the Qt Project should be clearly marked and should avoid using confusing numbering. For example, a release could be the last official Qt version plus the number of commits applied on top, plus the company's name. (e.g., 5.2.2-digia-125)
  8. the exception to the above are security or otherwise very critical fixes, which may be applied to previous releases, directly on top of an existing tag and triggering a new release. That is, if it's relevant enough that everyone should update, then there's no reason not to make that a Qt release. For example, with 4.7 now and the security fix that was released today, we should make a new Qt 4.7 release immediately and tag.

Where to push a change?

For official guidance on which changes belong in which branches, see QUIP 5.

New development should happen in dev or in a feature branch.

Only non-destabilizing changes go to the stable branch, using these criteria:

  1. Fixes to regressions against a previous "recent" version of Qt (less than 2 or 3 years old).
  2. Fixes in new features introduced in the most recent minor version.
  3. Critical fixes (P1/P0): security, crashes or data corruption (that are not candidates for the release branch).
  4. Compilation fixes or adaptations required for different versions of the compilers or upstream libraries.
  5. Documentation changes.
  6. Autotest fixes and additions.
  7. P2 fixes when there is a good reason/need

Guidelines for your own branches

These guidelines will help you to avoid creating a forest of branches of development and merging hell. Stick to simple rules and you will find it easier to maintain and merge separate code lines.

Creating a new feature branch

The first rule is that you should avoid creating public branches if possible, as they come with more or less significant administrative overhead.

If you merely want to back up some work in progress, the best option is your personal namespace on Gerrit. See the git-ppush tool from the qt/qtrepotools repository.

Secondly, Gerrit reviews are actually branches in themselves, and it is possible to comfortably maintain a series of up to around a dozen commits this way.

You probably do actually need a public branch if a) the above does not apply because of size, b) multiple people need to write code, or c) you need feedback from/for the CI system early on.

When creating a new branch, you need to decide on a base branch to start off of. Properties of good base branches are:

  1. Stable
  2. Long-lived
  3. Maintained

This typically means one of the mainline branches, that is stable or dev.

Note: The quality standards for feature branches that are ultimately merged are the same as for their target branches. That means that every commit must fully comply with the commit policy, be reviewed by the respective domain experts, and if the target branch is under CI control, so must be the feature branch.
If you intend to take some "shortcuts" during development, your branch becomes a "throw-away" branch: it will not be merged as-is. More on that below. As a matter of fact, most of our branches fall into this category.

Feature development outside of Gerrit

In principle it is possible to create a personal clone of the project on - all projects from are mirrored there.
However, there is a caveat: Should you be working on a feature with several other people, each of them must sign the Qt Project CLA, and it should be possible to track who contributed what. Therefore we strongly recommend that for team works you use Gerrit directly. In the end, you will need to submit the work there anyway …

Maintaining and updating a branch

Complementary to "don't branch unless necessary", "don't merge unless necessary" is also true. Only merge if you depend on specific changes from another branch (in which case a merge is better than cherry-picks).

As a rule of thumb, try to pull updates only from your base branch. Don't pull changes from other feature branches unless you decide to merge into that branch.

Take care to never merge local commits (which have not been integrated on Gerrit yet). Do not ever rebase while you have a local merge. Use gitk --all to verify that the parent commits of the merge correspond to upstream heads.

If you are working with a throw-away branch, you can merge as often as you want, and it is strongly recommended that you rebase the branch as soon as you get a conflicted merge. You should also try to clean up the history as often as possible - the longer you wait, the more painful it will be in the end.

Combining multiple branches

A common situation is that you have two separate feature branches and you would like to try out how both of them behave together. Simply create a third, short-lived branch based on the one feature branch and with the second one merged into it. If in the process of testing the combination of both branches you discover that additional changes are needed, then you can commit them first into your experimental branch. Afterwards use git-cherry-pick to pick them into one of the two originating branches and delete your experimental branch again. You should have git rerere.enabled (and possibly rerere.autoupdate) set to true globally, so repeating the merge of the experimental branches at a later point is a relatively cheap operation.

Closing a branch

When you're done with your feature, decide where to merge it back. Your options include:

  1. The most straightforward target is your base branch. Merge into it and delete your feature branch.
  2. You may also choose to merge your feature branch into another feature branch, and then close yours.
  3. If you would like to take only the changes that you've done and merge them into another branch, without including the changes of your base branch, then you can use git rebase. It can be a complex operation and works best on relatively short branches.

For throw-away branches, the procedure is different:

  1. Make sure the history of the branch is clean (ensure commit policy compliance, in particular atomicity (split/squash as necessary), fix commit messages, etc.). As noted above, you should not wait with this till the end.
  2. Re-submit every change for the target branch, and have it reviewed and integrated change by change, as if there never was a separate branch to start with.
  3. The specifics of the procedure need to be decided on a case by case basis. The Gerrit admins will help you with that.

All branches that go out of use (with the exception of stable branches) should be deleted. Abandoned branches and throw-away wip branches may be archived in the respective repository's refs/old/ namespace.