Title: fix(bump): skip commit step when there are no files to commit by bearomorphism · Pull Request #1969 · commitizen-tools/commitizen · GitHub
Open Graph Title: fix(bump): skip commit step when there are no files to commit by bearomorphism · Pull Request #1969 · commitizen-tools/commitizen
X Title: fix(bump): skip commit step when there are no files to commit by bearomorphism · Pull Request #1969 · commitizen-tools/commitizen
Description: Description Closes #1530. Why cz bump unconditionally calls git commit -a after updating version files, then treats any non-zero exit code as a fatal BumpCommitFailedError. When version_provider = "scm" is configured with no version_files and no changelog generation, there are no files for the bump to modify — so git exits with "nothing to commit, working tree clean" and the error is raised before any tag is ever created. Reported by @loelkes on commitizen 4.8.2 / Python 3.13 / macOS (#1530): the exact error is 2nd git.commit error: "On branch main\nnothing to commit, working tree clean\n". Maintainer @Lee-W confirmed the report ("this is a valid bug"), and @woile linked a prior fix attempt (PR #996) that was never merged. A triage note from the open-issues audit (2026-05-09) found the bug is worse on master (v4.15.1) than the original report described: even the first bump now fails, because newer commitizen versions no longer modify cz.toml during a bump. The version_provider = "scm" workflow is fully documented — the version is derived entirely from git tags and no file is ever written — yet there was no way to use it without also enabling update_changelog_on_bump as a workaround. What changed File Change commitizen/git.py Add has_pending_changes() helper after is_staging_clean (line 311): runs git status --porcelain --untracked-files=no and returns True when there are tracked-file changes that git commit -a would commit commitizen/commands/bump.py Gate the git.commit + retry block (lines 383–399) behind if git.has_pending_changes(): …; emit an out.info message and skip straight to tag creation when the tree is clean tests/commands/test_bump_command.py Add test_bump_skips_commit_when_no_files_changed: two consecutive cz bump --yes calls in a version_provider = "scm" repo both succeed and produce the expected v0.1.0 / v0.1.1 tags How it works git.has_pending_changes() (new function, commitizen/git.py after line 311) runs git status --porcelain --untracked-files=no. The --untracked-files=no flag restricts the query to tracked files — exactly the set that git commit -a would stage and commit. The function returns True if there is any non-empty output, False if the working tree is clean for tracked files. Why not reuse the existing git.is_staging_clean() (commitizen/git.py:308–311)? is_staging_clean runs git diff --cached --name-only, which checks only the index (already-staged changes). git commit -a also commits unstaged modifications to tracked files — for example a file reformatted by a pre-commit hook that wasn't re-staged. git status --porcelain --untracked-files=no covers both cases in a single call, making has_pending_changes the correct guard for git commit -a. Why not treat a non-zero exit from git commit as non-fatal? The existing error path also surfaces genuine commit failures — bad GPG key, locked .git/index, pre-commit hook rejection — as non-zero exit codes. Silently swallowing those would mask real problems. Checking before attempting the commit avoids the ambiguity entirely. The retry path is preserved: the pre-commit-reformatter retry (self.retry and c.return_code != 0 and self.changelog_flag) is moved inside the else branch and continues to work exactly as before — it only runs when there was something to commit and the first attempt failed. When skipping the commit, out.info("No file changes; skipping bump commit and tagging HEAD.") is emitted so the user can see why no bump commit appears in git log. Backward compatibility Repos that have version_files or update_changelog_on_bump = true always have pending changes after the update step; has_pending_changes() returns True and the commit path runs exactly as before. The retry-on-reformat path (self.retry) is preserved intact inside the else branch. Pre-commit hook integration, GPG signing (gpg_sign), and annotated-tag options are unaffected — those code paths all execute after the commit block. git.is_staging_clean() is untouched; nothing that calls it is changed. All pre-existing tests/commands/test_bump_command.py tests pass (132 tests; 4 GPG-fixture tests deselected on Windows — pre-existing, unrelated). Checklist I have read the contributing guidelines Was generative AI tooling used to co-author this PR? Yes (please specify the tool below) Generated-by: Claude following the guidelines Code Changes Add test cases to all the changes you introduce Run uv run poe all locally to ensure this change passes linter check and tests Manually test the changes (see "Steps to Test" below) Update the documentation for the changes Expected Behavior Scenario Outcome version_provider = "scm", no version_files, no changelog — first cz bump Tag v0.1.0 created on HEAD; no bump commit; exit 0 Same config — second cz bump after another conventional commit Tag v0.1.1 created; no error; exit 0 version_provider = "pep621" with version_files configured — cz bump Behaviour unchanged: version file updated → bump commit created → tag applied version_provider = "scm" with update_changelog_on_bump = true — cz bump Behaviour unchanged: changelog written → has_pending_changes() returns True → bump commit created → tag applied Steps to Test This Pull Request git fetch fork fix/1530-skip-commit-when-clean git checkout fork/fix/1530-skip-commit-when-clean # 1. Targeted regression test. uv run pytest tests/commands/test_bump_command.py::test_bump_skips_commit_when_no_files_changed -v # 2. Reproduce the bug, then verify the fix. mkdir /tmp/cz-1530 && cd /tmp/cz-1530 git init -b main git config user.name test && git config user.email test@example.com printf '[tool.commitizen]\nversion_provider = "scm"\ntag_format = "v$version"\n' > cz.toml git add cz.toml && git commit -m "feat: setup" # First bump — previously failed with BumpCommitFailedError on master. cz bump --yes # Expected: "No file changes; skipping bump commit and tagging HEAD." + exit 0 git tag --list # should show v0.1.0 # Second bump — the scenario from the original report. touch foobar && git add foobar && git commit -m "feat: foobar" cz bump --yes # Expected: exit 0, no "nothing to commit" error git tag --list # should show v0.1.0 and v0.2.0 Additional Context This fix was identified during the open-issues audit tracked in #1964. A triage note (@bearomorphism, 2026-05-09) confirmed the bug reproduces on master (v4.15.1) — and is worse than the original report, failing on the very first bump — and suggested checking is_staging_clean() before the commit. The implementation instead introduces a dedicated git.has_pending_changes() helper using git status --porcelain --untracked-files=no, which correctly covers both staged and unstaged modifications to tracked files (the full set that git commit -a acts on); see the "How it works" section for why the existing is_staging_clean() was insufficient. A prior fix attempt existed as PR #996 but was never merged.
Open Graph Description: Description Closes #1530. Why cz bump unconditionally calls git commit -a after updating version files, then treats any non-zero exit code as a fatal BumpCommitFailedError. When version_provider = ...
X Description: Description Closes #1530. Why cz bump unconditionally calls git commit -a after updating version files, then treats any non-zero exit code as a fatal BumpCommitFailedError. When version_provider = ...
Opengraph URL: https://github.com/commitizen-tools/commitizen/pull/1969
X: @github
Domain: github.com
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:5dc1e812-6478-9a6e-cbbb-1763849ab9a6 |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | D652:33E8BE:C2949:11E9C7:6A4DFAB7 |
| html-safe-nonce | 794340b00b85478fa2bb7618d0e30e560823e15d68a74f417370510075e0c51b |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJENjUyOjMzRThCRTpDMjk0OToxMUU5Qzc6NkE0REZBQjciLCJ2aXNpdG9yX2lkIjoiMzMxODUzNzg1MjM1MDYxNDMxIiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 6894a053ec6a4c572d7e543af238c0444676e1ae81edd396f9e74e28437bbf6b |
| hovercard-subject-tag | pull_request:3654688835 |
| github-keyboard-shortcuts | repository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/commitizen-tools/commitizen/pull/1969/files |
| twitter:image | https://avatars.githubusercontent.com/u/26526132?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/26526132?s=400&v=4 |
| og:image:alt | Description Closes #1530. Why cz bump unconditionally calls git commit -a after updating version files, then treats any non-zero exit code as a fatal BumpCommitFailedError. When version_provider = ... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | 5818716c93c6a2925b815402541a32814e43a7b1261c322b0c2df75224289566 |
| turbo-cache-control | no-preview |
| diff-view | unified |
| go-import | github.com/commitizen-tools/commitizen git https://github.com/commitizen-tools/commitizen.git |
| octolytics-dimension-user_id | 62252524 |
| octolytics-dimension-user_login | commitizen-tools |
| octolytics-dimension-repository_id | 106127589 |
| octolytics-dimension-repository_nwo | commitizen-tools/commitizen |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 106127589 |
| octolytics-dimension-repository_network_root_nwo | commitizen-tools/commitizen |
| turbo-body-classes | logged-out env-production page-responsive full-width |
| disable-turbo | true |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 4314b1df11fa8a565684f3a72dc971e3785da365 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width