From 44c271049d3c1057d13af2cee9dc257fd83b5bb6 Mon Sep 17 00:00:00 2001 From: Ekin Deveci Date: Sun, 4 Oct 2026 18:19:49 -0300 Subject: [PATCH 1/4] feat(bump): add retry_bump_after_failure config option --- commitizen/commands/bump.py | 5 ++++- commitizen/defaults.py | 1 + tests/commands/test_bump_command.py | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index 3e92775e2..a4fca6a25 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -102,7 +102,10 @@ def __init__(self, config: BaseConfig, arguments: BumpArgs) -> None: self.git_output_to_stderr = arguments["git_output_to_stderr"] self.no_verify = arguments["no_verify"] self.check_consistency = arguments["check_consistency"] - self.retry = arguments["retry"] + # retry if --retry is passed or retry_bump_after_failure is set in confic + self.retry = arguments["retry"] or self.config.settings.get( + "retry_bump_after_failure", False + ) self.pre_bump_hooks = self.config.settings["pre_bump_hooks"] self.post_bump_hooks = self.config.settings["post_bump_hooks"] deprecated_version_type = arguments.get("version_type") diff --git a/commitizen/defaults.py b/commitizen/defaults.py index 40dcee562..68339c2f3 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -55,6 +55,7 @@ class Settings(TypedDict, total=False): pre_bump_hooks: list[str] | None prerelease_offset: int retry_after_failure: bool + retry_bump_after_failure: bool # same as retry_after_failure, but for cz bump style: list[tuple[str, str]] tag_format: str template: str | None diff --git a/tests/commands/test_bump_command.py b/tests/commands/test_bump_command.py index 2e3921dea..c4f608528 100644 --- a/tests/commands/test_bump_command.py +++ b/tests/commands/test_bump_command.py @@ -1708,3 +1708,22 @@ def test_bump_allow_no_commit_issue( util.run_cli( "bump", "--allow-no-commit", "--prerelease", "rc" ) # Should not fail when changelog generation runs with no new commits + + +def test_bump_retry_bump_after_failure_config( + mocker: MockFixture, util: UtilFixture, pyproject: Path +): + """'cz bump' retries a failed commit when retry_bump_after_failure is true""" + with pyproject.open("a", encoding="utf-8") as f: + f.write("retry_bump_after_failure = true\n") + util.create_file_and_commit("feat: new file") + + # first commit fails (like when a pre-commit hook edits a file), second works + failed = cmd.Command("", "hook failed", b"", b"", 1) + passed = cmd.Command("", "", b"", b"", 0) + commit_mock = mocker.patch.object(git, "commit", side_effect=[failed, passed]) + + # bump only retries the commit when it also updates the changelog + util.run_cli("bump", "--changelog", "--yes") + + assert commit_mock.call_count == 2 From d90ad42f9720744175f6ebb8bf884e842ecd8f8d Mon Sep 17 00:00:00 2001 From: Ekin Deveci Date: Sun, 4 Oct 2026 18:54:14 -0300 Subject: [PATCH 2/4] style(bump): fix typo in comment --- commitizen/commands/bump.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/commitizen/commands/bump.py b/commitizen/commands/bump.py index a4fca6a25..7c576e4f5 100644 --- a/commitizen/commands/bump.py +++ b/commitizen/commands/bump.py @@ -102,7 +102,7 @@ def __init__(self, config: BaseConfig, arguments: BumpArgs) -> None: self.git_output_to_stderr = arguments["git_output_to_stderr"] self.no_verify = arguments["no_verify"] self.check_consistency = arguments["check_consistency"] - # retry if --retry is passed or retry_bump_after_failure is set in confic + # retry if --retry is passed or retry_bump_after_failure is set in config self.retry = arguments["retry"] or self.config.settings.get( "retry_bump_after_failure", False ) From f04aecf8afc556431108e3ca091b0a14740f61ee Mon Sep 17 00:00:00 2001 From: Ekin Deveci Date: Mon, 5 Oct 2026 16:07:25 -0300 Subject: [PATCH 3/4] feat(bump): add retry_bump_after_failure default and docs --- commitizen/defaults.py | 1 + docs/config/bump.md | 11 +++++++++++ docs/config/configuration_file.md | 2 +- tests/test_conf.py | 2 ++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/commitizen/defaults.py b/commitizen/defaults.py index 68339c2f3..b64a6ce42 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -91,6 +91,7 @@ class Settings(TypedDict, total=False): "ignored_tag_formats": [], "bump_message": None, # bumped v$current_version to $new_version "retry_after_failure": False, + "retry_bump_after_failure": False, "allow_abort": False, "allowed_prefixes": [ "Merge", diff --git a/docs/config/bump.md b/docs/config/bump.md index 7263f8066..e84b41d16 100644 --- a/docs/config/bump.md +++ b/docs/config/bump.md @@ -126,6 +126,17 @@ prerelease_offset = 1 !!! note Under some circumstances, a prerelease cannot start with `0`-for example, in embedded projects where individual characters are encoded as bytes. You can specify an offset from which to start counting. +## `retry_bump_after_failure` + +when set to `true`, `cz bump` is equivalent to `cz bump --retry`. If the bump commit fails the first time (for example because a pre-commit hook reformatted a file), it is retried once. The retry only happens when the changelog is also updated (`--changelog` or `update_changelog_on_bump = true`). + +If not specified, defaults to `false`. + +```toml title="pyproject.toml" +[tool.commitizen] +retry_bump_after_failure = true +``` + ## `tag_format` See [`--tag-format`](../commands/bump.md#-tag-format). diff --git a/docs/config/configuration_file.md b/docs/config/configuration_file.md index 172cbce1a..2174b32d2 100644 --- a/docs/config/configuration_file.md +++ b/docs/config/configuration_file.md @@ -235,7 +235,7 @@ Key configuration categories include: - **Version Management**: `version`, `version_provider`, `version_scheme`, `version_files` - **Tagging**: `tag_format`, `legacy_tag_formats`, `ignored_tag_formats`, `gpg_sign`, `annotated_tag` - **Changelog**: `changelog_file`, `changelog_format`, `changelog_incremental`, `update_changelog_on_bump` -- **Bumping**: `bump_message`, `major_version_zero`, `prerelease_offset`, `pre_bump_hooks`, `post_bump_hooks` +- **Bumping**: `bump_message`, `major_version_zero`, `prerelease_offset`, `pre_bump_hooks`, `post_bump_hooks`, `retry_bump_after_failure` - **Commit Validation**: `allowed_prefixes`, `message_length_limit`, `allow_abort`, `retry_after_failure` - **Customization**: `customize`, `style`, `use_shortcuts`, `template`, `extras` diff --git a/tests/test_conf.py b/tests/test_conf.py index 15be0630a..0bf433bee 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -85,6 +85,7 @@ "ignored_tag_formats": [], "bump_message": None, "retry_after_failure": False, + "retry_bump_after_failure": False, "allow_abort": False, "allowed_prefixes": [ "Merge", @@ -126,6 +127,7 @@ "ignored_tag_formats": [], "bump_message": None, "retry_after_failure": False, + "retry_bump_after_failure": False, "allow_abort": False, "allowed_prefixes": [ "Merge", From f3343256f34edb8e48c29483d82795399a5f800f Mon Sep 17 00:00:00 2001 From: EkinDeveci1907 Date: Thu, 8 Oct 2026 15:27:54 -0300 Subject: [PATCH 4/4] docs(bump): capitalize retry_bump_after_failure description Co-authored-by: Tim Hsiung --- docs/config/bump.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config/bump.md b/docs/config/bump.md index e84b41d16..642ee367d 100644 --- a/docs/config/bump.md +++ b/docs/config/bump.md @@ -128,7 +128,7 @@ prerelease_offset = 1 ## `retry_bump_after_failure` -when set to `true`, `cz bump` is equivalent to `cz bump --retry`. If the bump commit fails the first time (for example because a pre-commit hook reformatted a file), it is retried once. The retry only happens when the changelog is also updated (`--changelog` or `update_changelog_on_bump = true`). +When set to `true`, `cz bump` is equivalent to `cz bump --retry`. If the bump commit fails the first time (for example because a pre-commit hook reformatted a file), it is retried once. The retry only happens when the changelog is also updated (`--changelog` or `update_changelog_on_bump = true`). If not specified, defaults to `false`.