A Python port of commit-and-tag-version — a utility for versioning using semver and CHANGELOG generation powered by Conventional Commits.
- Follow the Conventional Commits Specification in your repository.
- When you're ready to release, run
commit-and-tag-version.
The tool will then:
- Retrieve the current version from
packageFiles, falling back to the last git tag. - Bump the version in
bumpFilesbased on your commits. - Generate a changelog based on your commits.
- Create a new commit including your
bumpFilesand updated CHANGELOG. - Create a new tag with the new version number.
| Commit Type | Version Bump |
|---|---|
fix: |
Patch (1.0.0 -> 1.0.1) |
feat: |
Minor (1.0.0 -> 1.1.0) |
feat!: or BREAKING CHANGE |
Major (1.0.0 -> 2.0.0) |
Breaking changes before v1.0.0 bump minor instead of major.
pip install commit-and-tag-versionOr with a package manager:
# pipx (recommended for CLI tools)
pipx install commit-and-tag-version
# poetry
poetry add --group dev commit-and-tag-version
# uv
uv pip install commit-and-tag-versioncommit-and-tag-version --first-releaseThis will tag a release without bumping the version.
commit-and-tag-versionAs long as your git commit messages are conventional and accurate, the version bump is automatic and you get CHANGELOG generation for free.
After you cut a release, push the new git tag:
git push --follow-tags origin main# unnamed prerelease: 1.0.1-0
commit-and-tag-version --prerelease
# named prerelease: 1.0.1-alpha.0
commit-and-tag-version --prerelease alphaPrerelease tag collisions are automatically avoided by incrementing the numeric suffix.
# force a minor release
commit-and-tag-version --release-as minor
# force a specific version
commit-and-tag-version --release-as 1.1.0You can combine --release-as and --prerelease to generate a pre-release for a specific version type.
commit-and-tag-version --dry-runSee what commands would be run, without committing to git or updating files.
commit-and-tag-version --skip-changelog
commit-and-tag-version --skip-bump --skip-tagOr via config:
{
"skip": {
"changelog": true
}
}Tags are prefixed with v by default. Customize with --tag-prefix:
commit-and-tag-version --tag-prefix "release/"commit-and-tag-version --signcommit-and-tag-version --no-verifyUse --commit-all to include all staged changes in the release commit:
commit-and-tag-version --commit-allUse --tag-force to replace an existing tag (e.g., after amending a release):
commit-and-tag-version --skip-bump --tag-forcecommit-and-tag-version has built-in updaters for reading and writing versions in:
| File | Updater |
|---|---|
package.json, bower.json, manifest.json |
JSON |
pom.xml |
Maven |
build.gradle, build.gradle.kts |
Gradle |
*.csproj |
.NET |
*.yaml, *.yml |
YAML |
openapi.yaml, openapi.yml |
OpenAPI |
pyproject.toml |
Python (Poetry) |
VERSION.txt, version.txt |
Plain text |
Point to your project's version file(s) via config:
{
"packageFiles": ["pyproject.toml"],
"bumpFiles": ["pyproject.toml"]
}Configure via any of these (highest to lowest precedence):
- CLI arguments
.versionrc/.versionrc.json(JSON)pyproject.tomlunder[tool.commit-and-tag-version]- Built-in defaults
{
"tagPrefix": "v",
"packageFiles": ["pyproject.toml"],
"bumpFiles": ["pyproject.toml"],
"types": [
{"type": "feat", "section": "Features"},
{"type": "fix", "section": "Bug Fixes"},
{"type": "refactor", "section": "Refactoring", "hidden": false}
],
"releaseCommitMessageFormat": "chore(release): {{currentTag}}"
}[tool.commit-and-tag-version]
tag-prefix = "v"
package-files = ["pyproject.toml"]
bump-files = ["pyproject.toml"]Execute custom commands at each stage of the release process:
{
"scripts": {
"prerelease": "python -m pytest",
"prebump": "echo 9.9.9",
"postbump": "echo bumped!",
"prechangelog": "",
"postchangelog": "",
"precommit": "",
"postcommit": "",
"pretag": "",
"posttag": ""
}
}prebump: If the script outputs a valid semver version, it overrides the calculated version.precommit: If the script outputs a string, it overrides the release commit message format.
Customize which commit types appear in the changelog and under what heading:
{
"types": [
{"type": "feat", "section": "Features"},
{"type": "fix", "section": "Bug Fixes"},
{"type": "chore", "hidden": true},
{"type": "docs", "hidden": true},
{"type": "style", "hidden": true},
{"type": "refactor", "section": "Refactoring", "hidden": false},
{"type": "perf", "section": "Performance", "hidden": false},
{"type": "test", "hidden": true}
]
}from commit_and_tag_version import commit_and_tag_version
from commit_and_tag_version.defaults import get_default_config
config = get_default_config()
config.package_files = ["pyproject.toml"]
config.bump_files = ["pyproject.toml"]
config.dry_run = True
commit_and_tag_version(config)Usage: commit-and-tag-version [OPTIONS]
Options:
-r, --release-as TEXT Specify release type or exact version
-p, --prerelease TEXT Make a prerelease with optional tag id
-f, --first-release Is this the first release?
-s, --sign GPG sign commits and tags
--signoff Add Signed-off-by trailer
-n, --no-verify Bypass git hooks
-a, --commit-all Commit all staged changes
--dry-run Simulate without making changes
--silent Suppress output
-t, --tag-prefix TEXT Tag prefix (default: v)
--tag-force Replace existing tag
-c, --config PATH Custom config file path
-i, --infile TEXT Changelog file path
--release-count INTEGER Number of releases in changelog
--header TEXT Custom changelog header
--release-commit-message-format TEXT
Commit message format
--commit-url-format TEXT Commit URL format template
--compare-url-format TEXT Compare URL format template
--issue-url-format TEXT Issue URL format template
--git-tag-fallback / --no-git-tag-fallback
Fallback to git tags for version
--path TEXT Only populate commits under this path
--skip-bump Skip version bump
--skip-changelog Skip changelog generation
--skip-commit Skip git commit
--skip-tag Skip git tag
--version Show the version and exit.
--help Show this message and exit.
commit-and-tag-version is branch-agnostic and local-only:
- No per-branch version strategies or branch-specific config
- No automatic pushing or publishing
- Deterministic bumps based solely on commit messages
- Prerelease is explicit via
--prerelease, not inferred from branch names - You control when and where to push
This makes it predictable across any git workflow — feat: always means minor, regardless of which branch you're on.
Yes. Despite being written in Python, commit-and-tag-version supports version files for many ecosystems (Maven, Gradle, .NET, YAML, JSON, etc.). Install it as a global CLI tool and point it at your project's version file(s).
MIT