fix: 修复 tag/changelog 生成范围,增加 --auto 模式
- fix(get_tags): 使用 find_object+peel_to_commit 支持 annotated tag - fix(changelog): insert_changelog_entry 替换覆盖逻辑,保留已有章节 - feat: parse_changelog_versions 按 semver 降序提取版本 - feat: sort_tags_by_semver 取代纯时间排序 - feat(tag --auto): 优先读 Cargo.toml/pyproject.toml,回退 commit 分析 - feat(changelog): 无 --from 时自动检测 changelog 最高已有版本 - refactor: TagInfo::version_name(), GitRepo::find_tag_by_version()
This commit is contained in:
@@ -7,6 +7,7 @@ use std::path::PathBuf;
|
||||
|
||||
use crate::config::{Language, manager::ConfigManager};
|
||||
use crate::generator::ContentGenerator;
|
||||
use crate::git::GitRepo;
|
||||
use crate::git::find_repo;
|
||||
use crate::git::{CommitInfo, changelog::*};
|
||||
use crate::i18n::{Messages, translate_changelog_category};
|
||||
@@ -120,7 +121,10 @@ impl ChangelogCommand {
|
||||
|
||||
// Get commits
|
||||
println!("{}", messages.fetching_commits());
|
||||
let commits = generate_from_history(&repo, self.from.as_deref(), Some(&self.to))?;
|
||||
|
||||
// Determine from_tag: use explicit --from, or auto-detect from changelog
|
||||
let from_tag = self.resolve_from_tag(&repo, &output_path, &messages);
|
||||
let commits = generate_from_history(&repo, from_tag.as_deref(), Some(&self.to))?;
|
||||
|
||||
if commits.is_empty() {
|
||||
bail!("{}", messages.no_commits_found());
|
||||
@@ -167,33 +171,13 @@ impl ChangelogCommand {
|
||||
}
|
||||
}
|
||||
|
||||
// Write to file (always prepend to preserve history)
|
||||
// Write to file (always prepend new entry before existing versions)
|
||||
if output_path.exists() {
|
||||
let existing = std::fs::read_to_string(&output_path)?;
|
||||
let new_content = if existing.is_empty() {
|
||||
format!("{}{}", CHANGELOG_HEADER, changelog)
|
||||
} else if existing.starts_with(CHANGELOG_HEADER) {
|
||||
format!("{}{}", CHANGELOG_HEADER, changelog)
|
||||
} else if existing.starts_with("# Changelog") {
|
||||
let lines: Vec<&str> = existing.lines().collect();
|
||||
let mut header_end = 0;
|
||||
|
||||
for (i, line) in lines.iter().enumerate() {
|
||||
if i == 0 && line.starts_with('#') {
|
||||
header_end = i + 1;
|
||||
} else if line.trim().is_empty() {
|
||||
header_end = i + 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let header = lines[..header_end].join("\n");
|
||||
let rest = lines[header_end..].join("\n");
|
||||
|
||||
format!("{}\n{}\n{}", header, changelog, rest)
|
||||
} else {
|
||||
format!("{}{}", CHANGELOG_HEADER, changelog)
|
||||
insert_changelog_entry(&existing, &changelog)
|
||||
};
|
||||
std::fs::write(&output_path, new_content)?;
|
||||
} else {
|
||||
@@ -206,6 +190,28 @@ impl ChangelogCommand {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve_from_tag(
|
||||
&self,
|
||||
repo: &GitRepo,
|
||||
output_path: &PathBuf,
|
||||
messages: &Messages,
|
||||
) -> Option<String> {
|
||||
// Explicit --from always wins
|
||||
if self.from.is_some() {
|
||||
return self.from.clone();
|
||||
}
|
||||
|
||||
// Auto-detect: find highest version already in changelog
|
||||
let existing = std::fs::read_to_string(output_path).ok()?;
|
||||
let versions = parse_changelog_versions(&existing);
|
||||
let highest = versions.first()?;
|
||||
|
||||
// Match highest version to a git tag
|
||||
let tag = repo.find_tag_by_version(highest)?;
|
||||
println!(" {}: {}", messages.version(), tag.name);
|
||||
Some(tag.name)
|
||||
}
|
||||
|
||||
async fn generate_with_ai(
|
||||
&self,
|
||||
version: &str,
|
||||
|
||||
Reference in New Issue
Block a user