diff --git a/commitizen/bump.py b/commitizen/bump.py index 7134ef6a6d..075f170ceb 100644 --- a/commitizen/bump.py +++ b/commitizen/bump.py @@ -101,7 +101,7 @@ def _version_to_regex(version: str) -> str: def normalize_tag( version: Version | str, - tag_format: str | None = None, + tag_format: str, scheme: VersionScheme | None = None, ) -> str: """The tag and the software version might be different. @@ -118,9 +118,6 @@ def normalize_tag( scheme = scheme or DEFAULT_SCHEME version = scheme(version) if isinstance(version, str) else version - if not tag_format: - return str(version) - major, minor, patch = version.release prerelease = version.prerelease or "" diff --git a/commitizen/commands/changelog.py b/commitizen/commands/changelog.py index e1cbcddb8f..cf667a299b 100644 --- a/commitizen/commands/changelog.py +++ b/commitizen/commands/changelog.py @@ -57,8 +57,8 @@ def __init__(self, config: BaseConfig, args): or defaults.change_type_order ) self.rev_range = args.get("rev_range") - self.tag_format = args.get("tag_format") or self.config.settings.get( - "tag_format" + self.tag_format: str = ( + args.get("tag_format") or self.config.settings["tag_format"] ) self.merge_prerelease = args.get( "merge_prerelease" diff --git a/commitizen/commands/init.py b/commitizen/commands/init.py index b9803d0713..33cd9a26f4 100644 --- a/commitizen/commands/init.py +++ b/commitizen/commands/init.py @@ -10,7 +10,7 @@ from commitizen.__version__ import __version__ from commitizen.config import BaseConfig, JsonConfig, TomlConfig, YAMLConfig from commitizen.cz import registry -from commitizen.defaults import config_files +from commitizen.defaults import DEFAULT_SETTINGS, config_files from commitizen.exceptions import InitFailedError, NoAnswersError from commitizen.git import get_latest_tag_name, get_tag_names, smart_open from commitizen.version_schemes import KNOWN_SCHEMES, Version, get_version_scheme @@ -203,14 +203,15 @@ def _ask_tag_format(self, latest_tag) -> str: f'Is "{tag_format}" the correct tag format?', style=self.cz.style ).unsafe_ask() + default_format = DEFAULT_SETTINGS["tag_format"] if not is_correct_format: tag_format = questionary.text( - 'Please enter the correct version format: (default: "$version")', + f'Please enter the correct version format: (default: "{default_format}")', style=self.cz.style, ).unsafe_ask() if not tag_format: - tag_format = "$version" + tag_format = default_format return tag_format def _ask_version_provider(self) -> str: diff --git a/commitizen/defaults.py b/commitizen/defaults.py index c3faaec8a0..62e634dbb7 100644 --- a/commitizen/defaults.py +++ b/commitizen/defaults.py @@ -39,7 +39,7 @@ class Settings(TypedDict, total=False): version_provider: str | None version_scheme: str | None version_type: str | None - tag_format: str | None + tag_format: str bump_message: str | None allow_abort: bool allowed_prefixes: list[str] @@ -73,7 +73,7 @@ class Settings(TypedDict, total=False): "version_files": [], "version_provider": "commitizen", "version_scheme": None, - "tag_format": None, # example v$version + "tag_format": "$version", # example v$version "bump_message": None, # bumped v$current_version to $new_version "allow_abort": False, "allowed_prefixes": [ diff --git a/commitizen/providers.py b/commitizen/providers.py index dbf8b9f9a2..b764e77020 100644 --- a/commitizen/providers.py +++ b/commitizen/providers.py @@ -195,9 +195,9 @@ class ScmProvider(VersionProvider): def _tag_format_matcher(self) -> Callable[[str], str | None]: version_scheme = get_version_scheme(self.config) - pattern = ( - self.config.settings.get("tag_format") or version_scheme.parser.pattern - ) + pattern = self.config.settings["tag_format"] + if pattern == "$version": + pattern = version_scheme.parser.pattern for var, tag_pattern in self.TAG_FORMAT_REGEXS.items(): pattern = pattern.replace(var, tag_pattern) diff --git a/docs/bump.md b/docs/bump.md index 5446ef3c42..287d4e1e35 100644 --- a/docs/bump.md +++ b/docs/bump.md @@ -317,7 +317,17 @@ cz -nr 21 bump ### `tag_format` -It is used to read the format from the git tags, and also to generate the tags. +`tag_format` and `version_scheme` are combined to make Git tag names from versions. + +These are used in: + +* `cz bump`: Find previous release tag (exact match) and generate new tag. +* Find previous release tags in `cz changelog`. + * If `--incremental`: Using latest version found in the changelog, scan existing Git tags with 89\% similarity match. + * `--rev-range` is converted to Git tag names with `tag_format` before searching Git history. +* If the `scm` `version_provider` is used, it uses different regexes to find the previous version tags: + * If `tag_format` is set to `$version` (default): `VersionProtocol.parser` (allows `v` prefix) + * If `tag_format` is set: Custom regex similar to SemVer (not as lenient as PEP440 e.g. on dev-releases) Commitizen supports 2 types of formats, a simple and a more complex. @@ -336,7 +346,7 @@ In your `pyproject.toml` or `.cz.toml` tag_format = "v$major.$minor.$patch$prerelease" ``` -The variables must be preceded by a `$` sign. +The variables must be preceded by a `$` sign. Default is `$version`. Supported variables: diff --git a/docs/config.md b/docs/config.md index 6f8dea47ea..fdd0ae2460 100644 --- a/docs/config.md +++ b/docs/config.md @@ -46,7 +46,7 @@ Select a version scheme from the following options [`pep440`, `semver`]. Useful Type: `str` -Default: `None` +Default: `$version` Format for the git tag, useful for old projects, that use a convention like `"v1.2.1"`. [Read more][tag_format] diff --git a/tests/test_conf.py b/tests/test_conf.py index ec9ba429fa..5c2cb6b6ff 100644 --- a/tests/test_conf.py +++ b/tests/test_conf.py @@ -46,7 +46,7 @@ "version": "1.0.0", "version_provider": "commitizen", "version_scheme": None, - "tag_format": None, + "tag_format": "$version", "bump_message": None, "allow_abort": False, "allowed_prefixes": ["Merge", "Revert", "Pull request", "fixup!", "squash!"], @@ -69,7 +69,7 @@ "version": "2.0.0", "version_provider": "commitizen", "version_scheme": None, - "tag_format": None, + "tag_format": "$version", "bump_message": None, "allow_abort": False, "allowed_prefixes": ["Merge", "Revert", "Pull request", "fixup!", "squash!"], diff --git a/tests/test_version_providers.py b/tests/test_version_providers.py index edf6e2f1cb..12948da851 100644 --- a/tests/test_version_providers.py +++ b/tests/test_version_providers.py @@ -173,11 +173,19 @@ def test_file_providers( @pytest.mark.parametrize( - "tag_format,tag,version", + "tag_format,tag,expected_version", ( - (None, "0.1.0", "0.1.0"), - (None, "v0.1.0", "0.1.0"), + # If tag_format is $version (the default), version_scheme.parser is used. + # Its DEFAULT_VERSION_PARSER allows a v prefix, but matches PEP440 otherwise. + ("$version", "no-match-because-version-scheme-is-strict", "0.0.0"), + ("$version", "0.1.0", "0.1.0"), + ("$version", "v0.1.0", "0.1.0"), + ("$version", "v-0.1.0", "0.0.0"), + # If tag_format is not None or $version, TAG_FORMAT_REGEXS are used, which are + # much more lenient but require a v prefix. ("v$version", "v0.1.0", "0.1.0"), + ("v$version", "no-match-because-no-v-prefix", "0.0.0"), + ("v$version", "v-match-TAG_FORMAT_REGEXS", "-match-TAG_FORMAT_REGEXS"), ("version-$version", "version-0.1.0", "0.1.0"), ("version-$version", "version-0.1", "0.1"), ("version-$version", "version-0.1.0rc1", "0.1.0rc1"), @@ -191,7 +199,7 @@ def test_file_providers( ) @pytest.mark.usefixtures("tmp_git_project") def test_scm_provider( - config: BaseConfig, tag_format: str | None, tag: str, version: str + config: BaseConfig, tag_format: str, tag: str, expected_version: str ): create_file_and_commit("test: fake commit") create_tag(tag) @@ -203,25 +211,13 @@ def test_scm_provider( provider = get_provider(config) assert isinstance(provider, ScmProvider) - assert provider.get_version() == version + actual_version = provider.get_version() + assert actual_version == expected_version # Should not fail on set_version() provider.set_version("43.1") -@pytest.mark.usefixtures("tmp_git_project") -def test_scm_provider_default_without_matching_tag(config: BaseConfig): - create_file_and_commit("test: fake commit") - create_tag("should-not-match") - create_file_and_commit("test: fake commit") - - config.settings["version_provider"] = "scm" - - provider = get_provider(config) - assert isinstance(provider, ScmProvider) - assert provider.get_version() == "0.0.0" - - @pytest.mark.usefixtures("tmp_git_project") def test_scm_provider_default_without_commits_and_tags(config: BaseConfig): config.settings["version_provider"] = "scm"