Skip to content

An opinionated specification for commits with a lighter title format compared to conventional commits.

License

Notifications You must be signed in to change notification settings

blameitonyourisp/lightweight-commits

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 

Repository files navigation

Lightweight Commits

An opinionated standard for writing git commit messages, focused mainly on consistency and the ability to automatically generate changelog release notes/prompts based on strictly formatted commits. This repository is hosted on github, if you're already reading this there, then great! Otherwise browse the repository here.

Table of Contents

Summary

This document proposes a lightweight and opinionated standard for writing git commit messages called simply the lightweight commit format. The lightweight commit format is heavily inspired and influenced by the conventional commit format, and by the keep a changelog project. Note that the lightweight commit format has no strict direction with respect to the size of a commit, however for best results it is advised that developers keep their commits as small as possible, preferably adopting atomic commits wherever possible. In this manner, commits will be concerned with only one "thing", and therefore concise commit messages will be easier to write.

Motivation

The main motivation behind creating this standard for writing git commits is a personal drive to standardise the way I write git commits in my own projects. Please see the following list of goals in writing this standard:

  1. Reduce unnecessary characters in the title of the git commit, maximising the space available for the summary 1
  2. Reduce potential ambiguity on the usage of conventional commit format nouns 2
  3. Reduce overlap between conventional commit format nouns, git commit summary lead imperative verbs, and changelog sections suggested by the keep a changelog project 3
  4. Increase clarity with respect to the impact a commit will have on the semver version number of a package
  5. Increase git log utility in automatically generating prompts for package changelogs upon release of a new version
  6. Increase visual uniformity across git commit titles so that they can be scanned more easily when they are logged

Format

With respect to general formatting, the commit title should have a maximum length of 50 characters, the body of the commit should be wrapped at 72 characters, references may overflow this limit if the link is longer, and each remaining line of the footer should have a maximum length of 72. See this gist for some discussion on standard formatting of git commits. See below for further details on the specific formats of each section of the commit.

Title

<noun-abbr><semver-flag><whitespace><summary>

The title must have a maximum length of 50 characters, and should have the format shown in the code block immediately above. Please see below for details on each component:

  1. <noun-abbr> - Three letter noun abbreviation describing the type of the object of a commit (see here)
  2. <semver-flag> - Single character flag describing the impact a commit will have on the semver version number of a package (see here)
  3. <whitespace> - Single space separating the summary
  4. <summary> - Summary of commit in imperative mood, starting in uppercase, not ending with a ".", and leading with a verb describing the type of changes a commit makes. The summary should make sense as standalone sentence (see here)

Body

The body must be separated from the title by one (1) blank line. The body must be wrapped at 72 characters, and should be formed of named sections taking the following format:

  1. Each named section must start with <SECTION_NAME>: (ALL_CAPS_NAME-colon-space) inline with the first paragraph of that section
  2. Each section must be separated from the next by a single blank line
  3. Each section may itself be made up of multiple paragraphs, and may contain the following simple, human-readable markup 4 (only four main markup features are used, all taken from standard markdown syntax, and comprising a minimum set of features required for writing succinct commits):
    1. Paragraphs separated by single blank lines
    2. Markdown style italic emphasis using single asterisks around a word/phrase to highlight it 5
    3. Markdown style lists nested to any required depth (starting with no padding) 2. The ordered lists deliminator <number>. (number-dot-space) 3. The unordered list deliminator is - (dash-space) 4. List items spanning more than one line should have subsequent lines space-aligned with the start of the item text (not the list deliminator) of the line above 5. List item text should start with a capital letter, but not end with a "."
    4. Markdown style references using caret notation
      1. Any link will be inserted using markdown references in order to prevent awkward line wrapping and/or the body of the commit having lines longer than 72 characters where long links are required
      2. Markdown references may also be used to provide additional text information/explanation which would otherwise clutter the body of the commit or adversely affect readability
      3. To insert a reference in the body of the commit, use [^<ref-name>] (bracket-caret-name-bracket) inline with the text where the link/explanation would be placed
      4. To provide the footer reference for a given number, use [^<ref-name>]: <reference> (bracket-caret-name-bracket-colon-space-link) in the footer section (see here)
      5. Use sequential numbers for the default <ref-name> (reference name), or alternatively use unique custom strings which identify/briefly describe the reference (do not use "see here" or other generic strings, as these will not be uniquely identifiable in the footer)

