commit · git:20260817.d4dfa41 · 2026-08-17 · sha256 0e44d62a52c56e53

commit git:20260817.d4dfa41A

Immutable. This exact content is served forever at /api/v1/blob/0e44d62a52c56e53.

---
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。
- 没有改动就不要造一个空提交。