Featured image of post Learn to write better git commit messages by example

Learn to write better git commit messages by example

When people learn programming they – for completely obvious and natural reasons – initially focus on learning the syntax of programming languages and libraries. However, these are just tools. The essence of software engineering is about automating thought, applying algorithmic thinking and anticipation of the known and unknown. The code might be succinct, but the reasoning behind it can be extensive, and it needs to show in the communication around the code. The more senior a programmer is, the more their success depends on their communication skills.

# Communication is important – even in programming

One could even claim that software development teams thrive or fall based on how quick and efficient the feedback cycle about the code is and how well the team shares information while researching and solving problems.

At the core of code-related communication is git commit messages. When a team member shares a new code change for others to review, the speed and accuracy of the reviewers depends heavily on how well the intent of the change was described and motivated.

In addition to reviews, a great git commit also has permanent utility as part of the code base. If it later turns out the commit had a bug, whoever is trying to fix it will have a much easier time reading in the commit what the change was supposed to do, and consequently understanding where it fell short, and will thus be able to rewrite the same change in the correct way. This leads to bugs being fixed much more quickly and with less effort – and most often the person doing the fix is a future you who no longer remembers what the present you were thinking while making that commit, and the future you just have to stare at the commit message and contents until it makes sense.

# Common mistakes

If you haven’t already, first read How to make a good git commit. In addition to knowing what a good end result looks like, it might be useful to learn the typical mistakes developers make and to know explicitly what to not do.

Repeating and extending the recommendations from the Git Pro book (authored by, among others, GitHub co-founder Scott Chacon):

  • Never exceed 70 characters in the git title, and preferably keep it under 50
  • Use imperative format, not past tense: instead of “Fixed” or “Added”, write “Fix” or “Add”
  • Write at least one sentence in the git message body
  • Separate the message body by one empty line from the subject line
  • A title is like an e-mail subject line – no dot at the end
  • The body should use full sentences that end with a dot
  • Wrap the message body around 72 characters, lines should not overly long
  • Don’t use diary-like language to explain what you did, but rather what the change does: if the description starts with “In this commit I..” or “I checked..”, there surely is a simpler way to express it clearly and universally
  • If you have multiple commit messages that have exactly the same title, you are surely also doing something wrong, as the change itself for sure isn’t identical
  • Writing “Update ” is never a good description of the change, as it just states the obvious – instead, describe the intent of what the change tries to achieve
  • Don’t use AI to write your commit messages – AI can only see what changed in the files, it cannot possibly know why you made the change, which is exactly the essence of the git commit message

# Example 1

In this example the improved version has a more descriptive title that captures both what the change was, as well why it was made. The git commit message is restructured to explain the same thing it less repetition.

Initial
Fix security vulnerabilities found by FlawFinder

Fixing security issues found by FlawFinder. Project code base
contains a number of old-style unsafe C function usage. In this commit we are
replacing string functions: `strcpy()` `strcat()` and `sprint()` with the safe
new and/or custom functions such as `snprintf()` `safe_strcpy()` and
`safe_strcat()`

The FlawFinder log before changes:
  $ cat flawfinder-all-vulnerabilities.html | grep "Hits ="
  Hits = 14955

After the change:
  $ cat flawfinder-all-vulnerabilities.html | grep "Hits ="
  Hits = 14668

The number of fixes - 287
Improved
Fix insecure use of strcpy, strcat and sprintf in Connect

Old style C functions `strcpy()`, `strcat()` and `sprintf()` are vulnerable
to security issues due to lacking memory boundary checks. Replace these in
the Connect storage engine with safe new and/or custom functions such as
`snprintf()` `safe_strcpy()` and `safe_strcat()`.

With this change, FlawFinder static security analyzer reports 287 fewer
findings.

# Example 2

In this example the title was changed to used imperative format, and to more precisely tell what was changed in order to distinguish the commit from other similar ones that fix cppcheck failures. The message body explains what the change does instead of what “we” did, and shows the error message verbatim so anybody seraching for the error message will find this text.

Initial
Fixing cppcheck failure

We have an error while running CI in gitlab "There is an unknown macro here
somewhere. Configuration is required. If DBUG_EXECUTE_IF is a macro then please
configure it." Add a workaround - change problematic string with false alarm
before cppcheck run then revert it back.
Improved
Add certain DBUG_EXECUTE_IF cases to cppcheck allowlist

Cppcheck failed on error:

    There is an unknown macro here somewhere. Configuration is required.
    If DBUG_EXECUTE_IF is a macro then please configure it.

This is a false positive and safe to ignore. Extend filtering to exclude it
from cppcheck results.

# Example 3