The body should contain some or all of the following named sections, describing changes in imperative mood prose consistent with the commit title (the named sections included in a given commit body should also follow the order listed below):

  1. WHAT - Details of change(s) made in this commit
    1. Summarise changes in an expanded manner with respect to the title
    2. Compare new functionality with old functionality, especially where breaking changes have occurred
  2. WHY - Motivations behind these changes and why they are required
  3. MIGRATION - Provide migration notes for any breaking changes, these will later inform the update instructions written in the changelog
  4. OTHER - Provide any other relevant details that do not fall under the above categories

The body should not explain the "how?" of the commit; this should either be self evident from the code, or should be reserved for source code comments or custom documentation.

Footer

The footer must be separated from the body of the commit by one (1) blank line. The footer should take the following format (note that where both footer sections are used, the git trailers must come second):

  1. Optional reference section
    1. Each reference may either be a link reference or a text reference which expands on/explains a concept in the commit body
    2. Each reference must start on a new line, but must not be separated from the reference above by a blank line
    3. Text references must form one (1) paragraph formed of standard sentence(s), wrap at 72 characters, and end with a "."
    4. Link references must occupy one (1) line only, and may be longer than the 72 character limit if a long link is required
    5. For each reference of either type, follow the format [^<ref-name>]: <reference> (bracket-caret-name-bracket-colon-space-link)
    6. The default <ref-name> (reference name) is a set of sequential numbers
  2. Blank line if both references and git trailers are used (git trailers must be separated from references by one (1) blank line for correct parsing)
  3. Optional git trailers section (see below for details)

The end of the footer must be comprised only of a series of git trailers, which must each be a maximum of 72 characters. Note that git trailers are key value pairs which may be parsed by git from the end of each commit, they should be formatted as follows:

  1. Of the form <key>: <value> (key-colon-space-value)
  2. Keys must be written in kebab-case (preferably lowercase)
  3. Key and value must be separated by ": " (colon-space)
  4. Value can be any string, but should be considered/written as an array of space separated values
  5. Each trailer occupies one line only, with a maximum length of 72 characters (i.e. no multi-line trailers allowed)
  6. Trailers must follow each other directly without empty lines between them

Additionally, git trailers follow the RFC 822 format for email headers. Strictly speaking this implies the usage of "Uppercase-Kebab-Case", obviously this is not a strict requirement, and for the lightweight commit format, "lowercase-kebab-case" for trailer keys is preferred simply for aesthetic reasons.

Trailers can be used for storing values including, but not limited to some of the following examples (the entire value of the trailer key value pair is parsed by git as string, and it is up to the user to interpret these strings as required):

  1. co-authored-by: <git-user>
  2. closes: <issue-id>
  3. scope: <path/to/module>
  4. issues: array of related issue ids

Most git trailers are used for automation processes such as closing issues. In order to parse git trailers, the value of each git trailer should follow a known and expected format (git trailer values can be any arbitrarily formatted string, although obviously this is pointless for the purposes of automation etc.). The expected format for a given trailer value will depend on the implementation of commit parsing/automation within your repository. For example the co-authored-by: <git-user> may be expected to have a value formatted as and email. In any case, it is recommended that when parsing git trailers, the value is interpreted as an array generated form the string value of the trailer split on single whitespace characters. In this case git trailers with array-like values are possible, and in the edge cases where a full string is required, the original string can be recovered by joining the array.

Title Nouns

Each git commit title must begin with a three letter title noun abbreviation. This abbreviation is a noun reflecting the type/location/scope of code being affected by the commit, i.e. the type of the subject of the commit. The type of code change being made to the subject of the commit is described by the summary verb. Please see the following table for the recommended title nouns.

Abbreviation Expanded Description
cfg config Changes to any configuration file
doc documents Changes to any documentation file, or changes which only concern code comments
ext external features Changes to the external API, i.e. any changes to code which change external functionality and how the users interact with the repository
int internal features Changes to the internal API, i.e. any changes to code which only affect internal functionality and other developers, and do not change the external API
msc miscellaneous Changes to any other part of the repository that are not covered above, for instance asset management
bld build Reserved noun for build commits, usually triggering a new release
rvt revert Reserved noun for revert commits
mrg merge Reserved noun for merge commits

Conventional Commit Format

The lightweight commit title noun should not be confused with the title nouns suggested by the conventional commit format, which often indicate both the type of code being changed and the type of change being made (for example the fix (fix) noun indicates both that the commit makes changes to a part of the internal/external API and that the nature of the change fixes a bug). The lightweight commit format strictly separates these concerns, using the noun to reflect only the type of code being changed.

Internal Changes

