Releasing from Hot-fix Branches

Most workspaces release their packages from a single default branch, such as main. Sometimes a fix also has to be released for an older version, for example when main already contains breaking changes or features that are not ready for a release, but the users of version 1.0.0 need a bug fix. The fix is then released from a separate branch, which this guide calls a hot-fix branch.

The changelogs and the package versions of a hot-fix branch differ from the ones on the default branch. The changelog on main might already have an entry for version 2.0.0 that lists the fix, while the changelog on the hot-fix branch ends at 1.0.0. This guide describes how to get a fix onto such a branch and how to release it from there, without ending up with a changelog that lists changes which the branch does not contain.

How Melos finds the commits to release

It helps to know how melos version decides what to release, since it explains the rules in the rest of this guide.

For each package, melos version reads the version from the pubspec.yaml of the package on the current branch and looks for the git tag of that version, for example my_package-v1.0.0. All commits on the current branch that came after that tag, and that changed files in the package, are released. They decide the next version and become the entries of the new changelog section.

This works on every branch, as long as the following holds true for the branch:

  • The version in each pubspec.yaml is the version that was last released from this branch.
  • The git tag of that version exists.
  • The changelog only contains the versions that were released from this branch or from its ancestors.

Picking a fix from main with plain git cherry-pick keeps these rules intact for most commits. It breaks them as soon as a picked commit contains release changes, which are changes to a CHANGELOG.md file or to the version of a package. Such a commit either conflicts with the older changelog, or it brings the changelog entries and the version of main onto the hot-fix branch. The melos cherry-pick command avoids this by leaving the release changes out of the commits that it picks.

Step by step

1. Create the hot-fix branch from the release tag

Create the branch from the tag of the version that needs the fix, not from the current state of the default branch:

bash
git checkout -b hot-fix/1.0.x my_package-v1.0.0
git push --set-upstream origin hot-fix/1.0.x

Pushing the branch with --set-upstream matters. By default melos version fetches the tags of the remote with git pull --tags before it looks for the latest release, which fails on a branch that does not track a remote branch. If you do not want to push the branch yet, turn off fetchTags on the hot-fix branch instead.

In a workspace with several packages that are released independently, the tag of one package is usually enough as the starting point, as long as no other package that needs the fix was released after it. Otherwise create the branch from the newest commit that all affected releases have in common.

2. Allow versioning on the branch

If the workspace restricts versioning to one branch with command/version/branch, melos version refuses to run on the hot-fix branch. Change the option on the hot-fix branch to the name of that branch and commit the change. Since the configuration lives in the repository, this does not affect the default branch:

yaml
melos:
  command:
    version:
      branch: hot-fix/1.0.x

Use a commit type that does not trigger a release for this change, for example chore: allow versioning on the hot-fix branch.

3. Pick the fix

Land the fix on the default branch first, then pick it onto the hot-fix branch. This way the default branch can never miss a fix that an older version has.

bash
melos cherry-pick 4ba1b8f

Several commits and ranges of commits can be picked at once, see the cherry-pick command for all options. The command:

  • Picks each commit with git cherry-pick -x, so that the message of the new commit records which commit it originates from.
  • Keeps the changelogs of the hot-fix branch as they are, even if the picked commit changed them or conflicts with them.
  • Keeps the package versions of the hot-fix branch as they are, as well as the constraints that the packages of the workspace have on each other, while picking all other changes to the pubspec.yaml files.
  • Skips commits that contain nothing but release changes, such as the commits that melos version creates on the default branch.

The message of the commit is kept. This is important, since the Conventional Commits type in the message decides the next version on the hot-fix branch in the same way as it does on the default branch.

If the fix cannot be picked because the code has diverged too much, write the fix directly on the hot-fix branch with a Conventional Commits message instead. melos version treats it like any other commit.

4. Version the packages

Run melos version on the hot-fix branch, exactly as you would on the default branch:

bash
melos version

Melos finds the picked commits after the latest release tag of the branch, bumps the versions, writes the changelog entries, and creates the release commit and the tags on the hot-fix branch. The changelog entries link to the commits on the hot-fix branch, which are the commits that the released version contains.

Check the versions that Melos proposes before confirming them. The picked commits decide the next version, so a picked feat commit results in a minor version such as 1.1.0. If that version was already released from the default branch, the release from the hot-fix branch would collide with it. Melos does not detect this. It does not replace a tag that already exists, and a version that was already published cannot be published again. Only pick fixes onto a hot-fix branch when possible, or choose the version yourself:

bash
melos version --manual-version=my_package:1.0.1

5. Publish and push

Publish the packages from the hot-fix branch and push the branch together with its tags:

bash
melos publish --no-dry-run
git push --follow-tags

6. Keep the default branch up to date

The fix itself is already on the default branch, since it was picked from there. It is listed in the changelog of the next release from the default branch as well, which is correct, since that release also contains the fix.

What the default branch does not have is the changelog section of the hot-fix release, for example ## 1.0.1. If you want the changelog on the default branch to show all released versions, copy that section into the CHANGELOG.md on the default branch by hand, below the sections of the newer versions, and commit it with a type that neither triggers a release nor shows up in the next changelog, for example chore.

Do not merge the hot-fix branch into the default branch for this, and do not pick its release commit. Both bring the versions and the changelog of the hot-fix branch onto the default branch, where they conflict with the newer ones.

Summary

On a hot-fix branchWhy
Create the branch from a release tagThe version, the tag and the changelog of the branch then line up.
Push the branch with --set-upstreammelos version fetches tags with git pull --tags by default.
Adjust command/version/branch if it is setOtherwise melos version refuses to run on the branch.
Pick fixes with melos cherry-pickChangelogs and versions of the branch stay intact.
Never merge the default branch or pick its release commitsThey carry the versions and the changelog of the default branch.
Check the proposed versions, prefer fixes onlyA version that was already released elsewhere would collide.
Copy the changelog section to the default branch by handMerging the branch back conflicts with the newer versions and changelog.