git-cliff
git-cliff is a highly customizable changelog generator built in Rust with a convenient Python interface. It processes Git history to produce detailed, human-readable release notes based on conventional commits. Currently at version 2.12.0, the library maintains an active development pace with frequent updates and new features.
Common errors
-
ModuleNotFoundError: No module named 'git_cliff'
cause The `git-cliff` Python package has not been installed in the current Python environment.fixRun `pip install git-cliff` to install the package. -
Error: failed to find a Git repository (or similar message from git-cliff)
cause The `git-cliff` command or `generate_changelog` function was executed in a directory that is not part of a Git repository, or outside the root of a repository.fixEnsure your current working directory is inside a Git repository, or explicitly provide the `repository` argument to `generate_changelog` pointing to the Git repository's path. -
Error: Failed to parse configuration file: ... (e.g., invalid TOML syntax or missing required sections)
cause The `.git-cliff.toml` (or `cliff.toml`) file contains syntax errors, is malformed, or is missing critical sections required by `git-cliff`.fixCarefully review your configuration file for TOML syntax errors (e.g., unmatched quotes, invalid keys, incorrect section headers). Refer to the official `git-cliff` documentation for example configurations. -
Error: template parsing failed: ... (followed by details related to Tera/Jinja2 syntax)
cause The `changelog.body` template in your `.git-cliff.toml` file contains syntax errors specific to the Tera templating language (which is similar to Jinja2).fixInspect the `body` section of your configuration file for common templating mistakes such as unmatched curly braces `{{ }}`, incorrect filter usage `|`, or invalid control flow statements `{% %}`. Consult Tera documentation or `git-cliff`'s template examples.
Warnings
- breaking Starting with `v2.0.0`, `git-cliff` officially transitioned its default configuration filename from `cliff.toml` to `.git-cliff.toml`. Existing configurations using the old filename may not be automatically discovered or used.
- gotcha `git-cliff` relies heavily on Git tags for version detection and changelog segmentation. If your repository lacks consistent tags (e.g., `v1.0.0`), the generated changelog might be incomplete, misleading, or include all history under an 'Unreleased' section.
- gotcha The Python interface is a direct binding to the underlying Rust core. This means that detailed error messages, especially those related to configuration file parsing or template rendering (which uses Tera, a Jinja2-like engine), often originate from the Rust side and might require familiarity with `git-cliff`'s CLI output for full interpretation.
Install
-
pip install git-cliff
Imports
- generate_changelog
from git_cliff import generate_changelog
Quickstart
import os
import tempfile
import shutil
import subprocess
from git_cliff import generate_changelog
# Create a temporary directory to simulate a project environment
temp_dir = tempfile.mkdtemp()
original_cwd = os.getcwd()
os.chdir(temp_dir)
try:
# Initialize a dummy Git repository
subprocess.run(["git", "init", "-b", "main"], check=True, capture_output=True)
subprocess.run(["git", "config", "user.name", "Test User"], check=True, capture_output=True)
subprocess.run(["git", "config", "user.email", "test@example.com"], check=True, capture_output=True)
# Create some dummy commits with conventional commit messages
with open("file1.txt", "w") as f:
f.write("initial content")
subprocess.run(["git", "add", "."], check=True, capture_output=True)
subprocess.run(["git", "commit", "-m", "feat: initial setup of the project"], check=True, capture_output=True)
with open("file2.txt", "w") as f:
f.write("more content")
subprocess.run(["git", "add", "."], check=True, capture_output=True)
subprocess.run(["git", "commit", "-m", "fix: resolve minor typo in file2"], check=True, capture_output=True)
subprocess.run(["git", "tag", "v1.0.0"], check=True, capture_output=True)
with open("file3.txt", "w") as f:
f.write("new feature")
subprocess.run(["git", "add", "."], check=True, capture_output=True)
subprocess.run(["git", "commit", "-m", "feat: implement new awesome feature"], check=True, capture_output=True)
# Create a minimal .git-cliff.toml config file for predictable output
cliff_config_content = r"""
[changelog]
body = """
{{% if version %}}
## {{ version }} - {{ 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 }} ([{{ commit.id | truncate(length=7, end="") }}]({{ commit.id }}))
{{% endfor %}}
{{% endfor %}}
"""
[git]
filter_commits = true
conventional_commits = true
[git.mapper]
feat = "Features"
fix = "Bug Fixes"
"""
with open(".git-cliff.toml", "w") as f:
f.write(cliff_config_content)
# Generate the changelog using the Python interface
# It will automatically discover the .git-cliff.toml config and the git repo
changelog_output = generate_changelog(
config_path=".git-cliff.toml" # Specify the config file explicitly
)
print("\n--- Generated Changelog ---\n")
print(changelog_output)
finally:
# Clean up the temporary directory
os.chdir(original_cwd)
shutil.rmtree(temp_dir)