In some cases (for example where a performance issue, bug etc. propagates from the internal API to the external API), changes to the internal API may require updates to the patch number (or even minor change number where appropriate) of the semver version number of the repository/distributed code. In these cases it will be possible to see commit titles starting int~ rather than the expected int= reflecting that internal changes will not normally update the version number.

Chores

The conventional commit format suggests only chore (chore) as the title noun for non code-related changes, however also suggests docs (documentation) for use with changes to documentation. The lightweight commit format suggests further division of non code-related changes into doc (documentation), cfg (config), and msc (any other miscellaneous chore such as asset management). This is due to the fact that configuration is usually an important and technical aspect of any project/repository, and changes of to the configuration should ideally be separated from other solely miscellaneous managerial tasks (i.e. separating technical config chores from chores which are less technical in nature).

Exceptions

Exceptions are made for the bld, rvt, and mrg nouns, which do not reflect the type of code being changed, but instead are reserved nouns for special types of commit which do not have exact subjects: running a build command should not change source code; changes when reverting a commit depend on the nature of the reverted commit; merge commits do not change code, only change branches in the git history. In this case the title noun is simply a contraction of the summary verb.

Semver Flags

Following the title noun of the commit title, each title must contain a semver flag indicating the impact a commit will have on the semver version number of a package. Please see the following list for all of the available flags and their usage:

  1. ! Breaking change to the external API, requiring semver major update
  2. ^ Minor change to the external API, requiring semver minor update
  3. ~ Patch to the external API, requiring semver patch update
  4. = Internal change which does not propagate changes to the external API, and therefore does not require semver change or changelog entry
  5. ? Other, reserved for build and revert commits

These flags are chosen to be as consistent with current standards as possible, based on the following factors:

  1. Existing use of ! (exclamation mark) flag to notify breaking changes by the conventional commit format
  2. Existing use of the ^ (caret) and ~ (tilde) flags to denote version ranges of software packages used in project in, such as in a package.json file explained here
  3. = (equal sign) flag to indicate that changes make no difference to the version of the distributed software/external API
  4. ? (question mark) flag to denote the fact that the version change of a given commit is uncertain

The semver flag should always be decided solely based on the minimum type of version change (i.e. major, minor, patch, or unchanged) that will be required at the next build/software release. Note that the minimum version change at the next release is determined by changes to the external API; i.e. version numbers are for end users, and commits are for developers (see these version increment and breaking vs non-breaking change notes for more). Please see Although certain title nouns and or summary verbs will commonly be used with certain flags, the title nouns and summary verbs can in no way mandate the use of a given semver flag.

Summary Verbs

Each git commit summary must start with an imperative mood verb reflecting the type of code change which the commit makes. As mentioned above, the summary verb of a given commit can often be used with selection of semver flag. The flag used for a given commit title should entirely depend on the changes made, although the notes below may mention common flags for each summary verb.

Note that the verbs listed under the following subheadings are not the only available verbs for use within this specification, but instead a set of suggested commonly used verbs. The verb chosen for a given commit should always best reflect the changes being made, and if required other verbs not listed below may be used. However, using a verb listed below for changes which are different to those described below for the given verb is not recommended, since this will cause commits to be ordered incorrectly when generating changelog release notes.

Feature Lifecycle Changes

Consult the following table for standard "lifecycle" commits which may be made for any given feature. These verbs cover the normal development of a feature from the initial addition of the feature to the repository, to different types of changes which may be made to the feature, and to the possible removal of the feature from the repository. In this case "feature" may apply to config files, documentation, or any other external or internal feature of the repository.

Verb Description
Add Addition of a new feature, commonly requiring a semver minor update for additions to the external API
Modify Patch changes to a feature which are forward and backward compatible with previous version/commits, commonly requiring a semver patch update for modifications to the external API
Change Minor, backward compatible only changes to a feature, commonly requiring a semver minor update for changes to the external API
Rewrite Major, breaking changes to a feature, commonly requiring a semver major update for rewrites of a feature of the external API
Deprecate Deprecate a feature for future removal, commonly requiring a semver minor update for deprecation of a feature of the external API
Remove Remove a previously deprecated feature, commonly requiring a semver minor update for removal of a feature of the external API

In the event that changes to a feature make a breaking change and warrant a semver major version number update (i.e. a Rewrite in the table above), consider instead deprecating the existing feature (to be removed at a later date at the same time as other major changes), and adding a new, rewritten version of the feature under a new name. Compared to directly rewriting the feature and triggering a major update, this allows for only a minor, backwards compatible update to occur, cutting down on the number of major updates required.

Fixes and Tests

Consult the following table for commits concerning testing, fixing bugs, resolving security issues, and improving code performance.

