Skip to content

Instantly share code, notes, and snippets.

@qoomon
Last active March 27, 2024 15:31
Star You must be signed in to star a gist
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.

ℹ️ git-conventional-commits A CLI util to ensure this conventions and generate 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


Copy link

ghost commented Mar 26, 2022

Thanks for sharing! :)

@marcothz
Copy link

marcothz commented Apr 6, 2022

Hey @qoomon,

I've just made a tiny fix and made it available here conventional_commit_messages.md.

💙

@qoomon
Copy link
Author

qoomon commented Apr 7, 2022

@marcothz thanks a lot I'll merge it

@jmrafael
Copy link

Wonderfull 😁👏

@gutisalex
Copy link

using gitlab, the MR always puts its own commit message smth like Merge branch 'feature xyz' into 'develop'. I found where I can change the template under general - settings... but I wonder what would be the type of merge and squas commit messages?

@qoomon
Copy link
Author

qoomon commented Jun 30, 2022

In my opinion there are 2 two way to handle merge messages

  • #1 allow default merge messages e.g. https://github.com/qoomon/git-conventional-commits will treat them as a merge commit
  • #2 decide whether it is a fix, a feat or something else and try to sum up what has happened on the branch and create a new commit message.

I highly recommend the first way if your merge contains more than one type of commits

@andreygio
Copy link

Hi @qoomon, I am getting this error: No names found, cannot describe anything when I executed version command in a new repository what should I do to get my first changelog file when the repository is new and have no tags?

@qoomon
Copy link
Author

qoomon commented Aug 16, 2022

@andreygio for now you need to tag the initial commit with v0.0.0 or whatever your version pattern looks like. I'll consider to fix that in the cli by having a default version of 0.0.0 in that case.

@qoomon
Copy link
Author

qoomon commented Aug 17, 2022

@greeneley
Copy link

Great, thanks a lot

@howsmyanimeprofilepicture

Thank you for the nice resource !! It is really helpful to me ... 💕💕💕💕💕

@qoomon
Copy link
Author

qoomon commented Sep 17, 2022

@howsmyanimeprofilepicture glad to hear that.

@BoboTheBarbar
Copy link

Thanks a lot, that is really helpful 👍
One question though: is there a reason we prefer the imperative, present tense in subject and body?

@qoomon
Copy link
Author

qoomon commented Oct 26, 2022

@BoboTheBarbar this is caused by the idea that a commit message should always complete the sentence This commit will... or This commit will <subject>.

@BoboTheBarbar
Copy link

@qoomon this, sir, just made writing my commit messages much more meaningful and rewarding for me. Thanks for that :)

@0xProtocol
Copy link

How to commit new comments? Is this "style:"?

@qoomon
Copy link
Author

qoomon commented Nov 22, 2022

I would say docs

@marchingon12
Copy link

How about commuting translations and updating strings? Which type would be most suitable for this kind of commit? feat(il8n): added/updated fr, de languages perhaps?

@qoomon
Copy link
Author

qoomon commented Feb 20, 2023

@marchingon12 I probably would go for chore(i18n): yada yada yada if it is just an extension of i18n. I would not choose feat because it does not add or change behaviour.

If you introduce i18n the first time or if you add totally new languages I would go for feat(i18n): yada yada yada

@marchingon12
Copy link

@qoomon thnx a bunch! This helped a lot because I was struggling with which type to use, especially when I have an action that auto generated changelogs based on conventional commits. Thank you again! :D

@qoomon
Copy link
Author

qoomon commented Feb 20, 2023

You're welcome 🤗

@wbuck
Copy link

wbuck commented Mar 2, 2023

One thing I'm unsure about with conventional commits is how many commits should a PR have? Should I be squashing all of my commits in to a single commit before submitting a PR? Or can a PR have multiple commits with all commit messages following conventional commits?

@qoomon
Copy link
Author

qoomon commented Mar 2, 2023

IMHO it depends :-) Just think about whether it helps you to understand the history or not.

@dd84ai
Copy link

dd84ai commented Mar 14, 2023

Heyaaa! I liked that tool very much, but discovered it had some bugs that were not solvable due to tool using too many dependencies on other tools.
I remade this tool in golang, making it CI friendly one binary file, which is having zero dependencies from Operational System. Even Git is not needed to its functionality (because it uses inbuilt git-go!). Can be just more comfortable to use because it is not requiring Node.js or anything else installed at system :)
Fixed some stuff, augmented with new one.

https://github.com/darklab8/darklab_autogit
Tool uses also minimal amount of dependencies on other libraries, so hopefully should be more fixable and maintanable.

@qoomon
Copy link
Author

qoomon commented Mar 14, 2023

@dd84ai could you open some issues for the bugs you found at https://github.com/qoomon/git-conventional-commits/issues that would be very helpful. And what do you mean with to many dependencies?