Here again the title was made more specific about which fix this is about exactly to distinguish it from other similar fixes, and since both the error message was known and the previous commit that caused it was identified, they are included in the git message to clearly justify the change, as well as make debugging similar things much easier in the future.

Initial
Releaser fix

releaser failed due to missing manifest path. Adding return statement to
the function
Improved
Add missing return to find_manifest_file()

The refactor in f9f6d299 split load_manifest() into two functions by simply
copy-pasting the lines. This omitted that the new function needs to have a
`return` added, otherwise it might return `None`.

This fixes the releaser failure about:

    line 214, in get_engine_name_from_manifest_file
      with open(manifest_filename, "r") as manifest:
      TypeError: expected str, bytes or os.PathLike object, not NoneType

# Example 4

This example shows make the title more specific by spelling out what component exactly is extended and with which variables. In the descriptiong use imperative ‘Add’ instead of ‘Adding’, and restructure the text to clearly say what is being done, followed by why it is useful, and include explanation about backwards compatibility to further champion that the change is safe to do. Also fix line breaks and add space between paragraphs.

Initial
Add TLS version to auth plugin available variables

The authentication audit plugins currently do not have access to the TLS
version used. Adding this variable to list of available variables for
audit plugin.
Logging the TLS version can be useful for traceability and to
 help identify suspicious or malformed connections attempting to use
unsupported TLS versions.
This can be used to detect and block malicious connection attempts.
Improved
Extend audit plugin to include tls_version and tls_version_length variables

Add tls_version and tls_version_length variables to the audit plugin so
they can be logged. This is useful to help identify suspicious or malformed
connections attempting to use unsupported TLS versions. A log with this
information will allow to detect and block more malicious connection attempts.

Users with 'server_audit_events' empty will have these two new variables
automatically visible in their logs, but if users don't want them, they can
always configure what fields to include by listing the fields in
'server_audit_events'.

# Example 5

In this example the title can be simplified to summarize the change. The change was initially tested, but later came permanent. There was no change in the contents of the change however, and thus in this case it was better to not describe the lifecycle of the commit in the git commit messge title, but instead keep that information elsewhere among the developers, or perhaps inferred from the fact that the commit was initially on a development branch and only later applied on mainline.

Also avoid writing in “I have ..”, and instead use imperative format that describes what the change is and most importantly extend it to explain why the change was made. Who renamed what file or changed what line of code is always visible in the git commit anyway. The focus of the git commit should be on communicating the intent of the change, as why something was changed isn’t always obvious, yet incredibly important in order to assess if the change is correct or not.

Initial
Switch to `new-archives` branch to test the new archives layout

I have renamed `archives.html` to `.archives.html` to disable overriding.

I have modified archives.md to output JSON.
Improved
Use new layout for archives page

Update theme to version with new archives page and disable the old archives
page. Ensure new archive page is also published as JSON so that
the interactive search can use the JSON file as backend.

# Easiest way to update a commit message: git citool --amend

screenshot of ‘git citool’

Writing a good git commit message while preparing a code submission is much easier if you follow this process:

  1. Start writing code changes
  2. Save intermediate changes with git commit -am WIP
  3. Test, polish, iterate
  4. Run git citool --amend to polish the git commit message when the code change is final and you can see the whole change and thus are able to easily explain what you did and why
  5. Rebase on latest main branch, e.g. git fetch origin main; git rebase -i origin/main
  6. Push to code review

If you find yourself doing frequent rebases and amends, congratulations! It means that you have mastered the craft of preparing great code submissions.

# Good git commit messages help you avoid duplicate effort

One extra benefit of having a great commit message is that you don’t have to rewrite anything when submitting the code for review. Every single code review system I have ever used will automatically use the git commit title and message as the review title and message (at least if the review is a single commit review).

Screencast of git commit message automatically being reused as the merge request description on GitLab

# Why you should always polish the commit message, even if the commit does not feel important

If you are proud of your work and like doing things well, you will follow these guidelines by nature. However, some lazy ass might say that while they agree with the principles, they don’t have time to follow them. To that I always respond that humans tend to get really good at the things we practice. If you do the wrong thing over and over, you become an expert at doing things incorrectly. Is that really what you want?

Doing things correctly from the onset will steer you away from situations where you are knowingly doing lousy quality just to “save time”. Practicing doing something well will ultimately lead you to become a person who does that thing well, effortlessly.

Have you run into situations where you find it challenging to write a good git commit title and message? Share your example in the comments below and I will try to help you formulate the text and capture the essence of the change in a concise title and description.

Hey if you enjoyed reading the post, please share it on social media and subscribe for notifications about new posts!

comments powered by Disqus