- Zero-Config DX:未配置时敲击
git msg即可配合本地默认端点(如 LMStudio / 本地 LLM 服务的http://127.0.0.1:1234)直接运行。 - Local-First:针对 2B–9B 本地小参数及量化模型进行 Prompt 约束与输出清洗(Sanitization),同时兼容各类云端 OpenAI 格式端点(DeepSeek, Qwen 等)。
- Git-Native:遵循 Git 子命令机制(二进制命名为
git-msg),深度继承 Git 配置(如core.editor),保留完整的 Git 提交生命周期与系统编辑器控制权。
git msg:默认执行流程(分析 Diff -> 生成 Message -> 交互式提交)。git msg config:使用系统编辑器打开全局配置文件(若不存在则自动以默认模板创建)。- 选项:
--show-path(仅打印配置文件绝对路径,不调起编辑器)。
- 选项:
git msg init:在当前 Git 仓库根目录下生成一份.gitmsg.toml模板文件。
-y, --yes:跳过交互,生成后自动确认提交(若使用未暂存的修改,会自动触发git add -A)。-d, --dry-run:仅向stdout打印生成的 Message,不产生交互,不执行任何 Git 写入操作。-t, --template <NAME>:临时指定模板类型(conventional|simple|gitmoji)。-m, --model <MODEL>:临时覆盖使用的模型名称。-e, --endpoint <URL>:临时覆盖请求的端点地址。
- 命令行参数 (CLI Flags)
- 环境变量 (
GIT_MSG_ENDPOINT,GIT_MSG_MODEL,GIT_MSG_API_KEY) - 项目级配置(当前 Git 仓库根目录下的
.gitmsg.toml) - 用户全局配置(
~/.config/git-msg/config.toml或 Windows%APPDATA%\git-msg\config.toml) - 内置默认值(Hardcoded Defaults)
[provider]
# 支持输入基础 URL(如 http://127.0.0.1:1234)或带版本路径(如 http://localhost:1234/v1)
endpoint = "http://127.0.0.1:1234"
api_key = "not-needed"
model = "qwen3.5-2b"
timeout_seconds = 30
temperature = 0.2
[behavior]
# 内置模板: "conventional" | "simple" | "gitmoji"
template = "conventional"
language = "en-US" # "en-US" | "zh-CN"
auto_stage_if_empty = true # 暂存区为空时是否自动暂存工作区所有更改
max_diff_lines = 500 # 全局 Diff 总行数截断上限
max_file_diff_lines = 150 # 单文件最大 Diff 行数(避免单大文件耗尽配额)
[diff_filter]
# 忽略不参与 Diff 提取的文件模式 (Glob 匹配)
ignore_files = [
"Cargo.lock",
"package-lock.json",
"pnpm-lock.yaml",
"yarn.lock",
"*.min.js",
"*.min.css",
"*.svg",
"*.lock"
]
# 可选:自定义 Prompt,若配置则完全覆盖内置 template
# 可用变量: {diff}, {recent_commits}, {language}
# custom_prompt = """
# 你是一个提交信息生成器,根据以下 Diff 生成中文提交信息:
# {diff}
# """[git diff --cached]
│
(为空?) ──Yes──> [检测工作区已跟踪与未跟踪文件]
│ │
│ (检查 git status --porcelain,并使用 git add -N 捕获未跟踪文件)
│ │
No (工作区仍为空?) ──Yes──> 打印 "No changes detected." -> 退出
│ │
│ (存在改动且 auto_stage_if_empty = true)
│ │
│ 标记 need_stage = true, 提取完整工作区 diff
│ │
└────────┬────────┘
▼
[Diff Filter 过滤] ──(剔除 ignore_files 中的匹配文件、忽略二进制文件)
▼
[Diff Truncate 截断保护]
1. 单文件超出 max_file_diff_lines 时截断并在文件尾部追加 "[... X lines truncated ...]"
2. 全局行数超出 max_diff_lines 时截断剩余文件并在末尾追加全局截断说明
▼
[Context 提取] ──(获取 git log -n 3 --oneline 作为风格参考;初次提交无历史时平滑忽略)
You are an expert Git commit message generator.
Analyze the provided code diff and recent commits, then output a clean and concise commit message.
Strict Rules:
1. Output MUST contain ONLY the commit message. No explanations, no markdown blocks, no prefix text.
2. Follow Conventional Commits format: <type>(<optional-scope>): <subject>
3. Use language: {language}.
4. Keep the subject line under 72 characters, imperative mood, no ending period.
5. If necessary, provide a brief body separated by an empty line using bullet points.
-
URL 规范化:
- 输入
http://127.0.0.1:1234$\rightarrow$ 规范化为http://127.0.0.1:1234/v1/chat/completions。 - 输入
http://localhost:1234/v1$\rightarrow$ 规范化为http://localhost:1234/v1/chat/completions。 - 输入已包含完整路径时直接保留。
- 输入
-
Payload 结构:
POST .../chat/completions-
messages:system角色说明 +user输入(包含过滤后的 Diff + 历史提交记录)。 -
temperature:0.2。
针对小参数模型及推理模型可能附带的多余输出,执行严格清洗流程:
- 剥离推理标签:去除
<think>[\s\S]*?</think>。 - 剥离 Markdown 围栏:去除首尾
```markdown/```git/```标记。 - 剥离前导提示语:去除常见的
Here is the commit message:、Commit message:、提交信息:等前导行。 - 格式规范化:修剪首尾多余空白换行,确保输出为纯正的 Git Commit Message 文本。
-
执行开始:
- 启动
indicatif动画(提示Analyzing diff & generating commit message...)。
- 启动
-
获取响应:
- 停止动画,清洗后在终端以清晰边框高亮输出生成的 Commit Message。
-
分流处理:
- 若含
--yes:若need_stage == true则先执行git add -A,随后直接提交。 - 默认交互:使用
inquire::Select弹出单选菜单:-
Commit: 确认提交。 -
Edit: 调用系统编辑器打开临时文件进行人工修改,保存后以修改内容提交。 -
Regenerate: 重新调用模型生成。 -
Abort: 取消并退出。
-
- 若含
-
编辑器与 Commit 执行安全机制:
-
编辑器解析优先级:
git var GIT_EDITOR$\rightarrow$ $GIT_EDITOR$\rightarrow$ $VISUAL$\rightarrow$ $EDITOR$\rightarrow$ Windows 下回退到notepad,Unix 下回退到vi。 -
安全 Commit:将最终 Message 写入临时文件,执行
git commit -F <temp_file>,完全避免 Shell 传递多行文本和引号时的转义问题。
-
编辑器解析优先级:
[dependencies]
clap = { version = "4.4", features = ["derive", "env"] }
reqwest = { version = "0.11", features = ["json", "blocking"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
toml = "0.8"
inquire = "0.7"
indicatif = "0.17"
colored = "2.1"
glob = "0.3"
tempfile = "3.10"
dirs = "5.0"
regex = "1.10"
anyhow = "1.0"src/
├── main.rs # 程序入口,主流程调度与命令分发
├── cli.rs # Clap 命令行结构定义
├── config.rs # 多级配置加载、合并、默认值与路径解析
├── git.rs # 封装 git diff, git log, git status, git commit -F, 编辑器调用
├── filter.rs # Diff 忽略文件过滤、单文件/全局行数截断
├── llm.rs # HTTP 客户端封装(URL规范化、OpenAI协议交互)
├── sanitizer.rs # 剥离 think 标签、Markdown 块、前导提示词
├── prompt.rs # 模板管理与变量填充
└── ui.rs # Inquire 菜单与 Indicatif 动画
- LLM 连接失败(
ECONNREFUSED/ Timeout):- 拦截底层网络错误,输出结构化提示:
Error: Failed to connect to LLM endpoint at [URL]. → Is your local LLM (e.g. LMStudio / Ollama) running? → Use 'git msg config' to check or modify your configuration.
- 拦截底层网络错误,输出结构化提示:
- Git Commit 失败(如 Pre-commit hook 拦截、GPG 签名错误):
- 透传 Git 原始 stderr 输出。
- 在终端保留本次生成的 Message 文本或提示临时文件位置,防止用户编辑内容丢失。
- 初次提交(无 HEAD 分支 / 无历史 commit):
git log获取 context 失败时平滑降级(忽略历史 commit 上下文),不中断主提交流程。