Verb Description
Fix Bug fixes, commonly requiring a semver patch update for fixes which propagate to the external API
Test Changes, additions, and removals from the test suite, commonly requiring no semver version update as tests are usually internal only in nature
Secure Change or bug fix addressing security of a feature, commonly requiring a semver patch or minor update for security fixes to the external API, depending on severity of the security issue
Improve Change or bug fix improving performance of a feature, commonly requiring a semver patch update for improvements to the performance of the external API

Code Style

Consult the following table for commits concerning code formatting, changes to naming conventions etc.

Verb Description
Lint Lint code involving changes strictly to formatting which do not alter the released code, and therefore should not require semver version update
Refactor Minor changes to names and statement orders etc., commonly requiring a semver patch or minor update for refactoring code which is part of the external API, depending on the nature of the refactor

Dependencies

Consult the following table for commits which update repository dependencies or migrate to a new dependency fulfilling the same purpose/feature set as the old dependency (for example switching between image processing libraries). Note that these commits may be tiny or fairly large depending on the amount of code which has to be updated to work with the API of the new/updated dependency. Where appropriate, it may be sensible to split these changes into multiple commits if code changes are extensive, and/or cover many parts of the repository (for instance when upgrading between major versions or migrating to a different dependency).

Verb Description
Bump Bump (semver patch) version of a dependency, may require any or no update to the semver version number depending on changes required to use the updated dependency
Update Update (semver minor) version of a dependency, may require any or no update to the semver version number depending on changes required to use the updated dependency
Upgrade Upgrade (semver major) version of a dependency, may require any or no update to the semver version number depending on changes required to use the updated dependency
Migrate Migrate to a different dependency, may require any or no update to the semver version number depending on changes required to use the new dependency

A commit which adds a dependency will not change internal or external code, and will only be adding the dependency for use in future features. Therefore, for adding a dependency in the first instance, since no existing code in the repository will be using that dependency at time of adding, we can use the Add verb in conjunction with the cfg (config) title noun.

Build, Revert and Merge

Consult the following table for descriptions of the build, revert, and merge verbs.

Verb Description
Build Build new release, commonly triggers new release of package accompanied by a new version number, change required to semver version number depends on previous commits
Revert Revert commit using reverted commit hash as rest of title, change required to semver version number depends on the reverted commit
Merge Merge commits, commonly referencing the merged branches in the title, and containing merge details in the body

Examples

See below for an example of a standard commit with the correct format, and some build, revert, and merge commit title examples.

Regular

See below for an example of a full, generic commit with title, body, and git trailers. The body contains all of the probable required sections for describing changes made by a commit. Note that, as described above, each section in the body is labelled for clarity. The body content also displays examples of most of the suggested allowable markup (emphasis, paragraphs, lists, and references)

ext! Remove useful feature

WHAT: Remove useful feature, replaced by updated dependency

WHY: Last update to dependency XYZ (see commit <hash>) adds new
functionality which supersedes functionality provided by this existing
feature. Here is a *very important* phrase.

MIGRATION: Stop using this feature, and instead make calls to package
XYZ.newFeature. Users will need to provide the new function with the
following data (for more information see here [^1]):
- Client name
	- First name
	- Last name
- Client age

[^1]: https://link.com/goes/here

closes: ABC-123
scope: path/to/module

Build

See below for commit titles for build, revert, and merge commits. These titles may follow a more strict/generic format simply because of the nature of changes to code/git history which are occurring.

Build commit titles may include further brief details of the changes or features which are being built. However, since a these commits only changes distributed code according to how the source code has changed since the last build (i.e. a build commit does not change source code), no unique commit title or details are particularly required.

bld? Build

Revert

Revert commits MUST follow the format below, where the hash fragment must uniquely identify the reverted hash from git history (git requires a minimum of 4 characters to uniquely identify a commit). Obviously the hash fragment can be anywhere up to the full hash length of 40 characters, although this would cause the commit title to be longer than the 50 character limit (it is recommended to use the standard short hash length instead as shown below). No other details should be included in the title, and details of the reverted changes etc. may be expanded on in the body.

rvt? Revert 116cd42

Merge

Merge commit titles may mention which branch is being merged into which other branch. Further details of the merge, for instance if it is a feature branch being merged into main etc., should obviously be reserved for the commit body.

mrg? Merge branchA into branchB

Template

Using a git commit template can make it easier to create uniformly formatted git commits, and can serve as a good aide memoir for following custom formats. The git commit template can be set by pointing the git config to your message file as follows git config --global commit.template ~/.gitmessage. See the following code block for my commit template, or view the file at ~/.gitmessage. This template also serves as an exhaustive specification for the format of lightweight commits.

