Skip to content

Commit 2f05414

Browse files
committed
refactor(changelog): code cleanup
1 parent 7efa40a commit 2f05414

File tree

3 files changed

+103
-62
lines changed

3 files changed

+103
-62
lines changed

commitizen/changelog.py

Lines changed: 84 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -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(
@@ -178,13 +194,15 @@ def process_commit_message(
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 generate_ordered_changelog_tree(
@@ -228,8 +246,7 @@ def render_changelog(
228246
**kwargs: Any,
229247
) -> str:
230248
jinja_template = get_changelog_template(loader, template)
231-
changelog: str = jinja_template.render(tree=tree, **kwargs)
232-
return changelog
249+
return jinja_template.render(tree=tree, **kwargs)
233250

234251

235252
def incremental_build(
@@ -256,7 +273,9 @@ def incremental_build(
256273
for index, line in enumerate(lines):
257274
if index == unreleased_start:
258275
skip = True
259-
elif index == unreleased_end:
276+
continue
277+
278+
if index == unreleased_end:
260279
skip = False
261280
if (
262281
latest_version_position is None
@@ -271,13 +290,15 @@ def incremental_build(
271290

272291
if index == latest_version_position:
273292
output_lines.extend([new_content, "\n"])
274-
275293
output_lines.append(line)
276-
if not isinstance(latest_version_position, int):
277-
if output_lines and output_lines[-1].strip():
278-
# Ensure at least one blank line between existing and new content.
279-
output_lines.append("\n")
280-
output_lines.append(new_content)
294+
295+
if isinstance(latest_version_position, int):
296+
return output_lines
297+
298+
if output_lines and output_lines[-1].strip():
299+
# Ensure at least one blank line between existing and new content.
300+
output_lines.append("\n")
301+
output_lines.append(new_content)
281302
return output_lines
282303

283304

@@ -327,8 +348,7 @@ def get_oldest_and_newest_rev(
327348
if not (newest_tag := rules.find_tag_for(tags, newest)):
328349
raise NoCommitsFoundError("Could not find a valid revision range.")
329350

330-
oldest_tag = None
331-
oldest_tag_name = None
351+
oldest_tag_name: str | None = None
332352
if oldest:
333353
if not (oldest_tag := rules.find_tag_for(tags, oldest)):
334354
raise NoCommitsFoundError("Could not find a valid revision range.")
@@ -340,17 +360,19 @@ def get_oldest_and_newest_rev(
340360
if not tags_range:
341361
raise NoCommitsFoundError("Could not find a valid revision range.")
342362

343-
oldest_rev: str | None = tags_range[-1].name
344363
newest_rev = newest_tag.name
345364

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

356378
return oldest_rev, newest_rev

commitizen/git.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ class GitObject:
4949
def __eq__(self, other: object) -> bool:
5050
return hasattr(other, "rev") and self.rev == other.rev
5151

52+
def __hash__(self) -> int:
53+
return hash(self.rev)
54+
5255

5356
class GitCommit(GitObject):
5457
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)