Skip to content

Instantly share code, notes, and snippets.

@qoomon
Last active April 27, 2024 03:58
Show Gist options
  • Save qoomon/5dfcdf8eec66a051ecd85625518cfd13 to your computer and use it in GitHub Desktop.
Save qoomon/5dfcdf8eec66a051ecd85625518cfd13 to your computer and use it in GitHub Desktop.
Conventional Commit Messages

Conventional Commit Messages

See how a minor change to your commit message style can make a difference.

Tip

Have a look at git-conventional-commits , a CLI util to ensure these conventions and generate verion and changelogs

Commit Message Formats

Default

<type>(<optional scope>): <description>
empty separator line
<optional body>
empty separator line
<optional footer>

Merge Commit

Merge branch '<branch name>'

Follows default git merge message

Revert Commit

Revert "<reverted commit subject line>"

Follows default git revert message

Inital Commit

init

Types

  • API relevant changes
    • feat Commits, that adds or remove a new feature
    • fix Commits, that fixes a bug
  • refactor Commits, that rewrite/restructure your code, however does not change any API behaviour
    • perf Commits are special refactor commits, that improve performance
  • style Commits, that do not affect the meaning (white-space, formatting, missing semi-colons, etc)
  • test Commits, that add missing tests or correcting existing tests
  • docs Commits, that affect documentation only
  • build Commits, that affect build components like build tool, ci pipeline, dependencies, project version, ...
  • ops Commits, that affect operational components like infrastructure, deployment, backup, recovery, ...
  • chore Miscellaneous commits e.g. modifying .gitignore

Scopes

The scope provides additional contextual information.

  • Is an optional part of the format
  • Allowed Scopes depends on the specific project
  • Don't use issue identifiers as scopes

Breaking Changes Indicator

Breaking changes should be indicated by an ! before the : in the subject line e.g. feat(api)!: remove status endpoint

  • Is an optional part of the format

Description

The description contains a concise description of the change.

  • Is a mandatory part of the format
  • Use the imperative, present tense: "change" not "changed" nor "changes"
    • Think of This commit will... or This commit should...
  • Don't capitalize the first letter
  • No dot (.) at the end

Body

The body should include the motivation for the change and contrast this with previous behavior.

  • Is an optional part of the format
  • Use the imperative, present tense: "change" not "changed" nor "changes"
  • This is the place to mention issue identifiers and their relations

Footer

The footer should contain any information about Breaking Changes and is also the place to reference Issues that this commit refers to.

  • Is an optional part of the format
  • optionally reference an issue by its id.
  • Breaking Changes should start with the word BREAKING CHANGES: followed by space or two newlines. The rest of the commit message is then used for this.