# <--           MAX_TITLE_WIDTH_50           --> #
# NOTE: Existing blank lines MUST be preserved for spacing between commit blocks
# TITLE: Git title MUST take the form "<noun><semver-flag> <summary>":
#   - Title noun describing type of the subject of commit:
#       - <cfg> Changes to any configuration file
#       - <doc> Changes to any documentation file or source code comments
#       - <ext> Changes to the external API
#       - <int> Changes to the internal API
#       - <msc> Changes to any other part of the repository
#       - <bld> Reserved noun for build commits
#       - <rvt> Reserved noun for revert commits
#       - <mrg> Reserved noun for merge commits
#   - SemVer flag indicating minimum update required at next release:
#       - <!> Breaking change requiring semver major update
#       - <^> Minor change requiring semver minor update
#       - <~> Patch requiring semver patch update 
#       - <=> Internal change not requiring semver change or changelog entry
#       - <?> Other, reserved for build and revert commits
#   - Summary imperative mood, starting in uppercase, and not ending with a ".":
#       - Feature lifecycle changes:
#           - <Add> Addition of a new feature
#           - <Modify> Patch, forward & backward compatible changes to a feature
#           - <Change> Minor, backward compatible only changes to a feature
#           - <Rewrite> Major, breaking changes to a feature
#           - <Deprecate> Deprecate a feature for future removal
#           - <Remove> Remove a previously deprecated feature
#       - Fixes and tests:
#           - <Fix> Bug fixes
#           - <Test> Changes, additions, and removals from the test suite
#           - <Secure> Change or bug fix addressing security of a feature
#           - <Improve> Change or bug fix improving performance of a feature
#       - Code style:
#           - <Lint> Lint code (formatting ONLY, no change to released code)
#           - <Refactor> Minor changes to names and statement orders etc.
#       - Dependencies (including required changes made upon version change):
#           - <Bump> Bump (semver patch) version of a dependency
#           - <Update> Update (semver minor) version of a dependency
#           - <Upgrade> Upgrade (semver major) version of a dependency
#           - <Migrate> Migrate to a different dependency
#       - Build, revert, and merge commits:
#           - <Build> Build new release
#           - <Revert> Revert commit using reverted commit hash as rest of title
#           - <Merge> Merge commits

# INSERT BODY AT THIS COMMENT                                                <--    
# <--                      WRAP_BODY_WIDTH_72                      --> #
# BODY: Series of named sections which MUST take the following format:
#   - "<SECTION_NAME>: " inline with the first paragraph of that section
#   - Each section MUST use imperative mood prose to describe changes
#   - Each section MUST be separated from the next by a single blank line
#   - Each section MAY contain the following markup in markdown format:
#       - Paragraphs separated by single blank lines
#       - Italic emphasis (single asterisks around word/phrase)
#       - Ordered and unordered lists nested as required (starting at 0 padding)
#           - Ordered lists deliminator MUST be "<number>. " (number-dot-space)
#           - Unordered list deliminator MUST be "- " (dash-space)
#       - References using caret notation
#           - Each body reference MUST be of the form "[^<ref-name>]"
#           - ALL links MUST be inserted using references to prevent overflow
#           - Text references MAY be used for additional explanation
#           - Reference names MUST be unique
#           - Use sequential numbers for the default reference name
# The body MUST contain some or all of the following named sections:
#   - <WHAT> Details of change(s) made in this commit
#       - Summarise changes in an expanded manner with respect to the title
#       - Compare new functionality with old functionality
#   - <WHY> Motivations behind these changes and why they are required
#   - <MIGRATION> Provide migration notes for any breaking changes
#   - <OTHER> Other details which strictly do not fit into the above groups
# For each included section, follow the order listed above.
# Do NOT include the "how?" of the commit (this should be in documentation).

# INSERT REFERENCES AT THIS COMMENT                                          <--
# <--                   WRAP_REFERENCES_WIDTH_72                   --> #
# FOOTER_REFERENCES: Markdown style references which MUST be of the format:
#   - Each footer reference MUST be of the form "[^<ref-name>]: <reference>"
#   - Each reference MUST start on a new line
#   - No blank lines between references
#   - Every reference in the body MUST have matching value in footer
#   - Text references MUST form 1 paragraph wrapped at 72 chars
#   - Link references MUST occupy ONLY 1 line, but MAY be longer than 72 chars

