Files
git-air/cmd/msg.go
T
unclesam b04d70b99a feat: 拆分评审与提交消息提示词并增加 hook 备份与 JSON 输出
- 新增 custom_reviewer_prompt 和 custom_commit_msg_prompt,兼容旧 custom_prompt
- pre-commit hook 安装时备份已有 hook,卸载时恢复
- 支持 --format json 输出评审结果
- diff 发送前自动脱敏敏感信息
2026-08-23 22:31:30 +08:00

171 lines
5.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package cmd
import (
"bufio"
"context"
"fmt"
"os"
"os/exec"
"strings"
"github.com/spf13/cobra"
"github.com/unclesam-ly/git-air/internal/git"
"github.com/unclesam-ly/git-air/internal/llm"
"github.com/unclesam-ly/git-air/internal/reviewer"
"github.com/unclesam-ly/git-air/internal/ui"
)
var msgCmd = &cobra.Command{
Use: "msg [git diff 参数,如 HEAD~1, main..feature, 文件名等]",
Aliases: []string{"commit-msg", "commit"},
Short: "根据代码变更智能生成规范的 Conventional Commits 提交信息",
Example: ` git air msg # 根据当前暂存区代码自动生成
git air msg -l ja # 指定生成日语提交说明
git air msg -l en # 指定生成英文提交说明
git air msg -c # 生成后直接执行 git commit`,
Run: func(cmd *cobra.Command, args []string) {
ctx := cmd.Context()
// 1. 检查并获取 Diff (默认优先获取暂存区)
diff, err := git.GetDiff(ctx, args...)
if err != nil {
ui.PrintError("%v", err)
return
}
// 2. 加载配置
cfg, err := LoadConfig()
if err != nil {
ui.PrintError("加载配置失败: %v", err)
return
}
if flagKey, _ := cmd.Flags().GetString("key"); flagKey != "" {
cfg.APIKey = flagKey
}
if flagModel, _ := cmd.Flags().GetString("model"); flagModel != "" {
cfg.Model = flagModel
}
if flagProvider, _ := cmd.Flags().GetString("provider"); flagProvider != "" {
cfg.Provider = flagProvider
}
// 3. 初始化大模型客户端
llmClient, err := llm.NewClient(llm.Config{
Provider: cfg.Provider,
APIKey: cfg.APIKey,
BaseURL: cfg.BaseURL,
Model: cfg.Model,
PriceInput: cfg.PriceInput,
PriceOutput: cfg.PriceOutput,
})
if err != nil {
ui.PrintError("初始化模型失败: %v", err)
return
}
// 4. 确定目标语言 (命令行 Flag > 配置文件 > 自动探测系统语言)
lang, _ := cmd.Flags().GetString("lang")
if lang == "" {
lang = cfg.CommitLang
}
repoRoot, _ := git.GetRepoRoot(ctx)
rev := reviewer.NewReviewer(llmClient, repoRoot, "")
targetLang := reviewer.DetectLanguage(lang)
langDesc := "English"
switch targetLang {
case "zh":
langDesc = "简体中文"
case "ja":
langDesc = "日本語"
case "ko":
langDesc = "한국어"
}
fmt.Printf("\033[1;36m[git-air]\033[0m 正在生成 Conventional Commit Message... \033[0;90m(Language: %s / Engine: %s)\033[0m\n", langDesc, cfg.Model)
fmt.Println("\033[0;90m" + strings.Repeat("─", 65) + "\033[0m")
commitMsg, err := rev.GenerateCommitMsg(ctx, diff, lang, cfg.CustomCommitMsgPrompt)
if err != nil {
ui.PrintError("生成 Commit Message 失败: %v", err)
return
}
// 5. 打印生成的提交信息
fmt.Printf("\033[1;32m%s\033[0m\n", commitMsg)
fmt.Println("\033[0;90m" + strings.Repeat("─", 65) + "\033[0m")
// 6. 是否自动暂存与提交
flagAll, _ := cmd.Flags().GetBool("all")
flagCommit, _ := cmd.Flags().GetBool("commit")
if flagCommit {
if flagAll {
if err := git.StageAll(ctx); err != nil {
ui.PrintError("自动暂存失败: %v", err)
return
}
}
executeGitCommit(ctx, commitMsg)
return
}
// 检查暂存区状态
hasStaged, _ := git.HasStagedChanges(ctx)
reader := bufio.NewReader(os.Stdin)
if !hasStaged && !flagAll {
fmt.Print("? 检测到改动尚未暂存 (unstaged),是否自动暂存并提交 (git add -A)?[Y/n]: ")
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(strings.ToLower(input))
if input == "" || input == "y" || input == "yes" {
if err := git.StageAll(ctx); err != nil {
ui.PrintError("自动暂存失败: %v", err)
return
}
executeGitCommit(ctx, commitMsg)
} else {
fmt.Println("\033[0;90m已跳过提交。请执行 'git add <文件>' 暂存修改后再提交。\033[0m")
}
return
}
// 交互式询问是否直接提交
fmt.Print("? 是否直接以此信息执行 git commit?[Y/n]: ")
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(strings.ToLower(input))
if input == "" || input == "y" || input == "yes" {
executeGitCommit(ctx, commitMsg)
} else {
fmt.Println("\033[0;90m已跳过提交。你可以复制上方内容手动提交。\033[0m")
}
},
}
func executeGitCommit(ctx context.Context, msg string) {
cmd := exec.CommandContext(ctx, "git", "commit", "-m", msg)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
ui.PrintError("执行 git commit 失败: %v", err)
return
}
ui.PrintSuccess("代码提交成功!🎉")
}
func init() {
msgCmd.Flags().StringP("lang", "l", "", "生成语言 (auto, zh, en, ja, ko)")
msgCmd.Flags().BoolP("all", "a", false, "自动暂存所有已修改的文件 (git add -A)")
msgCmd.Flags().BoolP("commit", "c", false, "生成后无需确认直接执行 git commit")
msgCmd.Flags().StringP("key", "k", "", "临时指定 API Key")
msgCmd.Flags().StringP("model", "m", "", "临时指定模型名称")
msgCmd.Flags().StringP("provider", "p", "", "临时指定 Provider")
rootCmd.AddCommand(msgCmd)
}