@dd84ai
Copy link

dd84ai commented Mar 16, 2023

@qoomon

@dd84ai could you open some issues for the bugs you found at https://github.com/qoomon/git-conventional-commits/issues that would be very helpful. And what do you mean with to many dependencies?

And what do you mean with to many dependencies?

Node.js is one fragile and big dependency, programs often need certain Node.js version to run a program
Git is another dependency, user needs to have it available in path? for its working. Some people install it to work only git bash

As for other dependencies... strange, not seeing them at the moment. I thought i saw dependency on some semantic versioning library, which caused unresolvable bug. Ergh, probably i got mistaken with another library, which implements a similar functionality. Not really remembering where to seek now since it happened to me 4 moths ago back, when was traversing through different similar tools.
https://www.conventionalcommits.org/en/about/
https://github.com/c4urself/bump2version/blob/master/RELATED.md
Checking dependencies now, there is only dependency on yargs which is pretty much good :) Great job ;) I am sorry for saying disinformation. Your tool and work are certainly great in discovering all interface and rules to make it happen, interacting with users for many years and maintaining the product, my work is merely refactoring your accumulated years of results into a new language view

Well node.js and git are still very big dependencies though. So my remake of the tool still helps to eliminate them and making tool more usable in CI and in a long run when people having different node.js versions already installed. (at least if people don't know how to work with nvm)
Plus golang static typing and error resolving strategy should help to add easier more features and increasing its maintainability in a long run.
Due to golang having features even embedding any template / additional files into same binary, it makes quite smooth end result which will remain being one file always.
+added some new small features, like having ability to request next alpha, beta, prerelease semantic version of a product with setting build tag of semver, and other small things like that

@qoomon
Copy link
Author

qoomon commented Mar 16, 2023

@dd84ai thanks for the details answer

@rezaelahidev
Copy link

Wonderfull 🔥

@WieeRd
Copy link

WieeRd commented Aug 27, 2023

This is by far the most clean and readable description of conventional commits. Amazing job 👍
But I'd like to point out that the breaking change indicator ! is missing in this specification.
Is there any reason this is omitted? I think feat!: fix!: is used more often than BREAKING CHANGE footer.

@qoomon
Copy link
Author

qoomon commented Aug 27, 2023

@WieeRd glad to hear that and thx for your hint regarding the breaking change exclamation mark !, I wasn't aware of that. I'll add this.

@superco01
Copy link

hi @qoomon i made an update here to add revert
kindly give your thoughts on this

@qoomon
Copy link
Author

qoomon commented Oct 6, 2023

@superco01 thx, I've added it as well

@ttytm
Copy link

ttytm commented Oct 9, 2023

If using conventional commit prefixes or not, for those who get stuck with Added, Adding, Adds / fixed fixing fixes and equivalents instead of add / fix in the commit body, think of it this way:

  • Either taking an imperative tone, commanding it to do something: Add ...

  • Or, "this commit should fix ..." / "this PR [aims/wants] to fix ..."

Both ways are semantically more correct versions that come closer to what is actually happening - as commits often should do something, but sometimes don't do what they state or want to do.

So thinking about it as This commit should... would also be more correct than This commit will.... Greetings from the nitpicky asshole in my cortex.

@qoomon
Copy link
Author

qoomon commented Oct 9, 2023

👍

@upmorpheus
Copy link

It is very great!
If you are a senior software engineer, you will use that commits like that.

@ttytm
Copy link

ttytm commented Oct 23, 2023

It is very great! If you are a senior software engineer, you will use that commits like that.

If you are a senior, you hopefully can distinguish that there is very little correlation between the text in a commit message and one's abilities as engineer.

@bloggerklik
Copy link

Here are some confusions 🤔

  1. What should be the conventional commit type when I install or update a new package?
  2. I am developing a mobile app. For example, which type should I use when I upgrade versionCode or versionName to release new version?
  3. Which type should I use when I remove a feature I added?

Thanks.

@qoomon
Copy link
Author

qoomon commented Nov 10, 2023

Hi @bloggerklik, I would use following conversation

1# use build: update dependencies or build: update dependency xxxxx, unless this will change the API of your artifact then it would be a feat: ... commit

#2 use build(release): bump version to x.x.x

#3 use feat!: remove ...., if you change the API it is always a feat: no matter if you add or remove one

@ttytm
Copy link

ttytm commented Nov 10, 2023

What should be the conventional commit type when I install or update a new package?

build or chore: is fitting

I am developing a mobile app. For example, which type should I use when I upgrade versionCode or versionName to release new version?

chore: fits here as well. E.g. https://github.com/clap-rs/clap/commits/master.

Which type should I use when I remove a feature I added?

depends on the reason why it is removed. Could be any of the prefixes.

@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