# INSERT GIT TRAILERS AT THIS COMMENT                                        <--
# <--                   MAX_GIT_TRAILER_WIDTH_72                   --> #
# FOOTER_TRAILERS: One line key-value git trailers following RFC 822 format:
#   - Each trailer MUST be of the form "<key>: <value>"
#   - Keys MUST be written in kebab-case (preferably lowercase)
#   - ONLY 1 line per trailer, each trailer MUST have a max length of 72 chars
#   - For array-like values, write trailer as a space separated list of values
#   - Trailers MUST follow each other directly without empty lines between them
# For example:
#   - "closes: <issue-id>"
#   - "scope: <path/to/module>"
#   - "issues: array of related issue ids"

Note that git will ignore trailing whitespace, and will ignore double line breaks by default, so in the git template message example above, whether you include git trailers and or references, the footer will be correctly separated from the body of the commit by only one blank line.

Changelog

By following a well defined title format, and using a broad set of specified title verbs, it is possible to parse each commit, determining which changelog category the commit belongs to, and further subdividing into breaking (major) changes, minor changes, and patches.

Please see the following list for examples of existing projects which create changelog output from commits following a specific format (usually adjacent to the conventional commit format).

Note that since the lightweight commit format is is a custom commit format, and since the changelog generators referenced below are fairly "heavy" in terms of configuring them for custom usage, it is probably more appropriate to write your own custom script to parse git commits following the lightweight format.

Parsing Git Trailers

Note that git trailers may be parsed directly from a string, file or git commit object in multiple ways. Primarily we should use the %(trailers) placeholder when specifying a string for the --pretty option using git log as shown in the following code block (see the git documentation for more detail on pretty formatting).

# logs git trailers of a specific commit
git log $COMMIT_HASH -1 --pretty="%(trailers)"

Alternatively, we can pipe the printed output of git log or git format-patch to git interpret-trailers as shown in this discussion. This can be achieved either by piping the result directly, or by supplying a path to a file containing the git log output.

# pipe result directly
git log $COMMIT_HASH -1 --pretty="%B" | git interpret-trailers --parse

# interpret-trailers can also take a path to a file
git log $COMMIT_HASH -1 --pretty="%B" > commit-message.txt
git interpret-trailers --parse commit-message.txt

Parsing Git Commits

As shown above, parsing git trailers is trivial using the --pretty option. Using a few more placeholders, it is possible to parse all information required from a commit to generate changelog prompt objects. The following command will parse all this information.

# logs long hash, summary, and git trailers of a specific commit
git log $COMMIT_HASH -1 --pretty="%H%n%h%n%as%n%s%n%(trailers)"

In order, this command will return the following data separated by single line breaks:

  1. %H Long commit hash
  2. %h Short commit hash
  3. %as Author date (YYYY-MM-DD)
  4. %s Commit summary (title of commit)
  5. %(trailers) git trailers (each separated themselves by single line breaks)

To fetch a list of git commits, use git log with the appropriate --pretty option to list all long commit hashes separated by line breaks. All entries in a changelog should list the start and end short hashes; by default, when parsing git commits for the purposes of updating the changelog, the start commit hash will be equal to the end commit hash listed in the most recent changelog entry.

# get all commit hashes since a certain commit (exculding start commit)
git log $COMMIT_HASH_START..HEAD --pretty="%H"

# specify start and end commit hashes (exculding start commit)
git log $COMMIT_HASH_START..$COMMIT_HASH_END --pretty="%H"

# to include starting commit in output, use "~" (tilde) to specify revision range
git log $COMMIT_HASH_START~1..$COMMIT_HASH_END --pretty="%H"

If a valid starting commit hash is not specified/found, then the hash should default to either the commit hash of the last tag, or the commit hash of the first commit. Note that there may be more than one root commit depending on the git tree, and a changelog should ideally be generated using known start and end commit hashes (see this discussion for more details).

# get commit hash of last tag (will throw error if no tags present)
git log $(git describe --tags --abbrev=0) -1 --pretty="%H"

# get first commit hash
git rev-list --max-parents=0 HEAD

# git first commit hash using linux "head" command
git log --reverse --pretty="%H" | head -1

When parsing commits to generate changelog prompts, only commits on the main/deployment branch should be of interest. Branches should be used for developing features etc., then merged appropriately to the main branch for deployment. The commit hashes used for generating changelog prompts should therefore be commits on the main branch. This version tags etc. sh means that any script used for generating changelog prompts should be run from the main branch such that the HEAD commit is a main branch commit, and would be limited to use on the main branch (or version tags on other branches should follow a separate format).

Changelog Categories