Examples

  • feat: add email notifications on new direct messages
    
  • feat(shopping cart): add the amazing button
    
  • feat!: remove ticket list endpoint
    
    refers to JIRA-1337
    
    BREAKING CHANGES: ticket enpoints no longer supports list all entites.
    
  • fix(api): handle empty message in request body
    
  • fix(api): fix wrong calculation of request body checksum
    
  • fix: add missing parameter to service call
    
    The error occurred because of <reasons>.
    
  • perf: decrease memory footprint for determine uniqe visitors by using HyperLogLog
    
  • build: update dependencies
    
  • build(release): `bump version to 1.0.0
    
  • refactor: implement fibonacci number calculation as recursion
    
  • style: remove empty line
    

Git Hook Scripts to ensure commit message header format

commit-msg Hook (local)

  • ensure node and npx command is installed on your local machine
  • create following file in your local repository folder.git-hooks/commit-msg
    #!/usr/bin/env sh
    
    commit_message="$1"
    # exit with a non zero exit code incase of an invalid commit message
    
    # use git-conventional-commits, see https://github.com/qoomon/git-conventional-commits
    npx git-conventional-commits commit-msg-hook "$commit_message"
    
    # or verify $commit_message with your own tooling
    # ...
    
  • ⚠ make .git-hooks/commit-msg executable (unix: chmod +x '.git-hooks/commit-msg')
  • set git hook directory to .githooks git config core.hooksPath '.git-hooks'
  • commit .git-hooks directory if you want to share them with your team, they only need to call the git config command once after cloning the repository

pre-receive Hook (server side)

  • create following file in your repository folder .git/hooks/pre-receive
    #!/usr/bin/env bash
    
    # Pre-receive hook that will block commits with messges that do not follow regex rule
    
    commit_msg_type_regex='feat|fix|refactor|style|test|docs|build'
    commit_msg_scope_regex='.{1,20}'
    commit_msg_description_regex='.{1,100}'
    commit_msg_regex="^(${commit_msg_type_regex})(\(${commit_msg_scope_regex}\))?: (${commit_msg_description_regex})\$"
    merge_msg_regex="^Merge branch '.+'\$"
    
    zero_commit="0000000000000000000000000000000000000000"
    
    # Do not traverse over commits that are already in the repository
    excludeExisting="--not --all"
    
    error=""
    while read oldrev newrev refname; do
      # branch or tag get deleted
      if [ "$newrev" = "$zero_commit" ]; then
        continue
      fi
    
      # Check for new branch or tag
      if [ "$oldrev" = "$zero_commit" ]; then
        rev_span=`git rev-list $newrev $excludeExisting`
      else
        rev_span=`git rev-list $oldrev..$newrev $excludeExisting`
      fi
    
      for commit in $rev_span; do
        commit_msg_header=$(git show -s --format=%s $commit)
        if ! [[ "$commit_msg_header" =~ (${commit_msg_regex})|(${merge_msg_regex}) ]]; then
          echo "$commit" >&2
          echo "ERROR: Invalid commit message format" >&2
          echo "$commit_msg_header" >&2
          error="true"
        fi
      done
    done
    
    if [ -n "$error" ]; then
      exit 1
    fi
  • ⚠ make .git/hooks/pre-receive executable (unix: chmod +x '.git/hooks/pre-receive')

References


@bloggerklik
Copy link

bloggerklik commented Nov 10, 2023

@qoomon @ttytm Thank you for your answers. The distinction between build and chore seems unclear and confusing. I'm searching for sources, but they are unclear and inconsistent. That's why I'm not considering using chore at all, as the Angular documentation says. What is your opinion?

@ttytm
Copy link

ttytm commented Nov 10, 2023

Since you already mentioned adapting the commit messages to your needs: I think it really depends on the project. The angular style prefixes fit well for the angular project and are fairly universal.

But e.g., at https://github.com/vlang/v/ we use module scoped prefixes. Which work better here than the Angular style prefixes to organize things.

So there is no need to take a narrow-minded view. Sometimes using no pre-fixes is also what is fitting and good enough https://github.com/webui-dev/go-webui/commits/main

Just keep things uniform.

@qoomon
Copy link
Author

qoomon commented Nov 11, 2023

@bloggerklik I use chore for everything that does not fit in any of the other conventional types

@bloggerklik
Copy link

@qoomon Last question😅 We need to use docs for comment lines, right? Thanks.

@qoomon
Copy link
Author

qoomon commented Nov 11, 2023

@wezzcoetzee
Copy link

Thank you so much this is awesome

@worldbestpro
Copy link

Awesome!

@monish-khatri
Copy link

Helpful 😄

@123atif
Copy link

123atif commented Feb 5, 2024

Helpful

@andre-alck
Copy link

Should the first commit of the project be considered as 'build'?

@qoomon
Copy link
Author

qoomon commented Feb 12, 2024

The first commit is tricky, I think build is probably the best choice, however I'd tend to always use just init as the very first commit. Ill add this recommendation to this gist.

@Borodkov
Copy link

can you help me with server side script?
on my home Gitlab @ ubuntu 20.04 LTS create pre-commit hook in repository but got error on push from client:
pre-receive: 32: Syntax error: "(" unexpected (expecting "then")

Next, when comment lines 32 and 37 (if condition)
if ! [[ "$commit_msg_header" =~.....
...
fi
got 3 echo of my commit hash, "ERROR: Invalid commit message format", commit msg body

how rewrite if condition in line 32?

@qoomon
Copy link
Author

qoomon commented Feb 16, 2024

@Borodkov my bad. You need to change the first line of the script from #!/usr/bin/env sh to #!/usr/bin/env bash

@Borodkov
Copy link

how simple!
thanks @qoomon for fast answer

helpful gist

@Borodkov
Copy link

Borodkov commented Feb 16, 2024

can you help with some modifications of regexp for gitlab isuues links patterns (#1, #2, .... #999)?

  • #1 feat: add cool feature

what wrong with this one?:
^(#\d{1,3} )(feat|fix|refactor|style|test|docs|build): .+$

@qoomon
Copy link
Author

qoomon commented Feb 17, 2024

@Borodkov you can not use \d in bash regex use [[:digit:]] instead
e.g. (^(#[[:digit:]]{1,3} )(feat|fix|refactor|style|test|docs|build): .+$)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment