2727
2828import os
2929import re
30+ import sys
31+ import typing
3032from collections import OrderedDict , defaultdict
3133from datetime import date
32- from typing import Callable , Dict , Iterable , List , Optional , Tuple
34+ from typing import Callable , Dict , Iterable , List , Optional , Tuple , Type
3335
3436from jinja2 import Environment , PackageLoader
37+ from packaging .version import InvalidVersion , Version
3538
3639from commitizen import defaults
3740from commitizen .bump import normalize_tag
3841from commitizen .exceptions import InvalidConfigurationError , NoCommitsFoundError
3942from commitizen .git import GitCommit , GitTag
4043
44+ if sys .version_info >= (3 , 8 ):
45+ from commitizen .version_types import VersionProtocol
46+ else :
47+ # workaround mypy issue for 3.7 python
48+ VersionProtocol = typing .Any
49+
4150
4251def get_commit_tag (commit : GitCommit , tags : List [GitTag ]) -> Optional [GitTag ]:
4352 return next ((tag for tag in tags if tag .rev == commit .rev ), None )
4453
4554
55+ def get_version (tag : GitTag ) -> Optional [Version ]:
56+ version = None
57+ try :
58+ version = Version (tag .name )
59+ except InvalidVersion :
60+ pass
61+ return version
62+
63+
64+ def tag_included_in_changelog (
65+ tag : GitTag , used_tags : List , merge_prerelease : bool
66+ ) -> bool :
67+ if tag in used_tags :
68+ return False
69+
70+ version = get_version (tag )
71+ if version is None :
72+ return False
73+
74+ if merge_prerelease and version .is_prerelease :
75+ return False
76+
77+ return True
78+
79+
4680def generate_tree_from_commits (
4781 commits : List [GitCommit ],
4882 tags : List [GitTag ],
@@ -51,6 +85,7 @@ def generate_tree_from_commits(
5185 unreleased_version : Optional [str ] = None ,
5286 change_type_map : Optional [Dict [str , str ]] = None ,
5387 changelog_message_builder_hook : Optional [Callable ] = None ,
88+ merge_prerelease : bool = False ,
5489) -> Iterable [Dict ]:
5590 pat = re .compile (changelog_pattern )
5691 map_pat = re .compile (commit_parser , re .MULTILINE )
@@ -73,15 +108,15 @@ def generate_tree_from_commits(
73108 for commit in commits :
74109 commit_tag = get_commit_tag (commit , tags )
75110
76- if commit_tag is not None and commit_tag not in used_tags :
111+ if commit_tag is not None and tag_included_in_changelog (
112+ commit_tag , used_tags , merge_prerelease
113+ ):
77114 used_tags .append (commit_tag )
78115 yield {
79116 "version" : current_tag_name ,
80117 "date" : current_tag_date ,
81118 "changes" : changes ,
82119 }
83- # TODO: Check if tag matches the version pattern, otherwise skip it.
84- # This in order to prevent tags that are not versions.
85120 current_tag_name = commit_tag .name
86121 current_tag_date = commit_tag .date
87122 changes = defaultdict (list )
@@ -286,7 +321,10 @@ def get_smart_tag_range(
286321
287322
288323def get_oldest_and_newest_rev (
289- tags : List [GitTag ], version : str , tag_format : str
324+ tags : List [GitTag ],
325+ version : str ,
326+ tag_format : str ,
327+ version_type_cls : Optional [Type [VersionProtocol ]] = None ,
290328) -> Tuple [Optional [str ], Optional [str ]]:
291329 """Find the tags for the given version.
292330
@@ -301,11 +339,15 @@ def get_oldest_and_newest_rev(
301339 except ValueError :
302340 newest = version
303341
304- newest_tag = normalize_tag (newest , tag_format = tag_format )
342+ newest_tag = normalize_tag (
343+ newest , tag_format = tag_format , version_type_cls = version_type_cls
344+ )
305345
306346 oldest_tag = None
307347 if oldest :
308- oldest_tag = normalize_tag (oldest , tag_format = tag_format )
348+ oldest_tag = normalize_tag (
349+ oldest , tag_format = tag_format , version_type_cls = version_type_cls
350+ )
309351
310352 tags_range = get_smart_tag_range (tags , newest = newest_tag , oldest = oldest_tag )
311353 if not tags_range :
0 commit comments