Consult the following table for a set of standard categories which may be included in an changelog entry for a new version or release (depending on the changes made since the last release, some or all of the following categories will be included in a new changelog entry).

The standard set of categories chosen are mainly based on the suggested categories from the keep a changelog project, combined with additional commit title summary verbs for feature lifecycle changes and others which may cause a semver version change. The added categories are for increased separation between changes which cause different minimum semver version changes: by adding Rewritten and Modified as changelog release categories, all categories now have a predominant minimum semver version change associated with changes/commits in that category.

Performance and Other are added as they are common extensions (see here) to the categories suggested to the keep a changelog project. Performance allows fixes to be further separated into general bug fixes, security fixes, and performance fixes. Other obviously allows flexibility, especially when the changelog category of a commit cannot be automatically inferred for any given reason (in this case Other allows for the commit to be prompted/included in the generated release notes, and correctly categorised by hand later).

Dependencies is added to cover the remaining title summary verbs which may give rise to a semver version change. Given that dependency changes are less frequent, and given that, where possible, code changes when updating a dependency should not force any semver version change (especially avoiding any breaking changes), Dependencies are grouped as a single changelog category rather than separating into categories for the Bump, Update, Upgrade, and Migrate title summary verbs.

The title summary verbs Test, Lint, Refactor, Build, and Revert have no dedicated changelog category as these commits should not normally be appearing in any changelog release note. In the event that a commit with one of these summary verbs does produce a semver version change, the Other changelog category should be used. When parsing commits for a new release, any revert commits encountered should be used to remove the reverted commit from the revision list (list of commits being processed); if the list is consumed from newest to oldest, then obviously any revert commit will be encountered before the commit being reverted, and it is possible to remove that commit before it is processed.

As with categories from the keep a changelog project, changelog categories are past tense version of the imperative mood verbs which would otherwise be used in commit titles.

The semver column refers to the most common minimum semver version change that will be required by changes in this category, however any category may include changes which require any minimum semver version change. In every category, the changelog prompt generated for each change will list the inferred minimum semver version change alongside the prompt. In this case of this table, "feature" may apply to config files, documentation, or any other external or internal feature of the repository.

Category Semver Description
Rewritten major Changes which rewrite a feature in a manner which causes a breaking change
Removed major Removal of a feature which has probably been previously deprecated
Added minor Addition of a feature
Changed minor Changes to a feature giving rise to a minor version change
Deprecated minor Deprecation of a feature which will likely be removed at the next major version
Modified patch Changes to a feature giving rise to a patch version change
Fixed patch Generic bug fixes
Security patch Bug fixes or changes concerning security issues
Performance patch Bug fixes or changes concerning performance issues
Dependencies any Changes which bump, update, upgrade or migrate dependencies
Other any Changes which do not have a category which can be automatically inferred

Changelog Format

See below for an example format of a new release section in a changelog using lightweight commits to auto-generate prompts for each commit since the last release which may be expanded on and or merged by the author to create more succinct, human readable changelog entries. The example format is a handlebars template for a section of a markdown changelog.

<!-- INSERT_AUTO_CHANGELOG_HERE -->

## [unreleased~{{tag}}](https://github.com/{{user}}/{{repo}}/tags) ({{date}}) [{{shortHash}}](https://github.com/{{user}}/{{repo}}/tree/{{longHash}})

