Changelogs are common when developing software to inform stakeholders about the changes and improvements a project is experiencing. In some domains, such a documentation is even mandatory. See for example the Zola Changelog Page on GitHub.
One option is, of course, to write this changelog by hand. But, if a version control system like Git is used, changes are already documented there. So why duplicate the work? This is where automatic changelog generators like git-cliff come in. The tool is capable of reading the git history, grouping the commits in a configurable way, and creating a changelog automatically. This has two main advantages:
- Manual work for writing the changelog is eliminated
- Knowing commit messages will be part of the changelog, commit discipline among developers might be improved
Example
git-cliff requires a configuration file named cliff.toml
either in the
project directory or in a global configuration folder (see docs for details) to
tell the tool how a document shall be created based on a commit history. The
most straightforward way is to just print every commit message one after
another. But git-cliff is capable to interpret commit messages to some extend,
allowing for a more sophisticated changelog.
An example config might look like this (see git-cliff docs for details):
[changelog]
# changelog header
header = """
# Changelog\n
All notable changes to this project will be documented in this file.\n
"""
# template for the changelog body
# https://tera.netlify.app/docs/#introduction
body = """
{% if version %}\
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
## [unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | upper_first }}
{% for commit in commits %}
- {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message | upper_first }}\
{% endfor %}
{% endfor %}\n
"""
# remove the leading and trailing whitespace from the template
trim = true
# changelog footer
footer = """
<!-- generated by git-cliff -->
"""
[git]
# parse the commits based on https://www.conventionalcommits.org
conventional_commits = true
# filter out the commits that are not conventional
filter_unconventional = false
# regex for parsing and grouping commits
commit_parsers = [
{ message = "^feat", group = "Features"},
{ message = "^Merge", skip = true},
]
# filter out the commits that are not matched by commit parsers
filter_commits = false
# glob pattern for matching git tags
tag_pattern = "*"
To break it down, the config allows for a header, a footer, as well as a templated part in between (using the Tera templating engine) that determines how each commit is represented. Here, tags are interpreted as releases and commits are broken down into each tag, allowing to associate changes with certain versions.
If the conventional_commits
flag is set to false, git-cliff will not
interpret the commit messages in any way, but use the regexes defined in
commit_parsers
to group commits. If the flag is true however, commits are
interpreted according to the conventional commit specification.
This means that Strings at the beginning of a message before a colon will be
used to group commits (if not overwritten by commit_parsers
). Eventually,
this allows to list commits of one group (e.g. bugfixes) together in the
changelog.
With filter_unconventional
it is possible to skip all commits that don't
follow the conventional commit specification. Also, the same is true for
filter_commits
if they don't match to any of the regexes in the
commit_parsers
. Furthermore, specific commits matching a regex can be
skipped in commit_parsers
, as done above with all commits starting with the
word "Merge".
If developers have a certain discipline in writing commits, like
feat: Add green button
Add green button on start page, allowing users to launch the app
then it is super easy to auto-generate a changelog out of a project using git-cliff.
Example (from git-cliff readme)
A commit history like this
* df6aef4 (HEAD -> master) feat(cache): use cache while fetching pages
* a9d4050 feat(config): support multiple file formats
* 06412ac (tag: v1.0.1) chore(release): add release script
* e4fd3cf refactor(parser): expose string functions
* ad27b43 (tag: v1.0.0) docs(example)!: add tested usage example
* 9add0d4 fix(args): rename help argument due to conflict
* a140cef feat(parser): add ability to parse arrays
* 81fbc63 docs(project): add README.md
* a78bc36 Initial commit
Can generate a changelog that looks like this
Changelog
All notable changes to this project will be documented in this file.
[unreleased]
Features
- Support multiple file formats
- Use cache while fetching pages
[1.0.1] - 2021-07-18
Miscellaneous Tasks
- Add release script
Refactor
- Expose string functions
[1.0.0] - 2021-07-18
Bug Fixes
- Rename help argument due to conflict
Documentation
- Add README.md
- Add tested usage example
Features
- Add ability to parse arrays