Skip to content

Instantly share code, notes, and snippets.

@rprospero
Last active March 20, 2024 16:00
Show Gist options
  • Save rprospero/9cdc1d83cae757725ffe9f81eaa9cecf to your computer and use it in GitHub Desktop.
Save rprospero/9cdc1d83cae757725ffe9f81eaa9cecf to your computer and use it in GitHub Desktop.

Problem

Most Changelog tools are written from the perspective of web service developers. There is only one supported version at a time and the log exists to allow users to see the changes that are happening.

This is an issue as a developer of desktop software, since we would like to be able to release patches for recent versions without merging in other new features. This means that the release history will not exactly match the git history of the develop branch, since multiple releases occurred on a specific version branch.

The direct result of this is that the Changelog will be inaccurate for patch releases. Version 1.5.8 will correctly list the changes on version 1.5.0 through 1.5.7, but version 1.6.0 will not list any of the patch releases as existing and take credit for all of the changes in the minor releases.

Below, we examine five solutions to the problem.

Solution: Dedicated Release Branch

Here, we create a single, dedicated release branch. Individual releases are denoted by tags on the branch. For patch releases, individual commits from develop would be cherry picked onto the branch and a new tag added. For minor or major releases, the develop branch would be merged into the release branch. The maintainer might then perform a rebase to remove the duplicate PRs.

Advantages

Only requires two branches, regardless of how many version are released

Always gives the correct release information for old releases

No rebasing of pushed branches

Disadvantages

Requires two branches, while other techniques require one

Accurate changelog requires rebase after merge

By default, changes from the patch releases will be mentioned again in the minor releases. This can be an issue depending on how declarative versus imperative the notes are. Two different mentions of "Set the font size to 22pt" would cause minimal confusion, but the change log twice mentioning "Double the font size" might cause users to expect a 44pt font.

The unfortunate part is that this can only be solved by rebasing after the merge and deleting the duplicate commits. Since rebasing after falls under the category of worst practices, this is a desided disadvantage

Solution: Single Branch Structure

In this format, all releases are based off tags in the develop branch. With only a single, linear git log, the tools work as expected. This just leaves the issue of patch releases. To perform a patch release, the maintainer must rebase the develop branch to move the desired commits to just after the previous release. Then a new tag is created for the new release and the updated develop would be force pushed to the server. All developers would then need to rebase on the latest develop

Advantages

Only a single branch to maintain

Changelog is always correct

No rebase after merge

Assuming that no merges ever occur in the develop branch (i.e. every pull request is a squash commit)

Disadvantages

Significant maintainer effort for patch releases

Rebasing the entire history for each patch release would be a major time sink for maintainers

Rebasing of pushed branches

All developers would need to rebase all current work in progress every time that there was a patch release, which could result in losing some of that work

Solution: Ignore the problem

Multiple blog posts suggested this as "best practice". Minor releases still get their own branch which have patch release cherry picked onto them. However, the next minor release is based off of develop and the previous patch releases are ignored. While the history in the Changelog is slightly inaccurate (since it does not mention old patch releases), the exact history is still available on those release branches. A header can be added to the Changelog to inform the user that the patch information must be pulled from the appropriate version branch.

Advantages

No rebasing

Minimal maintainer effort

Disadvantages

Changelogs ignore old patch releases

Solution: Do not automate changelogs

Under this approach, git-cliff and similar tools are abandoned entirely. Instead, a GitHub action forces every PR to include an addition to the Changelog. The maintainer then manually edits this file on each release.

Advantages

No rebasing

Accurate Changelogs

Disadvantages

Moderate maintainer effort

Editing the change log for each release is a bit of a hassle, but less effort than a rebase.

Mild developer effort

Developers need to perform the extra effort to update the Changelog for each PR. On its own, this should not be significantly more effort than ensuring conventional commits in the titles but

  1. It is slightly easier for a maintainer to step in and edit a PR title than to edit a Changelog file in a PR
  2. There will be annoying merge conflicts from every PR editing the Changelog that developers will need to put effort into fixing.

Solution: Write a better changelog tool

Every major changelog tool seems to use the same algorithm:

  1. Find the most recent commit (called X) before the current that has a version tag
  2. Add all commits between the current commit and X to the change set under the current version
  3. Set the current version the tag for X
  4. Set the current commit to X
  5. If at the beginning of the history, quit
  6. Goto step #1

It is this algorithm that forces everything to be on the same branch. However, an alternate algorithm exists.

  1. Filter all the tags on the repository to find the list of version tags
  2. Sort the version tags from old to new
  3. Pick the oldest tag X and the second old tag Y
  4. Use git marge-base to find the closest common ancestor to X and Y
  5. Add all commits between Y and the closest ancestor to the change set
  6. Remove all commits between X and the closest ancestor from the change set
  7. Add the Changeset under the tag for Y
  8. Remove X from the list of tags
  9. Goto step #3

This procedure allows commits to exist across arbitrary branches while still producing the automated changelog.

Advantages

No rebasing

No requirements on branch structure

Accurate changelogs

Minimal maintainer effort

Disadvantages

Massive time sink to write this utility

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment