Skip to content

Commit 1ae2d12

Browse files
committed
refactor(changelog): code cleanup
1 parent 741c1e7 commit 1ae2d12

File tree

3 files changed

+105
-64
lines changed

3 files changed

+105
-64
lines changed

commitizen/changelog.py

Lines changed: 86 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
from collections.abc import Iterable
3333
from dataclasses import dataclass
3434
from datetime import date
35-
from typing import TYPE_CHECKING
35+
from typing import TYPE_CHECKING, Any
3636

3737
from jinja2 import (
3838
BaseLoader,
@@ -74,6 +74,23 @@ def get_commit_tag(commit: GitCommit, tags: list[GitTag]) -> GitTag | None:
7474
return next((tag for tag in tags if tag.rev == commit.rev), None)
7575

7676

77+
def _get_release_info(
78+
current_tag_name: str,
79+
current_tag_date: str,
80+
changes: dict[str | None, list],
81+
changelog_release_hook: ChangelogReleaseHook | None,
82+
commit_tag: GitTag | None,
83+
) -> dict[str, Any]:
84+
release = {
85+
"version": current_tag_name,
86+
"date": current_tag_date,
87+
"changes": changes,
88+
}
89+
if changelog_release_hook:
90+
return changelog_release_hook(release, commit_tag)
91+
return release
92+
93+
7794
def generate_tree_from_commits(
7895
commits: list[GitCommit],
7996
tags: list[GitTag],
@@ -88,24 +105,24 @@ def generate_tree_from_commits(
88105
pat = re.compile(changelog_pattern)
89106
map_pat = re.compile(commit_parser, re.MULTILINE)
90107
body_map_pat = re.compile(commit_parser, re.MULTILINE | re.DOTALL)
91-
current_tag: GitTag | None = None
92108
rules = rules or TagRules()
93109

110+
used_tags: set[GitTag] = set()
111+
current_tag_name = unreleased_version or "Unreleased"
112+
current_tag_date = (
113+
date.today().isoformat() if unreleased_version is not None else ""
114+
)
115+
94116
# Check if the latest commit is not tagged
95-
if commits:
96-
latest_commit = commits[0]
97-
current_tag = get_commit_tag(latest_commit, tags)
98-
99-
current_tag_name: str = unreleased_version or "Unreleased"
100-
current_tag_date: str = ""
101-
if unreleased_version is not None:
102-
current_tag_date = date.today().isoformat()
103-
if current_tag is not None and current_tag.name:
104-
current_tag_name = current_tag.name
105-
current_tag_date = current_tag.date
106-
107-
changes: dict = defaultdict(list)
108-
used_tags: list = [current_tag]
117+
current_tag = get_commit_tag(commits[0], tags) if commits else None
118+
if current_tag is not None:
119+
used_tags.add(current_tag)
120+
if current_tag.name:
121+
current_tag_name = current_tag.name
122+
current_tag_date = current_tag.date
123+
124+
changes: defaultdict[str | None, list] = defaultdict(list)
125+
commit_tag: GitTag | None = None
109126
for commit in commits:
110127
commit_tag = get_commit_tag(commit, tags)
111128

@@ -114,21 +131,21 @@ def generate_tree_from_commits(
114131
and commit_tag not in used_tags
115132
and rules.include_in_changelog(commit_tag)
116133
):
117-
used_tags.append(commit_tag)
118-
release = {
119-
"version": current_tag_name,
120-
"date": current_tag_date,
121-
"changes": changes,
122-
}
123-
if changelog_release_hook:
124-
release = changelog_release_hook(release, commit_tag)
125-
yield release
134+
used_tags.add(commit_tag)
135+
136+
yield _get_release_info(
137+
current_tag_name,
138+
current_tag_date,
139+
changes,
140+
changelog_release_hook,
141+
commit_tag,
142+
)
143+
126144
current_tag_name = commit_tag.name
127145
current_tag_date = commit_tag.date
128146
changes = defaultdict(list)
129147

130-
matches = pat.match(commit.message)
131-
if not matches:
148+
if not pat.match(commit.message):
132149
continue
133150

134151
# Process subject from commit message
@@ -153,14 +170,13 @@ def generate_tree_from_commits(
153170
change_type_map,
154171
)
155172

156-
release = {
157-
"version": current_tag_name,
158-
"date": current_tag_date,
159-
"changes": changes,
160-
}
161-
if changelog_release_hook:
162-
release = changelog_release_hook(release, commit_tag)
163-
yield release
173+
yield _get_release_info(
174+
current_tag_name,
175+
current_tag_date,
176+
changes,
177+
changelog_release_hook,
178+
commit_tag,
179+
)
164180

165181

166182
def process_commit_message(
@@ -170,21 +186,23 @@ def process_commit_message(
170186
changes: dict[str | None, list],
171187
change_type_map: dict[str, str] | None = None,
172188
):
173-
message: dict = {
189+
message: dict[str, str | list[str] | Any] = {
174190
"sha1": commit.rev,
175191
"parents": commit.parents,
176192
"author": commit.author,
177193
"author_email": commit.author_email,
178194
**parsed.groupdict(),
179195
}
180196

181-
if processed := hook(message, commit) if hook else message:
182-
messages = [processed] if isinstance(processed, dict) else processed
183-
for msg in messages:
184-
change_type = msg.pop("change_type", None)
185-
if change_type_map:
186-
change_type = change_type_map.get(change_type, change_type)
187-
changes[change_type].append(msg)
197+
if not (processed := hook(message, commit) if hook else message):
198+
return
199+
200+
processed_messages = [processed] if isinstance(processed, dict) else processed
201+
for msg in processed_messages:
202+
change_type = msg.pop("change_type", None)
203+
if change_type_map:
204+
change_type = change_type_map.get(change_type, change_type)
205+
changes[change_type].append(msg)
188206

189207

190208
def order_changelog_tree(tree: Iterable, change_type_order: list[str]) -> Iterable:
@@ -225,8 +243,7 @@ def render_changelog(
225243
**kwargs,
226244
) -> str:
227245
jinja_template = get_changelog_template(loader, template)
228-
changelog: str = jinja_template.render(tree=tree, **kwargs)
229-
return changelog
246+
return jinja_template.render(tree=tree, **kwargs)
230247

231248

232249
def incremental_build(
@@ -253,7 +270,9 @@ def incremental_build(
253270
for index, line in enumerate(lines):
254271
if index == unreleased_start:
255272
skip = True
256-
elif index == unreleased_end:
273+
continue
274+
275+
if index == unreleased_end:
257276
skip = False
258277
if (
259278
latest_version_position is None
@@ -268,13 +287,15 @@ def incremental_build(
268287

269288
if index == latest_version_position:
270289
output_lines.extend([new_content, "\n"])
271-
272290
output_lines.append(line)
273-
if not isinstance(latest_version_position, int):
274-
if output_lines and output_lines[-1].strip():
275-
# Ensure at least one blank line between existing and new content.
276-
output_lines.append("\n")
277-
output_lines.append(new_content)
291+
292+
if isinstance(latest_version_position, int):
293+
return output_lines
294+
295+
if output_lines and output_lines[-1].strip():
296+
# Ensure at least one blank line between existing and new content.
297+
output_lines.append("\n")
298+
output_lines.append(new_content)
278299
return output_lines
279300

280301

@@ -324,8 +345,7 @@ def get_oldest_and_newest_rev(
324345
if not (newest_tag := rules.find_tag_for(tags, newest)):
325346
raise NoCommitsFoundError("Could not find a valid revision range.")
326347

327-
oldest_tag = None
328-
oldest_tag_name = None
348+
oldest_tag_name: str | None = None
329349
if oldest:
330350
if not (oldest_tag := rules.find_tag_for(tags, oldest)):
331351
raise NoCommitsFoundError("Could not find a valid revision range.")
@@ -337,17 +357,19 @@ def get_oldest_and_newest_rev(
337357
if not tags_range:
338358
raise NoCommitsFoundError("Could not find a valid revision range.")
339359

340-
oldest_rev: str | None = tags_range[-1].name
341360
newest_rev = newest_tag.name
342361

343-
# check if it's the first tag created
344-
# and it's also being requested as part of the range
345-
if oldest_rev == tags[-1].name and oldest_rev == oldest_tag_name:
346-
return None, newest_rev
347-
348-
# when they are the same, and it's also the
349-
# first tag created
350-
if oldest_rev == newest_rev:
351-
return None, newest_rev
362+
# Return None for oldest_rev if:
363+
# 1. The oldest tag is the last tag in the list and matches the requested oldest tag, or
364+
# 2. The oldest and newest tags are the same
365+
oldest_rev: str | None = (
366+
None
367+
if (
368+
tags_range[-1].name == tags[-1].name
369+
and tags_range[-1].name == oldest_tag_name
370+
or tags_range[-1].name == newest_rev
371+
)
372+
else tags_range[-1].name
373+
)
352374

353375
return oldest_rev, newest_rev

commitizen/git.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ def __eq__(self, other) -> bool:
4141
return False
4242
return self.rev == other.rev # type: ignore
4343

44+
def __hash__(self):
45+
return hash(self.rev)
46+
4447

4548
class GitCommit(GitObject):
4649
def __init__(

tests/test_changelog.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,6 +1529,22 @@ def test_get_smart_tag_range_returns_an_extra_for_a_range(tags):
15291529
assert 4 == len(res)
15301530

15311531

1532+
def test_get_smart_tag_range_returns_all_tags_for_a_range(tags):
1533+
start = tags[0]
1534+
1535+
end = tags[-1]
1536+
res = changelog.get_smart_tag_range(tags, start.name, end.name)
1537+
assert len(tags) == len(res)
1538+
1539+
end = tags[-2]
1540+
res = changelog.get_smart_tag_range(tags, start.name, end.name)
1541+
assert len(tags) == len(res)
1542+
1543+
end = tags[-3]
1544+
res = changelog.get_smart_tag_range(tags, start.name, end.name)
1545+
assert len(tags) - 1 == len(res)
1546+
1547+
15321548
def test_get_smart_tag_range_returns_an_extra_for_a_single_tag(tags):
15331549
start = tags[0] # len here is 1, but we expect one more tag as designed
15341550
res = changelog.get_smart_tag_range(tags, start.name)

0 commit comments

Comments
 (0)