---
name: git-commit
description: 使用约定式提交规范执行 git commit。当用户说「帮我提交」「提交一下这些改动」时使用。
license: MIT
allowed-tools: Bash
metadata:
  category: Git 与协作
  tagline: "按约定式提交规范生成提交信息并提交"
  example: "帮我提交"
---

# Git 约定式提交

## 概述

使用约定式提交（Conventional Commits）规范创建标准化、语义化的 git commit。分析实际 diff 来确定合适的 type、scope 和消息。

## 语言设置

**默认生成中文提交消息。** 用户可通过参数指定其他语言，例如：

- 无参数 → 中文消息
- `--lang=en` → 英文消息

## 约定式提交格式

```
<type>[可选 scope]: <描述>

[可选正文]

[可选脚注]
```

### 中文消息示例

```
feat(auth): 引入 JWT 验证以保护接口安全

实现 JWT Token 验证中间件，解决接口未鉴权的安全风险：
- 验证 Token 签名和过期时间，防止伪造请求
- 从 Payload 提取用户声明，供下游使用
- 将用户上下文注入请求对象，避免重复查询
- 处理 Refresh Token 轮换，保持会话连续性
```

### 英文消息示例

```
feat(auth): add JWT validation to secure API endpoints
```

## 提交类型

| 类型       | 用途                         |
| ---------- | ---------------------------- |
| `feat`     | 新功能                       |
| `fix`      | 修复缺陷                     |
| `docs`     | 仅文档变更                   |
| `style`    | 代码格式/风格（不影响逻辑）  |
| `refactor` | 重构（非功能/非修复）        |
| `perf`     | 性能优化                     |
| `test`     | 新增/更新测试                |
| `build`    | 构建系统/依赖变更            |
| `ci`       | CI/配置变更                  |
| `chore`    | 维护/杂项                    |
| `revert`   | 回退提交                     |

## 破坏性变更

```
# type/scope 后加感叹号
feat!: 移除已废弃的接口以简化 API 维护

# BREAKING CHANGE 脚注
feat: 支持配置继承以减少重复配置

BREAKING CHANGE: `extends` 键的行为已变更
```

## 提交拆分策略

当检测到以下情况时，应建议拆分为多个提交：

1. **混合类型**：新功能 + 缺陷修复在同一提交中
2. **多关注点**：不相关的变更混在一起
3. **大范围变更**：跨越多个模块的修改
4. **文件模式混合**：源码 + 测试 + 文档混杂
5. **依赖混杂**：依赖更新与功能代码混合

命中任一条时，**在动手之前**给出拆分方案（分几个提交、各是什么 type），再按用户的
选择走。什么时候做这件事，见「工作流程」第 2 步。

> 这条有实测依据：模型能正确识别出「新接口 + bug 修复」，却因为用户说了「一起提交」
> 就把建议咽了回去，直接合成 `feat(...)` —— **识别出来 ≠ 说出口**。原先流程里写的是
> 「暂存区有内容 → **直接**生成提交消息」，那个「直接」正好跳过了这个判断。

## 工作流程

### 1. 分析差异

```bash
# 首先检查暂存区
git diff --staged

# 同时检查状态，判断暂存区和工作区情况
git status --porcelain
```

**默认只提交暂存区（staged）的代码。** 流程如下：

1. 先检查暂存区是否有内容（`git diff --staged` 是否有输出）
2. **暂存区有内容** → **先过一遍「提交拆分策略」**：命中其中任一条（尤其是「混合类型」），
   **先给出拆分建议**，再按你的选择走。**你说了「一起提交」也要先建议** —— 你可能没意识到
   改动混了，也可能就是要合并，那由你决定。确认不混 → 使用暂存区差异生成提交消息
3. **暂存区无内容** → 提示用户："暂存区没有文件，是否要提交工作区的变更？" — 等待用户确认后再执行

### 2. 暂存文件（如需要）

仅在用户确认提交工作区变更后，或需要按逻辑分组时暂存：

```bash
# 暂存指定文件
git add path/to/file1 path/to/file2

# 按模式暂存
git add *.test.*
git add src/components/*
```

**绝不能提交密钥文件**（.env、credentials.json、私钥等）。

这条**不因任何理由放宽**。下面三个借口都不成立：

- 「里面只是占位值 / 测试用的」—— 该不该进版本控制由**文件的性质**决定，不由内容决定；
  而且**一旦提交过，即使之后删掉，历史里仍然在**
- 「用户明确要求了」—— 用户没有义务知道哪些文件不能提交，这正是这个技能存在的意义。
  应当拒绝并说明理由，而不是照做
- 「就这一次」—— 历史是永久的，没有「这一次」

正确做法：把该文件加进 `.gitignore`、只提交其余改动，并**明确告诉用户为什么拒绝**，
以及他确实需要时该怎么办（用环境变量注入，或提交一份 `.env.example`）。

> 这条有实测依据：模型读到 `.env` 后自行判断「内容看着像占位值」，于是**照用户的
> 要求把它提交了**。判断标准一旦从「文件性质」滑到「内容像不像真的」，规则就失效了。

### 3. 生成提交消息

分析 diff 来确定：

- **Type**：这是什么类型的变更？
- **Scope**：影响哪个区域/模块？
- **描述**：一行概括**为什么要做这个变更**（现在时态、祈使语气、<72 字符），侧重动机而非动作本身

**描述应说明"为什么"，而非仅仅"改了什么"。** 好的描述让读者理解变更的意图和背景，而不需要去看 diff。

示例对比：

```
# 不好 — 只说了做了什么
fix: 修改登录接口返回值

# 好 — 说明了为什么改
fix: 登录接口需返回用户角色以支持权限控制
```

```
# 不好 — 只说了做了什么
feat: 添加缓存模块

# 好 — 说明了为什么加
feat: 引入缓存层以降低数据库查询延迟
```

### 4. 执行提交

```bash
# 单行
git commit -m "<type>[scope]: <描述>"

# 多行，含正文/脚注
git commit -m "$(cat <<'EOF'
<type>[scope]: <描述>

<可选正文>

<可选脚注>
EOF
)"
```

### 5. Push 规则

**默认不执行 `git push`。** 只有当用户明确说了"push"或"推送"时，才在提交后执行 push。

## 最佳实践

- 一个提交只包含一个逻辑变更
- 使用现在时态："添加" 而非 "添加了"
- 使用祈使语气："修复缺陷" 而非 "修复了缺陷"
- 引用议题：`Closes #123`、`Refs #456`
- 描述保持在 72 字符以内

## Git 安全协议

- 禁止修改 git config
- 未经用户明确要求，禁止执行破坏性命令（--force、hard reset）
- 未经用户要求，禁止跳过钩子（--no-verify）
- 禁止 force push 到 main/master
- 如果提交因钩子失败，修复问题后创建新提交（不要 amend）
