orhun/git-cliff
A highly customizable Changelog Generator that follows Conventional Commit specifications ⛰️
Healthy across the board
weakest axisPermissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 6d ago
- ✓19 active contributors
- ✓Apache-2.0 licensed
Show all 6 evidence items →Show less
- ✓CI configured
- ✓Tests present
- ⚠Concentrated ownership — top contributor handles 50% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/orhun/git-cliff)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/orhun/git-cliff on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: orhun/git-cliff
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/orhun/git-cliff shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 6d ago
- 19 active contributors
- Apache-2.0 licensed
- CI configured
- Tests present
- ⚠ Concentrated ownership — top contributor handles 50% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live orhun/git-cliff
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/orhun/git-cliff.
What it runs against: a local clone of orhun/git-cliff — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in orhun/git-cliff | Confirms the artifact applies here, not a fork |
| 2 | License is still Apache-2.0 | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 36 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of orhun/git-cliff. If you don't
# have one yet, run these first:
#
# git clone https://github.com/orhun/git-cliff.git
# cd git-cliff
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of orhun/git-cliff and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "orhun/git-cliff(\\.git)?\\b" \\
&& ok "origin remote is orhun/git-cliff" \\
|| miss "origin remote is not orhun/git-cliff (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(Apache-2\\.0)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"Apache-2\\.0\"" package.json 2>/dev/null) \\
&& ok "license is Apache-2.0" \\
|| miss "license drift — was Apache-2.0 at generation time"
# 3. Default branch
git rev-parse --verify main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "src/main.rs" \\
&& ok "src/main.rs" \\
|| miss "missing critical file: src/main.rs"
test -f "src/lib.rs" \\
&& ok "src/lib.rs" \\
|| miss "missing critical file: src/lib.rs"
test -f "src/parser.rs" \\
&& ok "src/parser.rs" \\
|| miss "missing critical file: src/parser.rs"
test -f "Cargo.toml" \\
&& ok "Cargo.toml" \\
|| miss "missing critical file: Cargo.toml"
test -f ".github/config.yml" \\
&& ok ".github/config.yml" \\
|| miss "missing critical file: .github/config.yml"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 36 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~6d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/orhun/git-cliff"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
git-cliff is a Rust-based changelog generator that parses Git history and converts it into formatted changelogs using Conventional Commits and regex-powered custom parsers. It uses Tera templating to render highly customizable markdown changelogs from commit metadata, supporting integrations with GitHub, GitLab, Azure DevOps, and Bitbucket. Dual-layer architecture: git-cliff-core library (Rust, handles parsing and templating) exposed via docs.rs, wrapped by a CLI binary in the main crate. Configuration through cliff.toml files (TOML format) with [package.metadata.git-cliff] sections for Cargo-integrated projects. Fixture-driven testing pattern in .github/fixtures/ where each test has cliff.toml, commit.sh, and expected.md.
👥Who it's for
Rust and polyglot developers maintaining open-source or closed-source projects who need automated, compliant changelog generation without writing boilerplate. Project maintainers who follow Conventional Commits and want to avoid manual changelog management.
🌱Maturity & risk
Production-ready: the project has comprehensive CI/CD (ci.yml, cd.yml, docker.yml workflows), extensive fixture-based integration tests in .github/fixtures/, and well-structured Rust with 448k lines of code. Active maintenance evident from versioned releases and codecov integration. Clear documentation site (git-cliff.org) indicates stable, community-focused development.
Low risk: single maintainer (orhun) but strong test coverage via 50+ fixture test cases in .github/fixtures/. Rust's type system mitigates runtime breakage. Main risk is that Conventional Commits enforcement may conflict with teams using other commit styles, and template customization via Tera has learning curve. No high-volume dependency concerns visible in file list.
Active areas of work
Active fixture-based development visible in .github/fixtures/ with tests for feature variants (test-always-render, test-azure-devops-integration-custom-range, test-bump-initial-tag variants, etc.). GitHub Actions automation for CI, CD, and Docker builds. Custom GitHub actions in .github/actions/run-fixtures-test/ indicate focus on integration test execution.
🚀Get running
Clone: git clone https://github.com/orhun/git-cliff.git && cd git-cliff. Install Rust (rustup). Build: cargo build --release. Test fixtures: cargo test or use the custom GitHub action. Binary: ./target/release/git-cliff --help.
Daily commands:
Dev/test: cargo build && cargo test. Generate changelog for this repo: cargo run -- --repository . --output CHANGELOG.md. Customize: edit or create cliff.toml with [git] and [changelog] sections. Docker: docker run orhunp/git-cliff (from Docker Hub).
🗺️Map of the codebase
src/main.rs— Entry point for the git-cliff CLI application; defines command structure and argument parsingsrc/lib.rs— Core library interface exposing the changelog generation API used by CLI and integrationssrc/parser.rs— Parses conventional commits from git history into structured data; foundation for filtering and groupingCargo.toml— Project manifest defining dependencies (git2, tera, regex, serde) critical to changelog generation.github/config.yml— Configuration template and defaults for changelog generation; widely referenced by documentation.github/fixtures/test-bump-version/cliff.toml— Canonical example configuration file demonstrating bump strategy and Tera template usage patterns
🧩Components & responsibilities
- Git History Engine (git2-rs) (libgit2, Rust bindings) — Retrieves commit objects, parses raw messages, handles ref resolution and tag lookups
- Failure mode: Invalid repo path → error; no tags → empty changelog; malformed refs → graceful fallback to HEAD
- Commit Parser (src/parser.rs) (Regex, optional preprocessors) — Extracts conventional commit structure (type, scope, description, footers) from raw messages
- Failure mode: Non-compliant message → treated as 'other' group; preprocessor fails → original message used
- Config Loader (cliff.toml) (Serde, TOML) — Parses TOML configuration; defines grouping rules, Tera templates, bump strategy
- Failure mode: Missing config → uses defaults; invalid TOML → clear parse error; missing template → uses built-in fallback
- Template Renderer (Tera) (Tera, Filters (date, group_by, upper_first, etc.)) — Renders Markdown changelog from commit groups and version metadata using Jinja2-style templates
- Failure mode: Invalid template syntax → render error with line number; missing variable → empty string or error depending on settings
- Version Bumper (semver) (SemVer parsing, custom bump rules) — Calculates next semantic version based on commit type distribution and current version
- Failure mode: Invalid current version → fails with error; ambiguous bump rules → uses most significant bump
🔀Data flow
Git repository→Git History Engine— Raw commit objects and ref metadata retrieved via libgit2Git History Engine→Commit Parser— Raw commit messages and metadata (hash, author, date)Commit Parser→Core Library (lib.rs)— Structured CommitGroup objects with type, scope, message, footersConfig file (cliff.toml)→Core Library (lib.rs)— Grouping rules, Tera templates, bump strategy, commit filters
🛠️How to make changes
Add a New Commit Group (e.g., 'Security' or 'Performance')
- Update the conventional commit parser in src/parser.rs to recognize the new scope/type (
src/parser.rs) - Add the group definition to the cliff.toml config under [changelog] section with grouping rules (
.github/config.yml) - Update the Tera template body in cliff.toml to render the new group in the changelog (
.github/config.yml) - Create a new fixture test under .github/fixtures/test-<group-name>/ with commit.sh, cliff.toml, and expected.md (
.github/fixtures/new-fixture-template/commit.sh)
Add Integration Support (e.g., GitLab, GitHub Enterprise)
- Add integration-specific link generation logic to src/lib.rs or a new integration module (
src/lib.rs) - Define integration config in cliff.toml under [git] section with remote_url patterns (
.github/config.yml) - Create fixture test at .github/fixtures/test-<integration>-integration/ with expected.md showing link format (
.github/fixtures/test-azure-devops-integration/expected.md)
Add a New Configuration Option or Feature Flag
- Add the field to the Config struct in src/lib.rs with serde derive attributes (
src/lib.rs) - Parse the option in cliff.toml under the appropriate [section] and provide defaults (
.github/config.yml) - Implement feature logic in the relevant processing function (parser.rs, lib.rs) (
src/parser.rs) - Create a fixture test at .github/fixtures/test-<feature-name>/ to validate the new behavior (
.github/fixtures/new-fixture-template/cliff.toml)
🔧Why these technologies
- Rust (systems language) — Provides performance and safety for git operations; compiles to single binary for distribution; memory-safe concurrent parsing
- git2-rs (libgit2 bindings) — Direct access to git repository metadata without spawning external processes; efficient commit history traversal
- Tera (Jinja2-like templating) — Flexible, composable changelog formatting; allows users to customize output without code changes
- Serde (serialization) — Declarative TOML/JSON configuration parsing with strong type safety
- Regex — Powerful conventional commit pattern matching and commit message preprocessing
⚖️Trade-offs already made
-
Template-driven changelog generation (Tera)
- Why: Allows non-technical users to customize output without recompilation
- Consequence: Adds template parsing overhead; users must learn Tera syntax for advanced use cases
-
Git history parsing (full repo scan by default)
- Why: Provides accurate, deterministic changelog from source of truth
- Consequence: Performance degrades with very large repos (1000+ commits); requires local git clone
-
Single-binary distribution model
- Why: Easy deployment and no runtime dependency management
- Consequence: Binary size larger than interpreted language; must recompile for each platform/architecture
-
Conventional Commits as primary input format
- Why: Industry standard, machine-parseable, enables automatic categorization
- Consequence: Requires team discipline to follow spec; non-compliant commits must be preprocessed
🚫Non-goals (don't propose these)
- Real-time changelog generation from commit hooks (batch-oriented only)
- Authentication/permission management for private repos (delegates to git client)
- Changelog hosting or web UI (generates markdown files only)
- Support for non-git version control systems
- Interactive commit cherry-picking or history rewriting (read-only)
🪤Traps & gotchas
- Fixture-based testing is mandatory: Tests don't use standard Rust unit test assertions; instead, commit.sh creates commits and expected.md is diffed against output. Must understand shell scripting in fixtures. 2. Tera template errors are silent: Template syntax errors in cliff.toml can produce partial output without obvious error messages; test with
--verboseflag. 3. Git history dependency: Tests modify .git/; ensure working directory is clean before running tests or use Docker. 4. Conventional Commit parsing is strict by default: Non-standard commit formats are silently ignored unless custom regex parsers are added in cliff.toml.
🏗️Architecture
💡Concepts to learn
- Conventional Commits — git-cliff's core input specification; understanding the feat: / fix: / BREAKING CHANGE format is required to write effective commit_parsers in cliff.toml
- Tera Template Engine — Powers the header/body/footer rendering in cliff.toml; you must learn Tera syntax (filters like |upper_first, |trim_start_matches, |group_by) to customize changelog format
- Regex-based Commit Parsing — git-cliff uses regex (not AST parsing) to extract group, scope, and message fields from commit messages; understanding regex syntax is required to add custom parsers beyond Conventional Commits
- Semantic Versioning — The --bump flag in git-cliff increments versions (major.minor.patch) based on commit type; understanding semver rules helps users and contributors predict version bumping behavior
- Git Object Graph Traversal — git-cliff walks git history (commits between tags/ranges) to build changelog; understanding git's DAG model and tag semantics helps debug filtering and range issues
- Fixture-Driven Integration Testing — The test pattern used across .github/fixtures/ (shell commit creation + expected output diffing) is non-standard for Rust; requires understanding both shell scripts and markdown diffing logic
- Platform Integration Links (GitHub/GitLab/Azure/Bitbucket) — git-cliff renders commit hashes and PR/issue numbers as clickable links; understanding platform-specific URL schemes (github.com/owner/repo/commit/hash vs gitlab.com syntax) is needed to extend integrations
🔗Related repos
release-it/release-it— JavaScript-based release automation that also generates changelogs; similar problem space but uses conventional-changelog plugin instead of native parsingconventional-changelog/conventional-changelog— The JavaScript changelog generation standard that git-cliff reimplements in Rust with added customization; direct inspiration and reference for Conventional Commits parsingkeats/tera— The Tera template engine (Jinja2-like) used by git-cliff for changelog rendering; understanding Tera syntax is essential for customizationgoogle/gitmoji-cli— Alternative commit convention using emojis; represents the ecosystem of commit formatting tools that git-cliff parsers can integrate with via regex
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add integration test fixture for GitHub Release integration
The .github/fixtures directory contains comprehensive test cases for Azure DevOps, Bitbucket, and Gitea integrations, but GitHub Release integration is missing. Given that git-cliff is hosted on GitHub and GitHub Releases are the most common integration point, a dedicated fixture would ensure this critical integration path is well-tested and documented.
- [ ] Create .github/fixtures/test-github-release-integration/ directory
- [ ] Add cliff.toml with GitHub Release configuration and link template
- [ ] Add commit.sh with sample conventional commits
- [ ] Add expected.md with properly formatted GitHub Release links
- [ ] Follow the pattern used in test-bitbucket-integration/ and test-azure-devops-integration/
- [ ] Document the fixture in .github/fixtures/README.md
Add fixture for monorepo/scoped changelog generation
The commit_parsers in the config support scope filtering (e.g., scope = "cli"), but there's no test fixture demonstrating multi-scope changelog generation for monorepos. This would validate that commits with different scopes generate properly grouped sections and help users understand scope-based changelog organization.
- [ ] Create .github/fixtures/test-monorepo-scoped-changelog/ directory
- [ ] Add cliff.toml with commit_parsers for multiple scopes (e.g., 'core', 'cli', 'web')
- [ ] Add commit.sh creating commits with conventional format including scopes: feat(core):, feat(cli):, fix(web):, etc.
- [ ] Add expected.md showing each scope as a separate changelog section
- [ ] Validate the fixture runs successfully with the test action
Add fixtures for edge cases in version bumping logic
While test-bump-version-* fixtures exist, there are no fixtures for edge cases like: bumping from v0.0.x to v0.1.0, handling pre-release versions (1.0.0-alpha), or bumping when commits contain both breaking changes and regular features. These scenarios are common in real projects and warrant explicit test coverage.
- [ ] Create .github/fixtures/test-bump-version-prerelease/ for handling pre-release versions
- [ ] Create .github/fixtures/test-bump-version-zero-to-minor/ for v0.0.x to v0.1.0 transitions
- [ ] Create .github/fixtures/test-bump-version-breaking-and-features/ combining BREAKING CHANGE with feat commits
- [ ] Each fixture should include cliff.toml, commit.sh with appropriate conventional commits, and expected.md
- [ ] Ensure all fixtures pass the run-fixtures-test GitHub Action
🌿Good first issues
- Add missing integration test fixture for Forgejo/Gitea (follow .github/fixtures/test-github-integration/ pattern with Forgejo-specific API URLs in cliff.toml) — would expand platform support and help non-GitHub users
- Document regex parser examples in .github/fixtures/ with a dedicated fixture test-custom-regex-parsers showing practical commit_parsers for projects not using Conventional Commits — would help adoption by non-standard projects
- Add type-safe Config struct tests in src/ to validate all cliff.toml [package.metadata.git-cliff.*] options; currently validation is implicit in fixture tests but no unit test covers malformed configs explicitly
⭐Top contributors
Click to expand
Top contributors
- @dependabot[bot] — 50 commits
- @ognis1205 — 20 commits
- @orhun — 12 commits
- @sermuns — 2 commits
- @tessus — 2 commits
📝Recent commits
Click to expand
Recent commits
5d3a670— chore(release): prepare for v2.13.1 (orhun)b99a232— fix(cd): prepare embedded assets before PyPI and crates.io packaging (orhun)d235492— chore(release): prepare for v2.13.0 (orhun)3a6195f— docs(website): add highlights for 2.13.0 (#1491) (orhun)9b5e732— fix(cd): publish musl wheels to PyPI by matching matrix.build.NAME (#1490) (truffle-dev)b71d250— feat(tracing): migrate logging to tracing (#1488) (orhun)9d43a15— feat(context): add per-commit statistics (#1487) (WaterWhisperer)ead5587— feat(git): add configurable commit processing order (#1485) (orhun)3e97e75— feat(context): expose determined bump type in release context (#1483) (ChihebBENCHEIKH1)bd074d9— perf(ci): add caching where applicable (#1486) (sermuns)
🔒Security observations
The git-cliff repository demonstrates generally good security practices with pinned Docker base images and a clear security reporting policy. However, there are documentation gaps (truncated SECURITY.md and Dockerfile) that should be completed. The codebase uses template-based changelog generation which requires careful input validation. No critical vulnerabilities were identified in the provided structure, but documentation completeness and regular dependency updates are recommended. The project has a structured security reporting mechanism in place for versions 1.x.x and 2.x.x.
- Medium · Incomplete Dockerfile Security Documentation —
Dockerfile (runner stage). The Dockerfile snippet is truncated mid-line in the runner stage setup. The line 'so we can si' is incomplete, which may indicate missing security configurations or documentation about volume mounting and user permissions. Fix: Complete the Dockerfile documentation and ensure proper security practices are documented, such as: running as non-root user, proper file permissions, and explicit volume mount guidelines. - Low · Security Policy Incomplete —
SECURITY.md. The SECURITY.md file is truncated at the 'Confidentiality' section, with incomplete text: 'We kindly ask you to keep'. Critical security reporting guidelines may be missing or incomplete. Fix: Complete the security policy document with full confidentiality and disclosure timeline guidelines to ensure reporters understand expectations. - Low · Fixed Docker Base Image Digest —
Dockerfile. While the use of pinned Docker base image digests (lukemathwalker/cargo-chef and debian:bookworm-slim) is a positive security practice, ensure these digests are regularly reviewed and updated to include the latest security patches. Fix: Establish a process for regularly reviewing and updating pinned base image digests to incorporate security updates. Consider using automated dependency scanning tools. - Low · Potential Template Injection via Tera Templates —
Cargo.toml metadata (git-cliff.changelog body template). The changelog generation uses Tera templating engine to render user-provided commit data and configuration. While this is the intended design, improper sanitization of commit messages in templates could potentially lead to template injection if custom parsers accept untrusted input. Fix: Ensure all commit message data is properly sanitized before being passed to Tera templates. Document template safety and validate all user-provided commit parser inputs.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.