### Release Highlights
{{#categories}}
{{> category}}
{{/categories}}

See below for the handlebars category partial, which is the format for each category within a new release section of the changelog (depending on the changes included in the new release/version, some or all of the changelog categories may be included). The array of commits provided to render this template should be ordered with all breaking (major) changes first, followed by minor changes, and finally patch changes. This ensures that the most important/dangerous changes are given precedence at the top of any given category.

### {{title}}

{{#commits}}
{{> prompt}}
{{/commits}}

See below for the handlebars prompt partial, which is the prompt format for each commit since the last version/release (or for all commits in the desired revision/commit range), and follows the suggestions listed in the changelog prompts section. Commits marked as internal only changes (= semver flag) should not be listed/prompted.

- {{semver}}: {{summary}} ([{{shortHash}}](https://github.com/{{user}}/{{repo}}/tree/{{longHash}}))
{{#trailers}}
    - {{key}}: {{value}}
{{/trailers}}

The {{semver}} value should be a string reflecting the minimum semver version change required by the changes in the given commit:

  1. **BREAKING CHANGE** Major changes rendered in bold and ALL CAPS
  2. *Minor* Minor changes rendered with italic emphasis
  3. *Patch* Patch changes rendered with italic emphasis
  4. *Unkown* Where the minimum semver version change cannot be inferred from the commit title, render as unknown with italic emphasis

Changelog Prompts

By using commits formatted in a way which allows the inference of both the semver version impact of a commit and the changelog category of a commit, all commits can be parsed into objects reflecting these properties. The parsed commit object could look something like this for each commit:

{
	"commit": {
		"hash": "<long-commit-hash>"
		"summary": "<regex-separated-summary>"
		"trailers": [
			{ "key": "scope", "value": "path/to/module" }
		] 
	},
	"semver": "minor",
	"category": "added"
}

Using an object like this in conjunction with some template files such as mustache templates, it is then easy to create changelog "prompt entries" for every commit (excluding those marked as internal). Such prompt entries could appear as follows (listed underneath their respective changelog categories, with breaking changes listed at the top of each category):

[!example] Major Change BREAKING CHANGE: {summary} ({hash})

  • {trailers}

[!example] Minor Change Minor: {summary} ({hash})

  • {trailers}

[!example] Patch Patch: {summary} ({hash})

  • {trailers}

These changelog prompts could be inserted into the unreleased version section of a changelog as suggested by keep a changelog (or some custom release prompts section etc. to indicate that they are auto-generated notes). Once included in the changelog, these prompts could either be used directly for the changelog entry at the release of the next version, or more appropriately should be used as prompts to inform the writing of more human-readable changelog notes.

Roadmap

The lightweight commit format is feature complete as of git tag v1.0.0. Minor changes to the specification may be made, but in all cases changes should attempt to be backwards compatible such that existing commits which adopted this format remain consistent with newer version/additions to the format.

This commit format is an opinionated specification maintained by an individual developer, if you would like to use this specification in your own commits, and would like to make changes, feel free to fork this repository and release your own version(s). Please see below for proposed new features which may be added in later updates:

  • Documentation site (i.e. external from the existing github repository) for the lightweight commit format

License


DISCLAIMER The author(s) of this repository are in no way legally qualified, and are not providing the end user(s) of this repository with any form of legal advice or directions.


Copyright (c) 2023 James Reid. All rights reserved.

This software is licensed under the terms of the MIT license, a copy which may be found in the LICENSE.md file in the root of this repository, or please refer to the text below. For a template copy of the license see one of the following 3rd party sites:

License Text

Copyright 2023 James Reid

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Footnotes

  1. Given that the title is limited to 50 characters, ideally we should spend as few characters as possible on writing meta data about the commit into our title. For instance the scope of the commit is largely superfluous, as it can either be seen in the path of the git log, or written in the git trailers, and is therefore a waste of characters.

  2. Some of the conventional commit format standard nouns have ambiguous or different usages across repositories. For example, the feat (feature) noun is described by the documentation as being used for new features leading to ambiguity with respect to what noun should be used for minor modifications to features; should it be a feature change even though it is not strictly an entirely new feature, or should it be something else, and if so what should it be given that no other noun obviously fits? See here for an example of a discussion of this topic. See here for similar queries regarding the removal of a feature.

  3. Some of the conventional commit format standard nouns imply the imperative mood summary verb that will be used, causing a repetition of information in the title which is inefficient for given the character limit of the title. For example, the fix (fix) noun will nearly always imply the verb "Fix" at the start of the commit summary in order to have a standalone commit summary that makes sense: fix: Fix this bug vs fix: This bug (in the first example we see duplicate information in the commit title, and in the second sense the commit summary does not make any sense as a standalone sentence).

  4. All of the markup which is suggested for use in the body of git commits is based on markdown, however it is strictly limited to those elements which are clearly human readable considering that git commits are only ever rendered as text when they are viewed, the commit body wraps at 72 characters, and lines in the commit message starting # are ignored. These considerations eliminate markup such as tables (which would likely be far too wide and unreadable), horizontal rules (which although readable will produce a lot of visual clutter in the git log), any markdown heading (since the line will be ignored by git), and most others. In any case, markup in a git commit should be limited as far as possible to ensure readability and simplicity, and any commit message that requires complicated markup should perhaps be split into multiple commits, or indicate that an update to other documentation is required.

  5. Note that consistent with maintaining simplicity and reducing visual clutter, markdown emphasis in the git commit body is limited to only single asterisk italic emphasis. Given that the git log will render all markup strictly as text, double asterisks for bold text will not be easily discernible from single asterisks for italic emphasis.

About

An opinionated specification for commits with a lighter title format compared to conventional commits.

Resources

License

Stars

Watchers

Forks

Packages

No packages published