From bbe6f73007832f900d94d5aa6aad5eb1c60a94b2 Mon Sep 17 00:00:00 2001 From: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com> Date: Fri, 23 Oct 2026 15:47:18 -0700 Subject: [PATCH] fix(scm-provider): prefer full version over floating tags Motivation: With tags v1.0.0, v1.0 and v1 on one commit, the scm version provider returned "1.0". The three versions compare equal, and max() kept the first one, which is the newest tag by date. The patch part was dropped from the project version. Approach: Break ties between equal versions by choosing the longest spelling, so 1.0.0 wins over 1.0 and 1. Ordering of unequal versions is unchanged. Validation: `uv run pytest -n0` passed (1331 passed, 2 skipped, 2 xfailed). The new test fails without the change and passes with it. `ruff check`, `ruff format --check` and `mypy commitizen/providers` pass. `uv run poe all` was not run in full. Impact: Only affects `version_provider = "scm"` when equal-valued tags with different lengths exist. Only the version printed or used was wrong; nothing else changed. Report: https://github.com/commitizen-tools/commitizen/issues/1863 Assisted By: AI Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com> Assisted-by: claude-sonnet-5-5 (via Claude Code) --- commitizen/providers/scm_provider.py | 8 +++++++- tests/providers/test_scm_provider.py | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/commitizen/providers/scm_provider.py b/commitizen/providers/scm_provider.py index 687775da30..e9bc20e9db 100644 --- a/commitizen/providers/scm_provider.py +++ b/commitizen/providers/scm_provider.py @@ -18,7 +18,13 @@ def get_version(self) -> str: rules = TagRules.from_settings(self.config.settings) tags = get_tags(reachable_only=True) version_tags = rules.get_version_tags(tags) - version = max((rules.extract_version(t) for t in version_tags), default=None) + # Floating tags (e.g. `v1.0` next to `v1.0.0`) compare equal. Among equal + # versions, prefer the most specific (longest) spelling. + version = max( + (rules.extract_version(t) for t in version_tags), + key=lambda v: (v, len(str(v))), + default=None, + ) return str(version) if version is not None else "0.0.0" def set_version(self, version: str) -> None: diff --git a/tests/providers/test_scm_provider.py b/tests/providers/test_scm_provider.py index d3f0299f47..ef4451fb7c 100644 --- a/tests/providers/test_scm_provider.py +++ b/tests/providers/test_scm_provider.py @@ -140,3 +140,23 @@ def test_scm_provider_detect_legacy_tags(config: BaseConfig, util: UtilFixture): util.create_file_and_commit("test: fake commit") util.create_tag("v0.5.0") assert provider.get_version() == "0.5.0" + + +@pytest.mark.usefixtures("tmp_git_project") +def test_scm_provider_prefers_full_version_over_floating_tags( + config: BaseConfig, util: UtilFixture +): + """Floating tags (`v1.0`, `v1`) must not hide the full `v1.0.0` version.""" + # Arrange + util.create_file_and_commit("feat: initial commit") + util.create_tag("v1.0.0") + util.create_tag("v1.0") + util.create_tag("v1") + config.settings["version_provider"] = "scm" + config.settings["tag_format"] = "v$version" + + # Act + provider = get_provider(config) + + # Assert + assert provider.get_version() == "1.0.0"