Featured image of post How to make a good git commit

How to make a good git commit

As a software developer, your core skill is how to improve an existing code base to make the software better iteratively, patch by patch.

To be a good software developer you need to:

  • understand the concept of a code patch,
  • know how do code improvements in well sized and properly documented patches, and
  • skillfully use git version control software to manage patches.

What is a patch?

A patch defines the changes to be made to the code base. It is basically a list of code lines to be added, removed or modified in a code base. Each patch always also has an author, a timestamp when it was written, a title that describes it and a longer text body that explains why this particular patch is good and applying it on the code base is beneficial.

Example:

patch
Author: Otto Kekäläinen
Date:   June 22nd, 2022 08:08:08

Make output friendlier for users

Add line break so text is readable and add a 2 second delay between
messages so it does not scroll too fast.

--- a/demo.c
+++ b/demo.c
@@ -8,7 +8,8 @@ int main()
 {
    for(;;)
    {
-     printf("Hello world!");
+     printf("Hello world!\n");
+     sleep(2);
    }
    return 0;
 }

How to make a patch

You can make a patch by simply copying a file, changing something in it, and then comparing the copy to the original file using the command diff and saving the output.

$ cp demo.c demo.c.orig
$ nano demo.c
$ diff -u demo.c.orig demo.c > demo.patch
$ cat demo.patch
patch
--- demo.c.orig
+++ demo.c
@@ -8,7 +8,8 @@ int main()
 {
    for(;;)
    {
-     printf("Hello world!");
+     printf("Hello world!\n");
+     sleep(2);
    }
    return 0;
 }

The patch can be sent by email or uploaded somewhere. After that, anybody can download the patch, read it, and apply it to their copy of the code base using the command patch.

shell
$ grep Hello demo.c
     printf("Hello world!");

$ curl -O https://…/demo.patch

$ patch -p0 < demo.patch
patching file demo.c

$ grep Hello demo.c
     printf("Hello world!\n");

As this is not very fast nor convenient, software developers like to use git, a version control software that automates all of this. In git, we tend to talk about git commits, which basically just means a patch that has been applied on a code base.

Examples of a good git commit messages

A good git commit message typically has these characteristics (adapted from the Git Pro book):

Capitalized, short summary of what the change is

More detailed explanatory text that focuses on the 'why' to motivate
the change. Use present tense and imperative format (write "Fix bug",
not "Fixed bug"). Wrap it to about 72 characters or so. The blank line
separating the summary from the body is critical.

Further paragraphs come after blank lines.

- Bullet points are okay, too

- Typically a hyphen or asterisk is used for the bullet, followed by a
  single space, with blank lines in between, but conventions vary here

- Use a hanging indent

Here are a couple of real-world examples in pure text form:

From MariaDB@ff1d8fa7:

Deb: Clean away Buster to Bookworm upgrade tests in Salsa-CI

Upgrades from Debian 10 "Buster" directly to Debian 12 "Bookworm",
skipping Debian 11 "Bullseye", fail with apt erroring on:

    libcrypt.so.1: cannot open shared object file

This is an intentional OpenSSL transition as described in
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=993755

From MariaDB@2c529441:

Deb: Run wrap-and-sort -av

Sort and organize the Debian packaging files.

Also revert 4d03269 that was done in vain.

Five requirements for a good git commit

In order of importance:

1. Commits should be atomic

The first and most important thing about a good patch or a commit is that it should be a self-standing change. If a commit fixes a bug, it should not at the same time add a new feature or fix some other completely unrelated bug, otherwise it is not atomic. If you add a new feature, the same commit should ideally also add automatic tests for the feature to ensure it won’t regress, and the same commit should update the documentation to mention the feature, as it is all related and should either go or not go into the code base along with the feature itself.

If your changes are not properly scoped and self-standing, you might end up in a situation later on where somebody decides to revert or reject the commit that introduced a new feature, but miss removing the tests or documentation about it, which would not have happened if they were added in separate commits.

There is no clear rule on what is the optimal scope for a commit; it is something you will learn by experience. Sometimes it makes sense to have several separate changes in one single commit simply because of each one of them being so small. In other cases, one single logical change might span multiple commits, because it was perhaps clearer to move or rename files in one commit and then update their commits in another. This is something you will just have to learn over time as you become a more experienced software developer.

2. The title should be descriptive, yet terse and not too long

A title starts with a capital letter and has no trailing dot. Just like the subject line in an email. The title should make sense when read in a list of commits. If the title is too long, it will be cut off. A limit of 72 characters is safe to all typical places where people will be reading it, such as in a terminal window or when browsing GitHub or GitLab, but striving for under 50 characters is even better.

