Working with Conventional Commits
Something I learned about not long ago are Conventional Commits. Introduced by a friend and a colleague (thanks Moritz ๐) - it changed my way to author my commits. The reason why I've wanted to change something was to maintain clarity and consistency for the work I push to our projects. And Conventional Commits are one of the most effective ways to achieve this is by adopting standardized commit messages.
What are Conventional Commits?
Conventional Commits is a specification for writing standardized commit messages. By adhering to a set format, commit messages become more informative and easier to understand. This standardized approach not only helps in maintaining a clean and meaningful commit history but also facilitates automated processes such as versioning and changelog generation. This convention dovetails withย SemVer, by describing the features, fixes, and breaking changes made in commit messages.
A typical Conventional Commit message looks like this:
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
- type: A short keyword that describes the nature of the change, such as
feat
for new features orfix
for bug fixes. - scope: A sub-part of the project being affected, often a specific module or component.
- description: A concise explanation of the change.
- body: Optional, provides additional details about the change.
- footer: Optional, can include information such as breaking changes or issue references.
Common Commit Types
Even though the documentation is pretty good, hereโs a quick overview of the most commonly used types in Conventional Commits:
- feat: A new feature for the user.
- fix: A bug fix.
- docs: Documentation-only changes.
- style: Changes that do not affect the meaning of the code (e.g., formatting).
- refactor: A code change that neither fixes a bug nor adds a feature.
- test: Adding missing tests or correcting existing ones.
- chore: Other changes that don't modify
src
ortest
files. - ci: Changes to our CI configuration files and scripts.
By sticking to these types, you ensure that every team member can immediately understand the purpose of a commit without having to dive into the code change. And on top of that it is pretty easy to generate changelogs.
Why Use Conventional Commits
- Automatically generating CHANGELOGs.
- Automatically determining a semantic version bump (based on the types of commits landed).
- Communicating the nature of changes to teammates, the public, and other stakeholders.
- Triggering build and publish processes.
- Making it easier for people to contribute to your projects, by allowing them to explore a more structured commit history.
Enforce Conventional Commits in GitLab
Using GitLab you have the option to add "pre-defined push rules" for your group or certain repositories. GitLab uses the RE2 RegEx syntax to valid against your given commit messages. Currently we are using the following RegEx for our use case:
^(((Initial commit)|((Merge|Revert) [^\r\n]+)|((build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\(\w+\))?!?: [^\r\n]+((\r|\n|\r\n)((\r|\n|\r\n)[^\r\n]+)+)*))(\r|\n|\r\n)?)\z
Note: For already existing repositories you need to define the push rules in the repo itself. For new repositories, the rule is inherited from the group.
Final thoughts
Even if it means getting used to it for myself and other colleagues, you get the hang of it after 1-2 days and internalize the new way of working. In any case, it is particularly helpful to force this rule somewhere so that people stick to it. Another way would be to use git hooks and the related checking of commit messages. For me, it has definitely helped me to think more about a meaningful commit message.
Sources
Conventional Commits - https://www.conventionalcommits.org/en/v1.0.0/
GitLab Push Rules - https://docs.gitlab.com/ee/user/project/repository/push_rules.html