From 0598e360a564e52e7f251ead2fa469b57f4b13b9 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Fri, 13 Feb 2026 00:56:14 +0800 Subject: [PATCH 1/4] fix: bump command called changelog command with allow_no_commit=True, but changelog command raised NoCommitsFoundError --- commitizen/commands/bump.py | 6 ++++- commitizen/commands/changelog.py | 8 ++++-- tests/commands/test_bump_command.py | 39 +++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 6084c8c15..37173f098 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -330,7 +330,11 @@ def __call__(self) -> None: changelog_cmd = Changelog( self.config, - {**changelog_args, "file_name": self.file_name}, # type: ignore[typeddict-item] + { + **changelog_args, # type: ignore[typeddict-item] + "file_name": self.file_name, + "allow_no_commit": self.arguments["allow_no_commit"], + }, ) changelog_cmd() changelog_file_name = changelog_cmd.file_name diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 5093ed9f2..f995b88b1 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -43,6 +43,7 @@ class ChangelogArgs(TypedDict, total=False): extras: dict[str, Any] export_template: str during_version_bump: bool | None + allow_no_commit: bool # --allow-no-commit is still invalid in the changelog command class Changelog: @@ -124,6 +125,7 @@ def __init__(self, config: BaseConfig, arguments: ChangelogArgs) -> None: self.export_template_to = arguments.get("export_template") self.during_version_bump: bool = arguments.get("during_version_bump") or False + self.allow_no_commit: bool = arguments.get("allow_no_commit") or False def _find_incremental_rev(self, latest_version: str, tags: Iterable[GitTag]) -> str: """Try to find the 'start_rev'. @@ -255,8 +257,10 @@ def __call__(self) -> None: changelog_meta.unreleased_end = latest_full_release_info.index + 1 commits = git.get_commits(start=start_rev, end=end_rev, args="--topo-order") - if not commits and ( - self.current_version is None or not self.current_version.is_prerelease + if ( + not self.allow_no_commit + and not commits + and (self.current_version is None or not self.current_version.is_prerelease) ): raise NoCommitsFoundError("No commits found") diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index 94b40e94e..a0c8ce5fd 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -1540,3 +1540,42 @@ def test_changelog_merge_preserves_header( out = changelog_path.read_text() file_regression.check(out, extension=".md") + + +@pytest.mark.freeze_time("2025-01-01") +def test_bump_allow_no_commit_issue( + tmp_commitizen_project_initial, + util: UtilFixture, +) -> None: + """Issue #1866: bump command called changelog command with allow_no_commit=True, but changelog command raised NoCommitsFoundError""" + tmp_commitizen_project = tmp_commitizen_project_initial(version="1.0.0") + with (tmp_commitizen_project / "pyproject.toml").open("w") as f: + f.write( + dedent( + r""" + [project] + name = "abc" + version = "4.14.0" + + [tool.commitizen] + name = "cz_customize" + tag_format = "$version" + version_scheme = "semver2" + version_provider = "pep621" + update_changelog_on_bump = true + + [tool.commitizen.customize] + bump_pattern = '^(feat|fix|ci|build|perf|refactor|chore|remove|style|test)' + bump_map = {feat = "MINOR", fix = "PATCH", ci = "PATCH", build = "PATCH", perf = "PATCH", refactor = "PATCH", chore = "PATCH", remove = "PATCH", style = "PATCH", test = "PATCH" } + schema_pattern = "(build|bump|chore|ci|dev|docs|feat|fix|perf|refactor|remove|style|test):(\\s.*)" + commit_parser = "^(?Pbuild|bump|chore|ci|dev|docs|feat|fix|perf|refactor|remove|style|test):\\s(?P.*)?" + change_type_map = {"feat" = "New Features", "fix" = "Bug Fixes", "perf" = "Performance Improvements", "refactor" = "Refactoring", "chore" = "General Improvements", "remove" = "Removed", "style" = "Stylistic Changes", "test" = "Testing", "build" = "Build"} + change_type_order = ["BREAKING CHANGE", "New Features", "Bug Fixes", "Performance Improvements", "Refactoring", "General Improvements", "Removed", "Stylistic Changes", "Testing", "Build"] + changelog_pattern = "^(build|chore|feat|fix|perf|refactor|remove|style|test)" +""" + ) + ) + util.run_cli("bump", "--yes", "--allow-no-commit", "--prerelease", "beta") + util.run_cli( + "bump", "--allow-no-commit", "--prerelease", "rc" + ) # Failed because the bump command called changelog command From 7d59bf4076839b6241765b9160cd8a12b03bd3d0 Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Wed, 15 Apr 2026 23:03:35 +0800 Subject: [PATCH 2/4] fix: clarify allow-no-commit changelog behavior and docs Align internal typing/forwarding for allow_no_commit, simplify the regression test setup, and document how changelog entries are generated during no-commit bumps. Made-with: Cursor --- commitizen/commands/bump.py | 2 +- commitizen/commands/changelog.py | 5 +++-- docs/commands/bump.md | 5 +++++ docs/commands/changelog.md | 2 ++ tests/commands/test_bump_command.py | 30 +++-------------------------- 5 files changed, 14 insertions(+), 30 deletions(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 37173f098..9e2ae81b1 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -333,7 +333,7 @@ def __call__(self) -> None: { **changelog_args, # type: ignore[typeddict-item] "file_name": self.file_name, - "allow_no_commit": self.arguments["allow_no_commit"], + "allow_no_commit": bool(self.arguments["allow_no_commit"]), }, ) changelog_cmd() diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index f995b88b1..4548588b2 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -43,7 +43,7 @@ class ChangelogArgs(TypedDict, total=False): extras: dict[str, Any] export_template: str during_version_bump: bool | None - allow_no_commit: bool # --allow-no-commit is still invalid in the changelog command + allow_no_commit: bool | None class Changelog: @@ -125,7 +125,8 @@ def __init__(self, config: BaseConfig, arguments: ChangelogArgs) -> None: self.export_template_to = arguments.get("export_template") self.during_version_bump: bool = arguments.get("during_version_bump") or False - self.allow_no_commit: bool = arguments.get("allow_no_commit") or False + # Internal flag used when changelog is invoked from `cz bump --allow-no-commit`. + self.allow_no_commit: bool = bool(arguments.get("allow_no_commit")) def _find_incremental_rev(self, latest_version: str, tags: Iterable[GitTag]) -> str: """Try to find the 'start_rev'. diff --git a/docs/commands/bump.md b/docs/commands/bump.md index cbd6d5452..06510dfef 100644 --- a/docs/commands/bump.md +++ b/docs/commands/bump.md @@ -473,6 +473,11 @@ cz bump --allow-no-commit 2.0.0 cz bump --allow-no-commit 2.0.0 ``` +!!! note "Behavior with changelog updates" + When `update_changelog_on_bump = true` (or `--changelog` is used), `cz bump --allow-no-commit` also generates a changelog entry even if there are no commits in the selected range. + + This makes the new release visible in the changelog while still showing that no commit-based changes were included. + ### `--tag-format` `tag_format` and [version_scheme][version_scheme] are combined to make Git tag names from versions. diff --git a/docs/commands/changelog.md b/docs/commands/changelog.md index da40eb6fb..2c36c0429 100644 --- a/docs/commands/changelog.md +++ b/docs/commands/changelog.md @@ -2,6 +2,8 @@ Generates a changelog following the committing rules established. +When changelog generation is triggered by `cz bump --allow-no-commit` (with `--changelog` or `update_changelog_on_bump = true`), Commitizen still creates a release entry even when no commits are found in the selected revision range. + !!! tip To create the changelog automatically on bump, add the setting [update_changelog_on_bump](../config/bump.md#update_changelog_on_bump) diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index a0c8ce5fd..a07230954 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -1548,33 +1548,9 @@ def test_bump_allow_no_commit_issue( util: UtilFixture, ) -> None: """Issue #1866: bump command called changelog command with allow_no_commit=True, but changelog command raised NoCommitsFoundError""" - tmp_commitizen_project = tmp_commitizen_project_initial(version="1.0.0") - with (tmp_commitizen_project / "pyproject.toml").open("w") as f: - f.write( - dedent( - r""" - [project] - name = "abc" - version = "4.14.0" - - [tool.commitizen] - name = "cz_customize" - tag_format = "$version" - version_scheme = "semver2" - version_provider = "pep621" - update_changelog_on_bump = true - - [tool.commitizen.customize] - bump_pattern = '^(feat|fix|ci|build|perf|refactor|chore|remove|style|test)' - bump_map = {feat = "MINOR", fix = "PATCH", ci = "PATCH", build = "PATCH", perf = "PATCH", refactor = "PATCH", chore = "PATCH", remove = "PATCH", style = "PATCH", test = "PATCH" } - schema_pattern = "(build|bump|chore|ci|dev|docs|feat|fix|perf|refactor|remove|style|test):(\\s.*)" - commit_parser = "^(?Pbuild|bump|chore|ci|dev|docs|feat|fix|perf|refactor|remove|style|test):\\s(?P.*)?" - change_type_map = {"feat" = "New Features", "fix" = "Bug Fixes", "perf" = "Performance Improvements", "refactor" = "Refactoring", "chore" = "General Improvements", "remove" = "Removed", "style" = "Stylistic Changes", "test" = "Testing", "build" = "Build"} - change_type_order = ["BREAKING CHANGE", "New Features", "Bug Fixes", "Performance Improvements", "Refactoring", "General Improvements", "Removed", "Stylistic Changes", "Testing", "Build"] - changelog_pattern = "^(build|chore|feat|fix|perf|refactor|remove|style|test)" -""" - ) - ) + tmp_commitizen_project_initial( + version="1.0.0", config_extra="update_changelog_on_bump = true\n" + ) util.run_cli("bump", "--yes", "--allow-no-commit", "--prerelease", "beta") util.run_cli( "bump", "--allow-no-commit", "--prerelease", "rc" From 820d73e8f4b8da4476d37f67031898cef8faf8cf Mon Sep 17 00:00:00 2001 From: Yu-Ting Hsiung Date: Thu, 16 Apr 2026 00:03:52 +0800 Subject: [PATCH 3/4] chore: clarify internal changelog allow_no_commit argument Document that allow_no_commit in ChangelogArgs is internal-only for bump-triggered changelog generation. Made-with: Cursor --- commitizen/commands/changelog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index 4548588b2..8cef5d39f 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -43,7 +43,7 @@ class ChangelogArgs(TypedDict, total=False): extras: dict[str, Any] export_template: str during_version_bump: bool | None - allow_no_commit: bool | None + allow_no_commit: bool | None # Internal-only when invoked by bump. class Changelog: From 72403ad191bac3d2058aa916c0ec38d8fa9b25b0 Mon Sep 17 00:00:00 2001 From: Tim Hsiung Date: Thu, 16 Apr 2026 01:01:13 +0800 Subject: [PATCH 4/4] chore: Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- tests/commands/test_bump_command.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index a07230954..86acff466 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -1554,4 +1554,4 @@ def test_bump_allow_no_commit_issue( util.run_cli("bump", "--yes", "--allow-no-commit", "--prerelease", "beta") util.run_cli( "bump", "--allow-no-commit", "--prerelease", "rc" - ) # Failed because the bump command called changelog command + ) # Should not fail when changelog generation runs with no new commits