3. The commit message should explain why it was made

The text should be verbose enough for anybody reviewing the commit to understand why it was made, and to be convinced that the change is good. Every commit must have a text body, even if it is very short. This forces the author to spend a few seconds thinking about the change before committing.

Note that the commit message is about the change itself, so it should answer the question ‘why’. If you want to explain how a certain line of code works, simply use an inline comment next to the code itself. That way, the documentation is in the correct context. The git commit description should have just a tiny bit of ‘what’ and ‘how’, and mostly focus on the ‘why’.

The commit body should be wrapped at about 72 characters. Proper use of empty lines and lists that are indented with a dash or star makes the body more readable.

Remember to use imperative format. Don’t write Fixed bug or Added feature. Instead write Fix bug or Add feature. The patch hasn’t added or fixed anything at the time you wrote it. Think about it like an order you give to the code base to start following. Also, try to keep your text in the current tense and passive format. Don’t write This commit makes X but simply Make X. Don’t write I changed Y but just simply Change Y.

4. Use references when available

If your code change is related to a previous commit, mention the commit ID. In most software commits, IDs will automatically become links. If the code change is related to something that was discussed or tracked elsewhere, please include the bug tracker ID or a URL to the discussion. However, the reference alone does not remove the need to write a git commit message. You cannot expect that somebody reading your commit has time or even access to open and read all references - use them only as pointers for more information.

Viewing one of the earlier git commit message examples in gitk, GitLab and GitHub illustrates how a git commit automatically becomes a link:

Same commit in gitk, GitLab and GitHub

The author name and timestamp are automatic if you configure your git correctly, so this should be a non-issue. If you neglect to configure git with your real name and email, you will be muddling the waters for anybody who later wants to verify something about authorship. In the worst case scenario, all your commits might be purged from the git repository due to unclear copyright.

Also keep in mind that if you commit code on behalf of somebody else, you must tell git that the author for a particular commit was somebody else and you only committed it. Read up on git commit –author for details.

The right tools makes git commits easy

Using a good tool to craft your git commits goes a long way in making the commit flawless.

My personal choice is git-citool, which is distributed together with git itself, so anybody can use it on any operating system. It does not use the native graphics of each operating system, but a cross-platform graphics library, which may look a bit ugly. It is, however, very easy and convenient to use, so I love it.

To make a new commit, simply run git citool. It starts off empty and then you can select which files you want to stage, and write the git commit message in this box, and press commit. Super easy, and it is very clear what changes you are committing.

git citool example

Don’t settle for bad commits - amend them

If you are not happy with your commit and want to edit it, or to use git terminology you want to “amend”, this is possible only for the topmost git commit that does not have any child commits yet. Run git-citool --amend.

Here you can see a git commit that is really bad, so it really needs to be fixed. However, with git-citool it is easy and fast.

WIP commits: how to avoid postponing writing the perfect git commit message

Remember that you don’t have to make a perfect git commit right off the bat. Do it only once you know what you actually want to write in it. While still working on the code and saving intermediate versions of it, I recommend using WIP commits where the title is simply WIP, or if you already have some commit text draft, prefix the title with WIP:.

Use git rebase -i frequently

When you are done with WIP commits, you can run git rebase -i to squash them together and write the final git commit message.

For a visual explanation see the presentation A Branch in Time (a story about revision histories) by Tekin Süleyman on how to use interactive rebase in git and why rebasing and amending commits will end up making the code quality better in the long run.

A polished git commit is always worth the effort

Someone who is lazy might say that while they agree with the principles, they don’t have time to follow them. To that I respond that doing things correctly from the onset actually saves time down the road.

  • If your git commits are good, the job of the reviewer will be much easier. They won’t waste time on just trying to understand your change, but they will get it directly and will be able to focus their energy on actually reviewing and spotting flaws in your code. If you avoid shipping a bug, you save a lot of work not having to debug, write a fix and ship a new release.

  • A great git commit is also useful even if it later turns out the commit had a bug, because whoever fixes that bug will have a much easier time reading in the commit what the change was supposed to do, and understanding where it fell short, and then making 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 was thinking while making that commit, with the future you just having to stare at the commit until it makes sense.

  • You don’t have to rewrite anything when it comes time to submit the commit for review. Every single code review system I have ever used will automatically use the commit title and message as the review title and message if the review is a single commit review.

Now go and build great software – patch by patch!

Now you know how to make a good git commit message. If you are proud of your work and like doing things well, you will follow these guidelines. To further learn how to polish your git commit messages, see also the post on git commit messages by example.

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

comments powered by Disqus