fix(bump): skip commit step when there are no files to commit#1969
Open
bearomorphism wants to merge 3 commits intocommitizen-tools:masterfrom
Open
fix(bump): skip commit step when there are no files to commit#1969bearomorphism wants to merge 3 commits intocommitizen-tools:masterfrom
bearomorphism wants to merge 3 commits intocommitizen-tools:masterfrom
Conversation
When using `version_provider = "scm"` with no `version_files` and no `--changelog` / `update_changelog_on_bump`, the bump has nothing to commit. `cz bump` would then call `git commit -a`, which exits with `nothing to commit, working tree clean` and the bump fails with `BumpCommitFailedError`. Add a `git.has_pending_changes()` helper that returns `True` only if `git commit -a` would actually commit something, and use it to decide whether to issue the bump commit. When there is nothing to commit, the tag is created on `HEAD` directly. This is the typical flow for SCM- driven projects that derive the version from the latest tag. Closes commitizen-tools#1530 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1969 +/- ##
=======================================
Coverage 98.23% 98.24%
=======================================
Files 61 61
Lines 2779 2790 +11
=======================================
+ Hits 2730 2741 +11
Misses 49 49 ☔ View full report in Codecov by Sentry. |
15 tasks
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes cz bump failing in SCM-based versioning setups where a bump produces no file changes (no version files and no changelog), by skipping the bump commit step when there is nothing to commit and proceeding directly to tagging.
Changes:
- Add
git.has_pending_changes()to detect tracked-file changes thatgit commit -awould commit. - Gate the bump commit + retry logic behind a “pending changes” check and skip straight to tagging when clean.
- Add a regression test covering two consecutive bumps in a
version_provider = "scm"repo.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
commitizen/git.py |
Adds a helper to detect pending tracked changes before attempting git commit -a. |
commitizen/commands/bump.py |
Skips the bump commit when there are no pending changes and continues to tag creation. |
tests/commands/test_bump_command.py |
Adds regression coverage for SCM provider bumps with no file modifications. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
* skip git.add when updated_files is empty * remove now-misleading FIXME on has_pending_changes guard * use consistent double-backtick reST formatting in has_pending_changes docstring Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The git.add return-code check added in the previous reviewer-feedback commit was not exercised by tests. This adds a regression test that mocks git.add returning non-zero and asserts BumpCommitFailedError is raised with a useful message. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Closes #1530.
Why
cz bumpunconditionally callsgit commit -aafter updating version files, then treats any non-zero exit code as a fatalBumpCommitFailedError. Whenversion_provider = "scm"is configured with noversion_filesand 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 modifycz.tomlduring 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 enablingupdate_changelog_on_bumpas a workaround.What changed
commitizen/git.pyhas_pending_changes()helper afteris_staging_clean(line 311): runsgit status --porcelain --untracked-files=noand returnsTruewhen there are tracked-file changes thatgit commit -awould commitcommitizen/commands/bump.pygit.commit+ retry block (lines 383–399) behindif git.has_pending_changes(): …; emit anout.infomessage and skip straight to tag creation when the tree is cleantests/commands/test_bump_command.pytest_bump_skips_commit_when_no_files_changed: two consecutivecz bump --yescalls in aversion_provider = "scm"repo both succeed and produce the expectedv0.1.0/v0.1.1tagsHow it works
git.has_pending_changes()(new function,commitizen/git.pyafter line 311) runsgit status --porcelain --untracked-files=no. The--untracked-files=noflag restricts the query to tracked files — exactly the set thatgit commit -awould stage and commit. The function returnsTrueif there is any non-empty output,Falseif the working tree is clean for tracked files.git.is_staging_clean()(commitizen/git.py:308–311)?is_staging_cleanrunsgit diff --cached --name-only, which checks only the index (already-staged changes).git commit -aalso 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=nocovers both cases in a single call, makinghas_pending_changesthe correct guard forgit commit -a.git commitas 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.self.retry and c.return_code != 0 and self.changelog_flag) is moved inside theelsebranch and continues to work exactly as before — it only runs when there was something to commit and the first attempt failed.out.info("No file changes; skipping bump commit and tagging HEAD.")is emitted so the user can see why no bump commit appears ingit log.Backward compatibility
version_filesorupdate_changelog_on_bump = truealways have pending changes after the update step;has_pending_changes()returnsTrueand the commit path runs exactly as before.self.retry) is preserved intact inside theelsebranch.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.tests/commands/test_bump_command.pytests pass (132 tests; 4 GPG-fixture tests deselected on Windows — pre-existing, unrelated).Checklist
Was generative AI tooling used to co-author this PR?
Generated-by: Claude following the guidelines
Code Changes
uv run poe alllocally to ensure this change passes linter check and testsExpected Behavior
version_provider = "scm", noversion_files, no changelog — firstcz bumpv0.1.0created onHEAD; no bump commit; exit 0cz bumpafter another conventional commitv0.1.1created; no error; exit 0version_provider = "pep621"withversion_filesconfigured —cz bumpversion_provider = "scm"withupdate_changelog_on_bump = true—cz bumphas_pending_changes()returnsTrue→ bump commit created → tag appliedSteps to Test This Pull Request
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 dedicatedgit.has_pending_changes()helper usinggit status --porcelain --untracked-files=no, which correctly covers both staged and unstaged modifications to tracked files (the full set thatgit commit -aacts on); see the "How it works" section for why the existingis_staging_clean()was insufficient. A prior fix attempt existed as PR #996 but was never merged.