When several people are working together on a project, it is important that communication and documentation are well established. Git commit messages are a reflection of these two aspects. Conventions to write commit messages are crucial across a team to help the understanding of changes made in the project.
It's in this spirit that some large-scale open-source projects have established best practices that have become widespread. I'm thinking here of the Angular project, which has established a convention for its commit messages. Later, this convention gave rise to a standard called Conventional Commits. It is one (good) way to organize commits if you don’t want to think too much about this concern.
To enforce Conventional Commits, there is a nice Python tool called commitizen, and it will be our focus for the rest of this article.
Also, you will need to have some basic knowledge of Git manipulation to follow this article. If this is not the case, you can read this article.
Installation
To install commitizen, you can use pip or your favorite package manager. Note that commitizen starts working with Python 3.7 or higher and Git 1.8.5.2 or higher.
$ pip install commitizen
# or with poetry
$ poetry add commitizen -G dev
If you don’t know Poetry, I have a handy tutorial on it.
Usage
Create an empty folder (or Python project) to test the following commands at the same time as I do. Now initialize a Git project with git init
and git commit
(if you want to commit a file like .gitignore for example).
When you first install commitizen, you must configure it with the cz init
command.
There are a few questions to answer, here is what it looks like in my terminal if you want to choose the same defaults as me.
? Please choose a supported config file: pyproject.toml
? Please choose a cz (commit rule): (default: cz_conventional_commits) cz_conventional_commits
? Choose the source of the version: commitizen: Fetch and set version in commitizen config (default)
No Existing Tag. Set tag to v0.0.1
? Choose version scheme: semver
? Please enter the correct version format: (default: "$version")
? Create changelog automatically on bump Yes
? Keep major version zero (0.x) during breaking changes Yes
? What types of pre-commit hook you want to install? (Leave blank if you don't want to install) done
Notes:
It is recommended to use the pyproject.toml to configure package options wherever possible.
For the third question: “Choose the source of the version”. I chose the default option since I’m not using poetry to manage dependencies, but if it is your case, change it to poetry.
Now let’s add a dummy arithmetic module for our test with this content:
def add(a: int, b: int) -> int:
return a + b
To commit our changes, you will use commitizen cz commit
command. It will help us write a meaningful commit message. You can see the exchange I had with this command below:
? Select the type of change you are committing feat: A new feature. Correlates with MINOR in SemVer
? What is the scope of this change? (class or file name): (press [enter] to skip)
arithmetics
? Write a short and imperative summary of the code changes: (lower case and no period)
add a new function
? Provide additional contextual information about the code changes: (press [enter] to skip)
the function adds two integers
? Is this a BREAKING CHANGE? Correlates with MAJOR in SemVer No
? Footer. Information about Breaking Changes and reference issues that this commit closes: (press [enter] to skip)
feat(arithmetics): add a new function
the function adds two integers
...
Commit successful
Notes:
You select the type of commit involved. In this case, since I’m adding a new module and new function, it is
feat
. Look at the Conventional Commit standard for a full list of commit types.You can also specify if it is a breaking change or not.
In the end, you have the full commit message written by commitizen.
You can also check the Git history with the git log
command to be sure the message is exactly what you want.
Also if you look at your configuration file (pyproject.toml if you follow my previous instructions), you must notice that your project version didn’t change. Now let’s say we want to tag the project at this state and also generate a changelog. All we have to do is call the cz bump
command. This is the output I have when running this command.
Tag 0.0.1 could not be found.
Possible causes:
- version in configuration is not the current version
- tag_format is missing, check them using 'git tag --list'
? Is this the first tag created? Yes
bump: version 0.0.1 → 0.1.0
tag to create: 0.1.0
increment detected: MINOR
[master 476a0eb] bump: version 0.0.1 → 0.1.0
3 files changed, 8 insertions(+), 1 deletion(-)
create mode 100644 CHANGELOG.md
...
Done!
You should notice that a CHANGELOG.md file is created with the content of the commit we created previously. This changelog file respects the convention Keep a Changelog. Feel free to edit it, if you want to add additional information.
Also, now if you run git tag
, you will see the tag you just created, and the configuration file is also synced with the new version, houra!
Now you can repeat this workflow using cz commit
and cz bump
.
You can customize commitizen by, for example, creating new commit rules or new bump rules.
I hope this article has convinced you, or at least drawn your attention to the relevance of a tool like commitizen for improving collaboration in your team.
This is all for this article, hope you enjoy reading it. Take care of yourself and see you soon. 😁