---
name: commit
description: 要把改动提交成 git commit 时用。先看清工作区里有几件事、按主题分组、只暂存这一组，以及消息里该写什么不该写什么。
---

# 提交改动

## 先看清全貌，再决定分几个提交

并行跑这三条，不要只看 `git status`：

```bash
git status --short
git diff --stat
git log --oneline -10
```

`git log` 那条是为了**照着这个仓库的习惯来**：标题用什么语言、有没有
`feat:` 这类前缀、正文写多细、提交粒度是大批次还是一改一提。不同仓库
差别很大，猜错的表现是这个提交在历史里读起来像外来的。

判据是**能不能用一句话说清这批改动解决了什么问题**。能，就是一个提交；
需要用「以及」连接两件无关的事，就该切开。

## 只暂存这一组

`[约束]` **不要 `git add -A` 或 `git add .`**，除非你已经确认工作区里只有
这一件事。工作区经常同时挂着几个主题的改动（有些还是用户自己没提完的），
全加进去就把无关的东西一起提了，而那之后再拆得动历史。

逐个文件或逐个目录加，然后确认一遍暂存区：

```bash
git add path/to/a path/to/b
git status --short          # 前缀 M/A 的那些就是这次要提的
```

## 别带上不该带的

- 密钥与凭证：`.env`、`*.pem`、`auth.json`、任何含 token 的文件。
  用户明确要求提交这类文件时，先提醒他一次。
- 但**生成物不一定该跳过**。有些仓库把生成的类型、schema、lockfile
  检入版本库并在 CI 里校验同步 —— 那种情况下漏掉它们会让 CI 直接红。
  不确定就看 `.gitignore` 和 CI 配置，别按直觉判断。

## 消息

标题一行说清这批做了什么，语言和格式跟着 `git log` 里的既有风格。

正文写**为什么**，不是罗列做了什么 —— 做了什么看 diff 就有，而
「为什么这么做、为什么不用另一条路」只有现在写下来。特别值得写的：

- 不选另一条方案的理由；
- 踩过的坑和它的**具体症状**（「表现是按了没反应」比「修复了事件问题」
  有用得多）；
- 这次改动依赖或守住了什么约束，破了会怎样。

用 HEREDOC 传消息，保证换行不被吃掉：

```bash
git commit -m "$(cat <<'EOF'
标题

正文段落。

EOF
)"
```

## 提交之后

跑一次 `git status` 确认干净，并把结果告诉用户 —— 包括**没提交的那些还留在
工作区**，那是他接着要处理的。

## 不要做的事

- 不要 `git push`、切分支、`stash`、`reset --hard`，除非用户明确要求。
- 不要 `--amend`，除非用户明确要求、那个提交是本次对话里刚做的、而且还没推。
- 提交被 pre-commit hook 拒了：修问题再提一个**新的**提交，不要 amend。
- 没有改动就不要造一个空提交。
