Image of Lucian Ghinda writing for notes.ghinda.com
October 2nd, 2024

How do you know if a git commit message is good?

How do you know if a git commit message is good?

If you can browse your git history and understand why those changes were made and why/how that technical decision happened without needing to refer to tickets, issues, or requirements, then you likely have an excellent git history.

If your git history looks like: 

commit 04796b39d364b6c580bbe17ecf3f0160b5c8877d
Author: John Snow
Date:   Sun Jul 7 12:03:51 2024 +0300

    Fix PRJ-5623

commit 828394c2864ea98003491e17b2562ad24bada653
Author: John Snow
Date:   Sun Jul 7 12:03:51 2024 +0300

    Implement PRJ-6523

commit f64299c822a7cd38557eb935f074f18eed5719a2
Author: Jane Dane
Date:   Mon Aug 15 17:41:23 2022 +0300

    Refactor User authentication

commit abbd91854acce973ec662b0f2b75e89a4ceb3261
Author: Jane Dane
Date:   Mon Aug 15 15:30:08 2022 +0300

    Move parse_with_options to Team

Then, you might want to reconsider how you write your commits. 

An example of a goodish git commit message

Here is an example of a git commit message that is good enough: 

Commit 234a85f59da9c81edfa981dd56a1d97795a7d1c9
Author: Lucian Ghinda
Date:   Sun Aug 25 07:56:07 2024 +0300

    Add new tag formats to improve the speed of tagging

    This commit introduces new tag formats in the TagTypeMap and updates
    ContentTypeIdentifier tests for compatibility.

    These changes ensure that the ContentTypeIdentifier can correctly
    identify content types using the new tag formats, maintaining
    the system's flexibility and reliability.

    Why these changes?

    (1) We want to allow users to tag content using shorter tags
        eg: `stype` instead of `section:type`

    (2) Automatically fix typos in tag formats:
        eg: typing `a` instead of `s` in `s:type` => `a:type`

    (3) Duplicate tag letter replaces `:`
        eg: `aatype` is the same as `a:type`
            `sstype` is the same as `s:type`

    Changes:

    1. Updated TagTypeMap to include new formats:

    - 's:type' (e.g., 'a:code', 'a:article')
    - 'stype' (e.g., 'scode', 'sarticle')
    - 'sstype' (e.g., 'sscode', 'sstarticle')
    - 'a:type' (e.g., 'a:code', 'a:article')
    - 'aatype' (e.g., 'aacode', 'aaarticle')
    - 'Aatype' (e.g., 'Aacode', 'Aaarticle')

    2. Updated ContentTypeIdentifier tests to cover new tag formats
    for all existing content types:

    - Code, Articles, Videos, Library, Newsletter, Podcasts
    - Related, Community, Events, Books, Launch, Slides

A better example

Here is an example of a git commit message shared by Zlatko Alomerovic via Linkedin. The message is written by Jean Boussier (@byroot) in a commit for Rails  and it looks like this (here is just the first part of it)

Screenshot of a commit message
Screenshot of the first part of the message

The full message looks like this: 

commit 8c7e69b79b63a88a170a9b9004a906db00161a3b
Author: Jean Boussier 
Date:   Mon Jan 8 18:43:37 2024 +0100

    Optimize Hash\#stringify_keys

    Using Symbol\#name allows to hit two birds with one stone.

    First it will return a pre-existing string, so will save
    one allocation per key.

    Second, that string will be already interned, so it will
    save the internal `Hash` implementation the work of looking
    up the interned strings table to deduplicate the key.

    ```
    ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-darwin21]
    Warming up --------------------------------------
                    to_s    17.768k i/100ms
                    cond    23.703k i/100ms
    Calculating -------------------------------------
                    to_s    169.830k (±10.4%) i/s -    852.864k in   5.088377s
                    cond    236.803k (± 7.9%) i/s -      1.185M in   5.040945s

                    cond    236.803k (± 7.9%) i/s -      1.185M in   5.040945s

    Comparison:
                    to_s:   169830.3 i/s
                    cond:   236803.4 i/s - 1.39x  faster
    ```

    ```ruby
    require 'bundler/inline'

    gemfile do
      source 'https://rubygems.org'
      gem 'benchmark-ips', require: false
    end

    HASH = {
      first_name: nil,
      last_name: nil,
      country: nil,
      profession: nil,
      language: nil,
      hobby: nil,
      pet: nil,
      longer_name: nil,
      occupation: nil,
      mailing_address: nil,
    }.freeze

    require 'benchmark/ips'

    Benchmark.ips do |x|
      x.report("to_s") { HASH.transform_keys(&:to_s) }
      x.report("cond") { HASH.transform_keys { |k| Symbol === k ? k.name : k.to_s } }
      x.compare!(order: :baseline)
    end
    ```