Skip to content

Commit 2d193a5

Browse files
committed
✨ feat(changelog): add incremental changelog generation && support appending to existing file
1 parent f6509e3 commit 2d193a5

File tree

1 file changed

+33
-12
lines changed

1 file changed

+33
-12
lines changed

tgit/changelog.py

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import contextlib
12
import logging
23
import re
34
import warnings
@@ -231,27 +232,46 @@ def get_hash_link(commit: TGITCommit) -> str:
231232
return out_str
232233

233234

235+
def extract_latest_tag_from_changelog(filepath: str) -> str | None:
236+
filepath = Path(filepath)
237+
with contextlib.suppress(FileNotFoundError), filepath.open(encoding="utf-8") as f:
238+
for line in f:
239+
if line.startswith("## "):
240+
return line.strip().removeprefix("## ").strip()
241+
return None
242+
243+
234244
def handle_changelog(args: ChangelogArgs) -> None:
235245
repo = git.Repo(args.path)
236-
237246
from_raw = args.from_raw
238247
to_raw = args.to_raw
239248

240-
# 如果未指定 from/to,聚合所有 tag 版本的 changelog(不再包含 HEAD,仅以最后一个 tag 结尾)
249+
# 检查是否需要增量追加
250+
latest_tag_in_file = None
251+
if args.output and Path(args.output).exists() and from_raw is None and to_raw is None:
252+
latest_tag_in_file = extract_latest_tag_from_changelog(args.output)
253+
254+
# 默认:统计所有 tag 之间的差分(不包含最新 tag 到 HEAD)
241255
if from_raw is None and to_raw is None:
242256
tags = sorted(repo.tags, key=lambda t: t.commit.committed_datetime)
257+
if not tags:
258+
print("[yellow]No tags found in the repository.[/yellow]")
259+
return
243260
first_commit = get_first_commit_hash(repo)
244261
points = [first_commit] + [tag.commit.hexsha for tag in tags]
245262
point_names = [first_commit] + [tag.name for tag in tags]
246263
changelogs = ""
264+
start_idx = 1
265+
if latest_tag_in_file and latest_tag_in_file in point_names:
266+
idx = point_names.index(latest_tag_in_file)
267+
start_idx = idx + 1
247268
with Progress() as progress:
248-
task = progress.add_task("Generating changelog...", total=len(points) - 1)
249-
# 反向遍历区间,实现反向输出
250-
for i in reversed(range(len(points) - 1)):
251-
from_hash = points[i]
252-
to_hash = points[i + 1]
253-
from_name = point_names[i]
254-
to_name = point_names[i + 1]
269+
task = progress.add_task("Generating changelog...", total=len(points) - start_idx)
270+
for i in reversed(range(start_idx, len(points))):
271+
from_hash = points[i - 1]
272+
to_hash = points[i]
273+
from_name = point_names[i - 1]
274+
to_name = point_names[i]
255275
raw_commits = list(repo.iter_commits(f"{from_hash}...{to_hash}"))
256276
tgit_commits = []
257277
for commit in raw_commits:
@@ -267,8 +287,9 @@ def handle_changelog(args: ChangelogArgs) -> None:
267287
changelog = generate_changelog(commits_by_type, from_name, to_name, remote_uri)
268288
changelogs += changelog
269289
progress.update(task, advance=1)
270-
if args.output:
271-
with Path(args.output).open("w") as output_file:
290+
if changelogs:
291+
mode = "a" if latest_tag_in_file else "w"
292+
with Path(args.output).open(mode, encoding="utf-8") as output_file:
272293
output_file.write(changelogs.strip("\n") + "\n")
273294
print()
274295
print(changelogs.strip("\n"))
@@ -278,7 +299,7 @@ def handle_changelog(args: ChangelogArgs) -> None:
278299
from_ref, to_ref = get_git_commits_range(repo, from_raw, to_raw)
279300
changelog = get_changelog_by_range(repo, from_ref, to_ref)
280301
if args.output:
281-
with Path(args.output).open("w") as output_file:
302+
with Path(args.output).open("w", encoding="utf-8") as output_file:
282303
output_file.write(changelog.strip("\n") + "\n")
283304
else:
284305
print()

0 commit comments

Comments
 (0)