You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Tag not set correctly using devreleases with semver2 and custom tag_format #1615
When using a custom tag format like ${major}.${minor}-${patch}${devrelease} with semver2 the created tags miss the devrelease part. Further devreleases fail due to duplicate tags.
Steps to reproduce
Initialize git project and place cz configuration:
git init .
cat << EOF | tee cz.toml
[tool.commitizen]
name = "cz_conventional_commits"
tag_format = "\${major}.\${minor}-\${patch}\${devrelease}"
version_scheme = "semver2"
version = "0.0.1"
update_changelog_on_bump = true
EOF
Observe incorrect "tag to create" in output tag (note ${devrelease})
bump: version 0.0.1 → 0.0.2-dev.1
tag to create: 0.0-2${devrelease}
increment detected: PATCH
Observe inccorect git tag
git tag -l
Trying to add more changes and bump the next devrelease will fail
Current behavior
No devrelease part is added according to the configuration. The tag equals ${major}.${minor}-${patch} which produces further errors using a next devrelease.
Desired behavior
The tag shall be set correctly according to custom tag_format and semver2 convention like 0.0-2dev1
Screenshots
n/a
Environment
Commitizen Version: 4.9.1
Python Version: 3.9.21 (main, Aug 19 2025, 00:00:00)
[GCC 11.5.0 20240719 (Red Hat 11.5.0-5)]
Operating System: Linux
changed the title [-]Tag not set correctly using semver2 and custom tag_format[/-][+]Tag not set correctly using devreleases with semver2 and custom tag_format[/+]on Sep 18, 2025
Triage from #1964: Still valid. commitizen/tags.py::normalize_tag (lines 195-223) only substitutes version, major, minor, patch, and prerelease — there is no devrelease substitution. So a tag_format referencing ${devrelease} (e.g. ${major}.${minor}-${patch}${devrelease}) leaves that placeholder un-substituted in the rendered tag. Fix: add devrelease=str(getattr(version, 'devrelease', '') or '') to safe_substitute in normalize_tag.
Config: tag_format = "${major}.${minor}-${patch}${devrelease}", version_scheme = "semver2", version = "0.0.1".
After cz bump --devrelease 1 --yes:
tag to create: 0.0-2${devrelease}
git tag --list:
0.0-2${devrelease}
The literal ${devrelease} placeholder is not substituted, and a tag with ${devrelease} in its name actually gets created.
Verdict: STILL VALID.
Confirmed root cause in commitizen/tags.py::normalize_tag (lines 195-223): the safe_substitute call only passes version, major, minor, patch, and prerelease — devrelease is missing.
Note: while testing this, I also reproduced #1614 which has a related malformed-tag issue with ${prerelease}. Both should be fixed together — they share the same normalize_tag template-substitution path.
@saisharan0103 Thanks for checking! I haven't checked whether my old AI generated PR above works and recently I don't have bandwidth on it. You can fork the PR in case it helps.
Activity
[-]Tag not set correctly using semver2 and custom tag_format[/-][+]Tag not set correctly using devreleases with semver2 and custom tag_format[/+]bearomorphism commentedon Sep 19, 2025
May be related to #950
bearomorphism commentedon May 9, 2026
Triage from #1964: Still valid.
commitizen/tags.py::normalize_tag(lines 195-223) only substitutesversion,major,minor,patch, andprerelease— there is nodevreleasesubstitution. So atag_formatreferencing${devrelease}(e.g.${major}.${minor}-${patch}${devrelease}) leaves that placeholder un-substituted in the rendered tag. Fix: adddevrelease=str(getattr(version, 'devrelease', '') or '')tosafe_substituteinnormalize_tag.bearomorphism commentedon May 9, 2026
Verification update (re #1964)
Reproduced against current master (4.15.1):
Config:
tag_format = "${major}.${minor}-${patch}${devrelease}",version_scheme = "semver2",version = "0.0.1".After
cz bump --devrelease 1 --yes:git tag --list:The literal
${devrelease}placeholder is not substituted, and a tag with${devrelease}in its name actually gets created.Verdict: STILL VALID.
Confirmed root cause in
commitizen/tags.py::normalize_tag(lines 195-223): thesafe_substitutecall only passesversion,major,minor,patch, andprerelease—devreleaseis missing.Patch:
Note: while testing this, I also reproduced #1614 which has a related malformed-tag issue with
${prerelease}. Both should be fixed together — they share the samenormalize_tagtemplate-substitution path.fix(tags): widen prerelease and devrelease tag regexes for SemVer2 (#…
bearomorphism commentedon Jul 14, 2026
@saisharan0103 Thanks for checking! I haven't checked whether my old AI generated PR above works and recently I don't have bandwidth on it. You can fork the PR in case it helps.