Branch Guidelines

From Qt Wiki
Revision as of 11:47, 25 April 2022 by EdwardWelbourne (talk | contribs) (Document how to suppress the inanity-'bot warnings about cherry-picks.)
Jump to navigation Jump to search


This articles describes the guidelines for branches in Qt repositories. They do apply to most Qt 5 repositories, although some might follow 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 dev. The intention is that new developers who clone Qt repositories for the first time are presented with the most up to date code for bug fixing and developing new features against.

Release schedule and versions

  1. 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
  2. the Qt Project is the only entity allowed to assign Qt version numbers
  3. The release team guards that only release-critical fixes are applied to the release branch.
  4. 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)
  5. 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 16.

  • All new features go into dev, possibly via the detour of a feature branch (see below).
  • All bugfixes go into dev, with the exception of fixes that are not applicable to dev. Those fixes go into the actively maintained stable branch.
  • Bug fixes may be cherry-picked down into the currently maintained stable, lts, and release branches, once the fixes have been merged into their original target branch.

Only non-destabilizing changes may be cherry-picked down from dev, 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.

How to cherry-pick

Cherry-picking can be done semi-automatically, using the Pick-to: footer in the commit message. The footer specifies a space-separated list of branches into which the change should be cherry-picked once the patch has been successfully merged.

Pick-to: 5.15 5.12

Each branch listed in the  footer will generate a cherry-pick patch on the target branch based on the latest revision of the merged patch. The owner of the new patch will be the Qt Cherry-pick Bot, and the author of the original commit will be the author of the cherry-pick. If the cherry-pick bot does not encounter any conflicts, the commit will be automatically approved and staged.

In case of any issues during the process, the original author will be set as the assignee of the cherry-pick patch, and reviewers will be copied from the original change. Author and reviewers will be notified about the issue, and can amend the patch like any regular change review.

To cherry-pick changes manually, use the -x option, edit out any Pick-to: footers, and don't change the gerrit Change-Id footer. The cherry-pick must be done from the commit that has been integrated into the published branch, not from a branch in a local clone, whose sha1 will be different from the published version. Even when the change is a complete rewrite, it shall reference the original commit in its commit message, using a "fake" cherry-pick footer following the pattern (adapted from commit <sha1>). Submit the patch for review like any other change.

Alternatively, use the gerrit UI to cherry-pick a change into target branches.

The location of the Cherry-Pick option in the drop-down menu of a merged commit.
The gerrit-dialog for providing the target branch

Suppressing warnings against cherry-picks

When you cherry-pick a commit from dev back to some older branch, the sanitize-commit script may complain that the commit shall be merged back to the branch it came from or that cherry-picks are discouraged. These warnings exist for the benefit of other projects (e.g. Qt Creator) using the same infrastructure but using a merge-based workflow. They can be turned off by running

git config --global sanity.module-name.with-pickbot true

with the name of the git module (e.g. qtbase</kdb>) replacing module-name. (If you get such a warning from the Sanity-bot on Gerrit, it means the module is in fact using merges, so you should clear the same option, or set it to false, if you have it set.)

Working on modules that have not yet been ported to dev

During the transition from Qt 5 to Qt 6, some modules will be ported to Qt 6 only after the initial Qt 6.0 release. They are likely not to build and run against Qt 6. However, we probably need to make fixes in the Qt 5-based version of those modules. In those cases, develop and push those fixes against the latest Qt 5 branch, that is Qt 5.15. Once porting of the module to Qt 6 officially starts, we will manually merge the Qt 5.15 branch into dev to make sure that no fixes are lost.

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 Gerrit

In principle it is possible to create a personal clone of the project on github.com - all projects from codereview.qt.io 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.