Skip to main content
The [changelog] section controls how the changelog is generated, including header, body, and footer templates, as well as postprocessing and output options.

Configuration Options

header
string
Header template rendered at the beginning of the changelog.The template context contains the full list of releases in the releases variable. See templating for more details.Example:
[changelog]
header = """
# Changelog
All notable changes to this project will be documented in this file.
"""
body
string
required
Body template rendered for each release in the changelog.If the changelog contains 3 releases, this template is rendered 3 times, once for each release. The template context contains one release in the release variable.Example:
[changelog]
body = """
{% for group, commits in commits | group_by(attribute="group") %}
    ### {{ group | upper_first }}
    {% for commit in commits %}
        - {{ commit.message | upper_first }}
    {% endfor %}
{% endfor %}
"""
Footer template rendered at the end of the changelog.The template context contains the full list of releases in the releases variable.Example:
[changelog]
footer = "<!-- generated by git-cliff -->"
trim
boolean
default:"false"
Remove leading and trailing whitespace from the body template.This is useful for adding indentation to templates for readability while keeping the output clean.Example:
[changelog]
trim = true
render_always
boolean
default:"false"
Render the changelog body even if there are no releases to process.Example:
[changelog]
render_always = false
postprocessors
array
An array of regex-based postprocessors to manipulate the changelog before outputting.Each postprocessor has a pattern (regex) and either replace (string) or replace_command (shell command).Examples:
[changelog]
postprocessors = [
  # Replace repository placeholder with URL
  { pattern = '<REPO>', replace = "https://github.com/orhun/git-cliff" },
  
  # Replace issue numbers with links
  { pattern = '#(\\d+)', replace = "[#$1](https://github.com/orhun/git-cliff/issues/$1)" },
  
  # Use command to process text
  { pattern = '.*', replace_command = 'pandoc -t commonmark' },
]
output
string
Output file path for the changelog.You can also use the --output argument to override this value.Example:
[changelog]
output = "CHANGELOG.md"

Complete Example

Here’s a comprehensive changelog configuration:
[changelog]
header = """
[![animation](https://raw.githubusercontent.com/orhun/git-cliff/main/website/static/img/git-cliff-anim.gif)](https://git-cliff.org)

"""

body = """
{%- macro remote_url() -%}
  https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }}
{%- endmacro -%}

{% macro print_commit(commit) -%}
    - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
        {% if commit.breaking %}[**breaking**] {% endif %}\
        {{ commit.message | upper_first }} - \
        ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\
{% endmacro -%}

{% if version %}\
    {% if previous.version %}\
        ## [{{ version | trim_start_matches(pat="v") }}]\
          ({{ self::remote_url() }}/compare/{{ previous.version }}..{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }}
    {% else %}\
        ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
    {% endif %}\
{% else %}\
    ## [unreleased]
{% endif %}\

{% for group, commits in commits | group_by(attribute="group") %}
    ### {{ group | striptags | trim | upper_first }}
    {% for commit in commits
    | filter(attribute="scope")
    | sort(attribute="scope") %}
        {{ self::print_commit(commit=commit) }}
    {%- endfor %}
    {% for commit in commits %}
        {%- if not commit.scope -%}
            {{ self::print_commit(commit=commit) }}
        {% endif -%}
    {% endfor -%}
{% endfor -%}
"""

footer = """
<!-- generated by git-cliff -->
"""

trim = true

postprocessors = [
  { pattern = '<REPO>', replace = "https://github.com/orhun/git-cliff" },
]

output = "CHANGELOG.md"

Template Variables

The following variables are available in changelog templates:
  • version - The current release version
  • commits - Array of commits in the release
  • commit_id - Git commit SHA
  • timestamp - Release timestamp
  • previous - Information about the previous release
  • releases - All releases (in header/footer)
  • github - GitHub integration data (if enabled)
  • remote - Remote configuration
See templating documentation for complete details.

Common Patterns

GitHub Release Style

[changelog]
body = """
## What's Changed
{%- if version %} in {{ version | trim_start_matches(pat="v") }}{%- endif -%}
{% for commit in commits %}
  * {{ commit.message | split(pat="\\n") | first | trim }}\
    {% if commit.remote.username %} by @{{ commit.remote.username }}{%- endif -%}
{%- endfor -%}
"""

Keep a Changelog Format

[changelog]
header = """
# Changelog
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
"""

body = """
{% if version %}\
    ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
{% else %}\
    ## [Unreleased]
{% endif %}\
{% for group, commits in commits | group_by(attribute="group") %}
    ### {{ group | upper_first }}
    {% for commit in commits %}
        - {{ commit.message | upper_first }}
    {% endfor %}
{% endfor %}
"""

See Also

Build docs developers (and LLMs) love