From ad055bdfd6d612e477a1fedfcfd988b306a0c4f7 Mon Sep 17 00:00:00 2001 From: zhangkun Date: Fri, 16 Jan 2026 15:35:03 +0800 Subject: [PATCH] feat: preserve Change-Id in amend mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Added extract_change_id function to parse Change-Id from existing commit messages 2. Added append_change_id function to add Change-Id to new commit messages when not present 3. Modified build_prompt to accept original commit message parameter for reference in amend mode 4. Updated generate_commit_message to extract original Change-Id in amend mode and preserve it in new commit message 5. Added original commit message to AI prompt for better context in amend mode 6. Added comprehensive unit tests for Change-Id extraction and appending functions Log: Improved commit message generation in amend mode to preserve Change-Id and reference original message Influence: 1. Test amend mode with existing Change-Id to ensure it's preserved 2. Test amend mode without Change-Id to ensure no extra Change-Id is added 3. Verify AI generates improved commit messages while referencing original content 4. Test Change-Id extraction with various message formats 5. Verify Change-Id appending logic handles edge cases properly 6. Test commit message generation with and without other metadata tags feat: 在 amend 模式下保留 Change-Id 1. 添加 extract_change_id 函数从现有提交消息中解析 Change-Id 2. 添加 append_change_id 函数在提交消息不存在 Change-Id 时添加 3. 修改 build_prompt 函数以接受原始提交消息参数,用于 amend 模式下的参考 4. 更新 generate_commit_message 函数在 amend 模式下提取原始 Change-Id 并 保留到新提交消息中 5. 将原始提交消息添加到 AI 提示词中,为 amend 模式提供更好的上下文 6. 添加全面的单元测试,覆盖 Change-Id 提取和添加功能 Log: 改进 amend 模式下的提交消息生成,保留 Change-Id 并参考原始消息 Influence: 1. 测试带有现有 Change-Id 的 amend 模式,确保 Change-Id 被保留 2. 测试没有 Change-Id 的 amend 模式,确保不会添加额外的 Change-Id 3. 验证 AI 在参考原始内容的同时生成改进的提交消息 4. 测试不同消息格式下的 Change-Id 提取功能 5. 验证 Change-Id 添加逻辑正确处理边界情况 6. 测试带有和不带有其他元数据标签的提交消息生成 --- src/commit.rs | 117 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 3 deletions(-) diff --git a/src/commit.rs b/src/commit.rs index 3ba3adc..5af5494 100644 --- a/src/commit.rs +++ b/src/commit.rs @@ -3,6 +3,35 @@ use crate::ai_service; use crate::config; use crate::git; +/// 从提交消息中提取 Change-Id +fn extract_change_id(message: &str) -> Option { + let change_id_regex = Regex::new(r"(?m)^Change-Id:\s*(.+)$").ok()?; + change_id_regex.captures(message) + .and_then(|cap| cap.get(1)) + .map(|m| m.as_str().trim().to_string()) +} + +/// 将 Change-Id 添加到提交消息中(如果还没有的话) +fn append_change_id(message: &str, change_id: &str) -> String { + // 检查消息中是否已经有 Change-Id + if message.contains("Change-Id:") { + return message.to_string(); + } + + let mut result = message.to_string(); + + // 确保消息末尾有换行 + if !result.ends_with('\n') { + result.push('\n'); + } + + // 添加 Change-Id(如果有其他标记,Change-Id 应该在最后) + result.push('\n'); + result.push_str(&format!("Change-Id: {}", change_id)); + + result +} + // 语言模式枚举 #[derive(Debug, Clone, Copy)] enum LanguageMode { @@ -562,9 +591,21 @@ impl LanguageMode { } // 统一的提示词构建函数 -fn build_prompt(mode: LanguageMode, user_message: Option<&str>, include_test_suggestions: bool, include_log: bool) -> String { +fn build_prompt(mode: LanguageMode, user_message: Option<&str>, include_test_suggestions: bool, include_log: bool, original_message: Option<&str>) -> String { let mut prompt = String::from(mode.template(include_test_suggestions, include_log)); + // 如果有原始提交信息(amend 模式),先添加它作为参考 + if let Some(orig_msg) = original_message { + match mode { + LanguageMode::ChineseOnly => { + prompt.push_str(&format!("\n\n原始提交信息(请参考但不要完全照搬):\n{}\n", orig_msg)); + } + _ => { + prompt.push_str(&format!("\n\nOriginal commit message (for reference, but create improved version):\n{}\n", orig_msg)); + } + } + } + if let Some(msg) = user_message { match mode { LanguageMode::ChineseOnly => { @@ -760,7 +801,27 @@ pub async fn generate_commit_message( let language_mode = LanguageMode::determine(only_chinese, only_english); let include_test_suggestions = !no_influence; let include_log = !no_log; - let prompt = build_prompt(language_mode, message.as_deref(), include_test_suggestions, include_log); + + // 在 amend 模式下获取原始提交消息作为参考 + let (original_message, original_change_id) = if amend { + match git::get_last_commit_message() { + Ok(msg) => { + let change_id = extract_change_id(&msg); + (Some(msg), change_id) + } + Err(_) => (None, None) + } + } else { + (None, None) + }; + + let prompt = build_prompt( + language_mode, + message.as_deref(), + include_test_suggestions, + include_log, + original_message.as_deref() + ); debug!("生成的提示信息:\n{}", prompt); @@ -771,7 +832,7 @@ pub async fn generate_commit_message( if amend { println!("\n正在基于上一次提交的更改生成新的提交信息..."); // 显示原提交信息供参考 - if let Ok(original_msg) = git::get_last_commit_message() { + if let Some(ref original_msg) = original_message { println!("原提交信息:"); println!("----------------------------------------"); println!("{}", original_msg.trim()); @@ -817,6 +878,13 @@ pub async fn generate_commit_message( } } } + + // 在 amend 模式下,如果原提交有 Change-Id,保留它 + if amend { + if let Some(change_id) = original_change_id { + content = append_change_id(&content, &change_id); + } + } // 预览生成的提交信息 if amend { @@ -1043,4 +1111,47 @@ mod tests { fn parse_pms_link_multiple(issues: &str) -> anyhow::Result { parse_issue_reference(issues) } + + #[test] + fn test_extract_change_id() { + let message = "feat: add new feature\n\nThis is a commit message\n\nChange-Id: I1234567890abcdef1234567890abcdef12345678\n"; + let change_id = extract_change_id(message); + assert_eq!(change_id, Some("I1234567890abcdef1234567890abcdef12345678".to_string())); + } + + #[test] + fn test_extract_change_id_not_found() { + let message = "feat: add new feature\n\nThis is a commit message without change id\n"; + let change_id = extract_change_id(message); + assert_eq!(change_id, None); + } + + #[test] + fn test_append_change_id() { + let message = "feat: add new feature\n\nThis is a commit message\n"; + let change_id = "I1234567890abcdef1234567890abcdef12345678"; + let result = append_change_id(message, change_id); + assert!(result.contains("Change-Id: I1234567890abcdef1234567890abcdef12345678")); + } + + #[test] + fn test_append_change_id_already_exists() { + let message = "feat: add new feature\n\nThis is a commit message\n\nChange-Id: I1234567890abcdef1234567890abcdef12345678\n"; + let change_id = "I1234567890abcdef1234567890abcdef12345678"; + let result = append_change_id(message, change_id); + // 应该返回原消息,不重复添加 + assert_eq!(result, message); + } + + #[test] + fn test_append_change_id_with_other_marks() { + let message = "feat: add new feature\n\nThis is a commit message\n\nFixes: #123\nPMS: BUG-456\n"; + let change_id = "I1234567890abcdef1234567890abcdef12345678"; + let result = append_change_id(message, change_id); + assert!(result.contains("Fixes: #123")); + assert!(result.contains("PMS: BUG-456")); + assert!(result.contains("Change-Id: I1234567890abcdef1234567890abcdef12345678")); + // Change-Id 应该在最后 + assert!(result.ends_with("Change-Id: I1234567890abcdef1234567890abcdef12345678")); + } }