Git 提交
核心原则
一次提交只承载一个可独立审查、验证和回滚的职责。
目录相同、scope 相同、属于同一功能或同一次发布,都不能证明改动应该放在同一提交中。
判断优先级:
职责边界 > 仓库规则 > 变更类型 > 文件数量
当一个提交主题必须使用“和”“以及”“同时”才能描述时,通常说明还需要继续拆分。
操作边界
根据用户请求确定操作模式:
| 用户请求 | 允许的操作 |
|---|---|
| 生成、优化或检查 commit message | 只读取状态和差异,不执行暂存或提交 |
| 分析或建议如何拆分提交 | 只输出拆分方案,除非用户同时要求暂存 |
| 暂存或拆分改动 | 可以执行 git add,不得自动提交 |
| 提交代码 | 可以暂存并执行 git commit |
| 推送代码 | 只有用户明确要求时才执行 git push |
| 拆分、合并或修改已有提交 | 必须先检查历史改写安全条件 |
“提交全部改动”只授权处理全部改动范围,不代表可以把全部改动压成一个提交。
始终保留用户已有改动,不清理、不覆盖、不重置、不顺手提交无关内容。
仓库规则优先级
提交前必须按以下顺序检查规则:
- 用户当前指令
AGENTS.md、CLAUDE.md、GEMINI.md等智能体说明CONTRIBUTING.md、开发文档和提交模板commitlint、Commitizen、语义发布等配置- 最近的有效提交历史
- 本 Skill 的默认规则
建议先运行:
git status --short --branch
git diff --stat
git diff --cached --stat
git log --oneline -20
如果仓库规定了固定的 type、scope、主题语言、长度、正文或署名格式,必须遵守仓库规定。
不要根据个人习惯发明仓库未允许的 type 或 scope。历史提交只能辅助判断,不能覆盖明确的文档和配置。
强制执行流程
第一步:盘点改动
检查工作区、暂存区和未跟踪文件:
git status --short
git diff --stat
git diff --name-only
git diff --cached --stat
必要时查看具体差异:
git diff -- path/to/file
git diff --cached -- path/to/file
必须同时检查:
- 已修改但未暂存的文件
- 已暂存的文件
- 未跟踪文件
- 用户此前已经暂存的内容
- 同一文件中的多个独立 hunk
不得假设暂存区中的内容都属于当前任务。
第二步:识别职责
暂存前,对每组改动依次回答:
- 能否用一个准确主题描述,且不使用“和”“以及”“同时”?
- 能否独立审查、验证和回滚?
- 每个文件或 hunk 是否都直接服务于同一个行为?
任一答案为“否”,必须继续拆分。
以下改动通常具有独立审查和回滚价值:
- 用户功能或缺陷修复
- 配置调整
- 数据库迁移
- 公共接口变更
- 重构
- 性能优化
- 文档
- 测试
- 构建与依赖
- CI 配置
- 发布与部署脚本
同一文件包含多个职责时,必须按 hunk 分析,不能仅按文件路径分组。
第三步:输出提交计划
首次执行任何 git add 前,必须先向用户输出提交计划。
格式:
| 候选提交 | 文件或 hunk | 单一职责 | 验证命令 | 拟定提交消息 |
|---|---|---|---|---|
| 1 | path/to/file |
可独立审查和回滚的行为 | 能直接验证该行为的具体命令 | type(scope): subject |
要求:
- 每一行对应一个候选提交。
- 一个候选提交可以包含多个文件,但这些文件必须共同构成不可分割的行为。
- 同一文件包含多个职责时,使用不同 hunk 分行。
- 验证命令必须能验证该职责,不能只写“跑测试”“检查功能”等笼统描述。
git diff --cached --check只能检查补丁质量,不能作为行为验证命令。- 未输出计划,不得暂存。
- 发现新的职责后,必须更新计划再继续。
第四步:逐个暂存
每次只处理一个候选提交。
优先显式暂存路径:
git add -- src/auth/token.py tests/auth/test_token.py
同一文件包含多个职责时使用:
git add -p -- src/auth/token.py
除非已经确认全部改动只有一个职责,否则禁止使用:
git add .
git add -A
git add --all
不得覆盖或撤销用户此前已经暂存的内容。发现暂存区包含无关改动时,先说明情况,再采用不会丢失改动的方式重新划分。
第五步:检查暂存内容
每次提交前必须运行:
git diff --cached --stat
git diff --cached --check
git diff --cached
逐项确认:
- 暂存区只包含当前候选提交
- 所有文件和 hunk 都服务于同一职责
- 没有调试代码、临时文件或意外格式化
- 提交主题可以覆盖全部暂存内容
- 没有遗漏该行为不可缺少的改动
第六步:验证行为
运行提交计划中对应的验证命令。
验证应与风险相匹配,例如:
pytest tests/auth/test_token.py
npm test -- search
python -m compileall src/auth
docker compose config
bash -n scripts/deploy.sh
如果无法运行验证:
- 明确说明无法验证的原因;
- 说明已经完成的替代检查;
- 不得声称测试通过或功能正常。
第七步:提交
仅在用户授权提交时执行:
git commit -m "fix(auth): refresh expired access tokens"
提交后检查:
git show --stat --oneline HEAD
git status --short
然后处理下一个候选提交,重复暂存、验证和提交流程。
职责拆分规则
默认应该拆分
出现以下任一情况时,默认拆分:
- 不同部分可以独立回滚
- 主题需要列举多个独立动作
- 配置、迁移、业务逻辑、接口或文档具有独立价值
- 同一文件包含无关 hunk
- 纯重构与行为变更混在一起
- 多个测试验证不同的行为
- 发布流程中包含不同阶段的操作
涉及发布脚本时,至少分别判断以下职责:
- 构建镜像或制品
- 离线部署或首次安装
- 热更新或版本切换
- 回滚
- 模型转换或导出
这些职责即使服务于同一次发布,也默认是不同候选提交。
可以合并
只有满足以下条件之一时才能合并:
- 多个文件缺一不可,拆开后无法构建或无法表达完整行为
- 实现和最小回归测试按仓库惯例属于同一原子改动
- 纯机械重命名必须同步修改所有引用
- 拆开后中间提交会处于不可运行或语义不完整状态
“属于同一功能”“位于同一目录”“使用同一 scope”不是合并理由。
测试是否和实现放在同一提交,优先遵循仓库惯例;没有明确惯例时,根据是否能够独立验证和回滚判断。
提交消息
没有仓库规范时,默认采用 Conventional Commits:
<type>[(<scope>)][!]: <subject>
[可选正文]
[可选页脚]
规则:
type必填scope可选,使用稳定且简洁的模块名称!表示破坏性变更subject使用祈使语气,描述结果而不是操作过程- 主题末尾不加句号
- 正文解释原因、背景和重要影响,不重复 diff
- 破坏性变更使用
BREAKING CHANGE:页脚
默认 type:
| type | 使用场景 |
|---|---|
feat |
新增用户可见功能或能力 |
fix |
修复缺陷、异常或回归 |
docs |
仅修改文档 |
style |
仅修改格式、空白或不影响行为的样式 |
refactor |
调整结构但不改变外部行为 |
perf |
性能优化 |
test |
新增或修改测试 |
build |
构建系统或外部依赖 |
ci |
持续集成配置或脚本 |
chore |
不改变产品行为的维护工作 |
revert |
回滚已有提交 |
update、deps 等非标准 type 只有仓库明确允许时才能使用。
好的示例:
feat(search): add exact phrase matching
fix(auth): reject expired refresh tokens
docs(api): document pagination limits
refactor(cache): isolate eviction policy
避免:
update files
fix bug
misc changes
feat(core): add cache and update docs
未经用户要求或仓库明确规定,不添加:
Co-Authored-By- AI 或生成工具署名
- 额外身份页脚
历史改写
修改已有提交前必须确认:
- 目标提交由当前任务创建,或用户明确授权修改
- 提交尚未推送,或用户明确授权改写远端历史
- 工作区和暂存区改动已经安全保留
- 当前分支不是多人共享或受保护分支
不得擅自改写:
- 他人创建的提交
- 已共享分支历史
- 已发布版本
- 用户未授权修改的远端提交
提交已经推送时,必须先说明强制推送可能覆盖他人工作的风险。
最终检查
全部提交完成后运行:
git status --short --branch
git log --oneline -10
逐个检查本次创建的提交:
git show --stat --oneline <commit>
确认:
- 每个提交只有一个职责
- 每个提交可以独立审查、验证和回滚
- type、scope、语言和长度符合仓库规则
- 提交消息覆盖全部改动
- 没有意外文件或无关 hunk
- 没有未经允许的署名
- 剩余工作区改动符合预期
工作区干净只能证明文件已经被处理,不能证明提交拆分正确。
红色信号
出现以下想法时,停止暂存并重新检查职责:
| 想法 | 实际问题 |
|---|---|
| “这些都属于同一个功能” | 一个功能可能包含多个可独立回滚的行为 |
| “这些文件都在同一目录” | 目录结构不等于职责边界 |
| “它们使用同一个 scope” | scope 不能代替原子性判断 |
| “用户说提交全部改动” | 用户授权的是处理范围,不是合并方式 |
| “先全部暂存再检查” | 容易混入无关文件,应该逐个候选提交暂存 |
| “测试最后统一运行就行” | 每个提交必须有与其职责对应的验证 |
| “工作区已经干净” | 干净不能证明提交边界正确 |
| “文档和配置顺手一起提交” | 它们可能具有独立审查和回滚价值 |
| “拆得太细不好看” | 判断标准是职责完整性,不是提交数量 |
自动化相关请求
用户询问以下内容时:
- commitlint
- Husky
- Commitizen
- lint-staged
- 语义发布
- CI 提交消息校验
REQUIRED REFERENCE:读取 references/automation.md。