Skip to main content

git_commit_helper_cli/
git.rs

1use crate::commit::CommitMessage;
2use crate::ai_service;
3use crate::review;
4use crate::config::{self, TranslateDirection};
5use dialoguer::Confirm;
6use log::{debug, info};
7use std::path::Path;
8use textwrap::fill;
9
10const MAX_LINE_LENGTH: usize = 72;
11
12fn is_auto_generated_commit(title: &str) -> bool {
13    let patterns = ["Merge", "Cherry-pick", "Revert"];
14    patterns.iter().any(|pattern| title.starts_with(pattern))
15}
16
17pub async fn process_commit_msg(path: &Path, no_review: bool) -> anyhow::Result<()> {
18    debug!("开始处理提交消息: {}", path.display());
19
20    // 检查环境变量是否设置了跳过审查
21    if std::env::var("GIT_COMMIT_HELPER_SKIP_REVIEW").is_ok() {
22        debug!("检测到跳过审查环境变量,跳过代码审查");
23        return Ok(());
24    }
25
26    let content = std::fs::read_to_string(path)?;
27    let msg = CommitMessage::parse(&content);
28
29    // 检查是否是自动生成的提交消息
30    if is_auto_generated_commit(&msg.title) {
31        debug!("检测到自动生成的提交消息,跳过翻译和审查");
32        return Ok(());
33    }
34
35    // 获取配置并执行代码审查
36    let config = crate::config::Config::load()?;
37    if !review::should_skip_review(&msg.title) {
38        info!("正在进行代码审查...");
39        if let Some(review) = review::review_changes(&config, no_review).await? {
40            // 直接在终端显示审查结果
41            println!("\n{}\n", review);
42        }
43    }
44
45    if !contains_chinese(&msg.title) {
46        debug!("未检测到中文内容,跳过翻译");
47        return Ok(());
48    }
49
50    info!("检测到中文内容,准备翻译");
51
52    if !Confirm::with_theme(&dialoguer::theme::ColorfulTheme::default())
53        .with_prompt("检测到提交信息包含中文,是否需要翻译?")
54        .default(true)
55        .interact()? {
56        return Ok(());
57    }
58
59    info!("开始翻译流程,默认使用 {:?} 服务", config.default_service);
60
61    // 翻译标题(中译英)
62    let en_title = ai_service::translate_with_fallback(&config, &msg.title, &TranslateDirection::ChineseToEnglish).await?;
63    let en_title = wrap_text(&en_title, MAX_LINE_LENGTH);
64    let original_title = msg.title.clone();
65
66    // 翻译正文(如果有的话)
67    let (en_body, cn_body) = if let Some(body) = &msg.body {
68        let en_body = ai_service::translate_with_fallback(&config, body, &TranslateDirection::ChineseToEnglish).await?;
69        (Some(wrap_text(&en_body, MAX_LINE_LENGTH)), Some(body.clone()))
70    } else {
71        (None, None)
72    };
73
74    // 构建新的消息结构
75    let mut body_parts = Vec::new();
76
77    // 添加英文和中文内容
78    if let Some(en_body) = en_body {
79        body_parts.push(en_body);
80        body_parts.push(String::new());
81    }
82
83    body_parts.push(original_title);
84
85    if let Some(body) = cn_body {
86        body_parts.push(String::new());
87        body_parts.push(wrap_text(&body, MAX_LINE_LENGTH));
88    }
89
90    let new_msg = CommitMessage {
91        title: en_title,
92        body: Some(body_parts.join("\n")),
93        marks: msg.marks, // 保持原有标记不变
94    };
95
96    info!("翻译完成,正在写入文件");
97    std::fs::write(path, new_msg.format())?;
98    info!("处理完成");
99    Ok(())
100}
101
102fn contains_chinese(text: &str) -> bool {
103    text.chars().any(|c| c as u32 >= 0x4E00 && c as u32 <= 0x9FFF)
104}
105
106pub fn wrap_text(text: &str, width: usize) -> String {
107    fill(text, width)
108}
109
110/// 获取上一次提交的差异内容,用于 amend 模式
111pub fn get_last_commit_diff() -> anyhow::Result<String> {
112    use std::process::Command;
113    
114    let output = Command::new("git")
115        .args(["diff", "HEAD~1..HEAD", "--no-prefix"])
116        .output()?;
117
118    if !output.status.success() {
119        return Err(anyhow::anyhow!("执行 git diff HEAD~1..HEAD --no-prefix 命令失败,可能没有足够的提交历史"));
120    }
121
122    Ok(String::from_utf8(output.stdout)?)
123}
124
125/// 获取上一次提交的信息
126pub fn get_last_commit_message() -> anyhow::Result<String> {
127    use std::process::Command;
128    
129    let output = Command::new("git")
130        .args(["log", "-1", "--pretty=format:%s%n%n%b"])
131        .output()?;
132
133    if !output.status.success() {
134        return Err(anyhow::anyhow!("执行 git log 命令失败"));
135    }
136
137    Ok(String::from_utf8(output.stdout)?)
138}