git-cliff: Changelog Generator for Git History
What git-cliff is
git-cliff is a command-line changelog generator written in Rust. Its README describes the job in one sentence: it generates changelog files from Git history by using conventional commits as well as regex-powered custom parsers, and the changelog template can be customized with a configuration file to match the format you want. The crate's own description is shorter still: "A highly customizable changelog generator".
Writing a CHANGELOG.md by hand is tedious, and the information is already in the commit log. If commit messages follow a predictable convention, a tool can group them, attach them to the tag they shipped in, and render the result in the project's format. git-cliff does that, and goes further: it can calculate the next semantic version from the unreleased commits, pull pull-request and contributor data from GitHub, GitLab, Gitea/Forgejo or Bitbucket, and scope a changelog to one directory of a monorepo.
The audience is broad. Rust maintainers can configure it straight from Cargo.toml, Python projects from pyproject.toml, and JavaScript projects can install it from npm. Release engineers use it in CI to produce release notes, and the project's own continuous deployment workflow uses the companion GitHub Action to set the release notes for its GitHub releases. The code is split into a git-cliff binary crate and a git-cliff-core library crate, both dual-licensed under MIT or Apache-2.0.
How it works
From commits to a rendered changelog
At a high level, git-cliff walks the Git history, turns each commit into a structured object, assigns commits to releases based on tags, and passes the result to a template. The documentation calls that structured data the context: "the model that holds the required data for a template rendering". For a conventional commit, each entry in the context carries fields such as group, scope, message, body, parsed footers, a breaking flag, links extracted by link parsers, author and committer details, and per-commit statistics (files changed, additions and deletions). Each release adds its version, the release commit ID, a timestamp, the repository path and the commit range it covers.
The [git] section of the configuration controls how commits become context. The documented processing steps are commit_preprocessors (regex rewrites applied before parsing), split_commits (treat each line as its own commit), conventional_commits (parse against the Conventional Commits specification), commit_parsers (regex rules that assign groups, scopes or skip commits) and link_parsers (turn references such as #123 or RFC1234 into URLs). An optional processing_order key lets you reorder that pipeline; when it is omitted, git-cliff uses what the docs call the legacy processing flow for backwards compatibility.
Templates in Tera
Rendering uses Tera, a template engine with syntax based on Jinja2 and Django templates. The [changelog] section defines a header and footer that receive the full list of releases, and a body template that is rendered once per release. git-cliff adds its own filters on top of Tera's built-ins: upper_first, find_regex, replace_regex, split_regex, commit_groups (which keeps groups in the order of your configured parsers instead of sorting them alphabetically) and group_by_scope for grouping releases by major, minor or patch version. Postprocessors can then rewrite the rendered output with regex replacements or external commands, and an optional format = true setting runs Markdown output through a built-in formatter.
Configuration discovery
The configuration file can be TOML (preferred) or YAML. When --config is not given, git-cliff looks for cliff.toml, .cliff.toml and .config/cliff.toml in the project directory, walks up parent directories, and then checks a global file under the platform config directory. If nothing is found, it uses the defaults from the repository's config/cliff.toml. Any configuration value can also be overridden with environment variables in the form GIT_CLIFF__SECTION__FIELD, and the schema is published on SchemaStore for editor validation.
Key features
- Conventional commits out of the box. The default configuration parses commits against the Conventional Commits specification and groups them by type, with breaking changes marked in the output.
- Regex commit parsers. Parsers can match the message, body, footer, a specific SHA, or any field in the commit context via dot notation (for example
author.name), and can set a group, override the scope, set a default scope or skip the commit. Settingcontinue = truelets several parsers apply to the same commit. - Built-in templates.
--initcan write configurations for Keep a Changelog, GitHub's release-notes format, GitLab Releases, a detailed format with commit links, a minimal format, scope-grouped formats, a cocogitto-style format and one for unconventional commits. User-defined templates can be kept in a directory and selected with--templates-dir. - Version bumping.
--bumpcalculates the next semantic version from unreleased commits, and--bumped-versionprints it to stdout. The[bump]section tunes the rules for zero-based versions, custom major/minor regexes and commit types that should not increment the version. - Git host integrations. With a remote configured, commits gain usernames, PR titles, numbers and labels, and releases gain contributor lists including first-time contributors, which is enough to reproduce GitHub's generated release notes format.
- Monorepo and multi-repo support. Running inside a subdirectory scopes the changelog to it;
--include-pathand--exclude-pathtake glob patterns, and--repositoryaccepts several repositories whose histories are merged. - Flexible ranges. Generate for the latest tag, the current tag, unreleased commits only, or any Git range such as
v2.2.1..; restrict to tags on the current branch with--use-branch-tags. - Prepend mode.
--prependadds new entries to an existing changelog, using a stableheader_markerto replace the previous header. - Context in, context out.
--contextprints the full context as JSON and--from-contextrenders from a JSON file or stdin, so you can modify the data with external tools in between. Free-form metadata can be attached through anextrafield. - Commit hygiene controls. Skip commits by SHA with
--skip-commitor a.cliffignorefile; commits listed in.git-blame-ignore-revsare excluded automatically.--with-commitinjects a commit message that does not exist yet, such as the release commit itself.
Getting started
Install
The quickstart installs from crates.io. The minimum supported Rust version is 1.88.0:
cargo install git-cliffOther documented channels include Homebrew on macOS, npm, PyPI, Arch Linux's official repositories, Alpine Edge, prebuilt binaries for glibc and musl on the releases page (release tarballs are signed with a published PGP key), and Docker images:
brew install git-cliff
npx git-cliff@latest
npm install git-cliff --save-dev
pip install git-cliff
pacman -S git-cliffThe npm package supports Linux, Windows and macOS on x64 and arm64, requires Node >=18.19, >=20.6 or >=21, and exposes a typed programmatic API (runGitCliff) that calls the binary under the hood. With Docker, run from the Git root with the repository mounted:
docker run -t -v "$(pwd)":/app/ "orhunp/git-cliff:${TAG:-latest}"The crates.io build enables the update-informer, github, gitlab, gitea and bitbucket features by default. To drop the integrations but keep the new-version notifier:
cargo install git-cliff --no-default-features --features update-informerFirst changelog
Write the default configuration, edit it, then generate:
git-cliff --init
git-cliff -o CHANGELOG.mdThe docs note that git cliff works interchangeably with git-cliff in most environments when Git is installed, but the hyphenated form should be used with the npm install. To start from a built-in template instead, or use one without writing a file at all:
git cliff --init keepachangelog
git cliff --config detailedCommon release commands
Label unreleased changes with an upcoming tag without creating the tag, or prepend only the new release to an existing file:
git cliff --tag 1.0.0
git cliff --unreleased --tag 1.0.0 --prepend CHANGELOG.mdLet git-cliff work out the version, or print just the version for a release script:
git cliff --bump
git cliff --bumped-version
git cliff --unreleased --bump --context | jq -r .[0].versionFor a package inside a monorepo, change into its directory and filter paths (patterns are relative to the repository root):
cd packages/some_library
git cliff --include-path "packages/some_library/**/*" --exclude-path ".github/*"A minimal parser configuration
The git configuration reference shows the shape of the [git] section. A trimmed version of its example:
[git]
conventional_commits = true
filter_unconventional = true
commit_parsers = [
{ message = "^feat", group = "Features"},
{ message = "^fix", group = "Bug Fixes"},
{ message = "^doc", group = "Documentation"},
]
tag_pattern = "v[0-9].*"GitHub metadata and GitHub Actions
With a token, git-cliff can query the GitHub REST API for usernames, PRs and contributors. The embedded github template reproduces GitHub's "What's Changed" and "New Contributors" layout:
GITHUB_TOKEN="***" git cliff --github-repo "orhun/git-cliff"
git cliff -c githubIn CI, the companion action needs a full-history checkout so tags and commits are available:
- name: Check out repository
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Generate a changelog
uses: orhun/git-cliff-action@v4
with:
config: cliff.toml
args: --verbose
env:
OUTPUT: CHANGELOG.md
GITHUB_REPO: ${{ github.repository }}Use cases
- Keeping CHANGELOG.md current. Generate the full file once, then use
--unreleased --prependon each release so historical entries are never regenerated or reformatted. - Release notes for a GitHub or GitLab release. Run
--latestor--currentin a release pipeline with the GitHub or GitLab template, and paste the output into the release body, as the project does for its own releases. - Automated version numbers. Use
--bumped-versionin a release script to decide the next tag from commit types, with--offlineor theofflinesetting to avoid any network calls in restricted environments. - Per-package changelogs in a monorepo. Combine
--include-path,--exclude-pathand atag_patternper package to give each library its own history. - Enforcing commit conventions. Set
require_conventionalorfail_on_unmatched_commitso changelog generation fails in CI when a commit does not match, rather than silently dropping it. - Repositories without conventional commits. Use regex parsers or the
unconventionaltemplate to group free-form messages.
How it compares
The README lists several similar or related projects rather than claiming superiority over them. Among those it names are git-journal and clog-cli, older Rust changelog tools; cocogitto, a set of CLI tools for conventional commits and semver; release-plz, which releases Rust packages from CI; git-changelog-command-line, which also generates changelogs and determines the next version from conventional commits; and git-changelog, a generator that uses Jinja2 templates. It also lists cliff-jumper, a Node.js tool that combines git-cliff with conventional-recommended-bump to bump a package version.
Based on the documentation, git-cliff stands out for template control (any Tera template, plus the JSON context round trip), regex parsing that does not require conventional commits, distribution across several ecosystems, and integrations with four Git hosts. Tools such as release-plz or cocogitto cover more of the release process itself, like publishing or commit linting, while git-cliff concentrates on producing the changelog and the next version number and leaves tagging and publishing to your own scripts.
Things to know before adopting
- Non-conforming commits are dropped by default. The default configuration sets
conventional_commits = trueandfilter_unconventional = true, which the docs describe as the default behaviour of completely excluding unconventional commits from the changelog. If your history is mixed, add a catch-all parser such as an "Other" group withfilter_unconventional = false, or setrequire_conventionalto make generation fail instead of silently omitting entries.fail_on_unmatched_commitdoes the same for commits that no parser matches. - Merge strategy matters. The FAQ recommends squash merges for PR-based workflows, because rebase merges or merge commits may fail to associate PR-level context such as titles, labels and issue references with a single commit.
- Remote APIs need tokens. The GitHub integration uses the REST API, which allows 60 requests per hour unauthenticated. The docs say that is enough for a couple of runs but recommend an access token; a classic or fine-grained token without permissions works, and
secrets.GITHUB_TOKENis enough in GitHub Actions. GitLab and Bitbucket have their own rate limits. Putting the token in the configuration file is marked "not recommended"; use the--github-tokenflag orGITHUB_TOKEN(and the equivalents for other hosts). - Guard remote fields in templates. The GitHub integration page warns that
commit.remoteis absent for any commit the API did not return, and an unguarded lookup aborts the whole render. Builds from source also need the matching feature flag for each integration. - Version bump rules have edge cases.
fixbumps patch,featbumps minor and breaking changes bump major, but with major version 0 the defaults change:features_always_bump_minorandbreaking_always_bump_majordecide whether a 0.x project stays below 1.0. For unconventional commits only the subject line is considered, and the computed version must matchtag_pattern. - Tag handling is commit-level. If a commit carries several tags and one matches
ignore_tags, all of that commit's tags are ignored, a limitation the docs call out explicitly. - Templates have a learning curve. Customising output means writing Tera templates and regular expressions. The default template already uses whitespace-control delimiters and filters, and the docs added the
formatoption precisely so users don't have to fight the template with{%-andtrimto get tidy Markdown. - External commands run by default. Preprocessors and postprocessors can call shell commands through
replace_command. When running a configuration you did not write, especially one fetched with--config-url,--no-execdisables external command execution. - Monorepo limits. Path filters must be relative to the repository root and valid globs, multiple unquoted values after one option are not supported, and setting
include_pathsmeans the current working directory is no longer included automatically. Multiple repositories passed to--repositoryproduce one changelog from their merged history. - Recent breaking changes. The 2.14.1 release notes mark config discovery when
--configis omitted, the newcommit.remote.pr_authorfield and a change to how greedy multi-value options interact with the positional range as breaking. Pin a version in CI and read the changelog before upgrading. - Licence. The project is dual-licensed under Apache-2.0 or MIT at your option, per the README, both licence files and the
licensefield of each crate, so it is straightforward to vendor or embed.
Project activity
As of November 2026 the repository has about 12,500 stars and around 365 forks on GitHub. It was created in May 2021, is written in Rust, and the default branch main received its most recent push in early November 2026. The latest release recorded in the project's changelog is 2.14.2, dated September 18, 2026, which allowed commits to be parsed by multiple parsers and fixed path resolution with --workdir; it followed 2.14.1 on September 1, 2026.
The code is licensed under MIT OR Apache-2.0. The README lists a RustLab 2023 talk, an Emacs integration and a second-place finish at the KaiCode Open Source Festival 2024. The source, issues and releases are at github.com/orhun/git-cliff, and the documentation is at git-cliff.org.
Enjoying this project?
Discover more amazing open-source projects on TechLogHub. We curate the best developer tools and projects.
Repository:https://github.com/orhun/git-cliff
GitHub - orhun/git-cliff: git-cliff: Changelog Generator for Git History
git-cliff is a Rust changelog generator, dual-licensed MIT or Apache-2.0, that builds changelogs from Git history using conventional commits, regex parsers and ...
github - orhun/git-cliff
Related Projects
Maintain git-cliff: Changelog Generator for Git History? Add the badge to your README
Show that the project is featured on TechLogHub and send readers to this write-up.
[](https://techloghub.com/open-source/git-cliff?utm_source=techloghub_badge&utm_medium=referral&utm_campaign=readme)
