增量提交Skill incremental-commits

增量提交是一种软件开发技能,用于将涉及多个文件的更改分解为逻辑波次,每个波次对应一个原子提交,以创建清晰、可追溯的git历史。适用于重构、API变更和多文件功能开发。关键词:增量提交、git提交策略、原子提交、依赖管理、版本控制、软件开发、DevOps。

DevOps 0 次安装 0 次浏览 更新于 3/20/2026

name: 增量提交 description: 将多文件更改分解为按依赖顺序排列的原子提交。用于重构、破坏性API变更或涉及3+文件的功能。

增量提交

当功能涉及多个文件时,以波次方式实现。每个波次是一个逻辑关注点,一个提交。这创建了一个干净的git历史,讲述一个故事。

模式

波次1:基础(类型、接口)
  ↓
波次2:工厂/构建器(创建实例的函数)
  ↓
波次3:合约/API(使用类型的公共接口)
  ↓
波次4:基础设施(实用工具、转换器、依赖)
  ↓
波次5:消费者(应用、UI、集成)

并非每个变更都需要所有波次。一个简单的bug修复可能是一个波次。一个横切重构可能需要五个。

波次特性

每个波次必须:

特性 描述
原子性 每个波次一个逻辑关注点
可构建性 代码在此波次后编译(运行类型检查)
聚焦性 更改与一个层/关注点相关
完整性 波次内没有半途而废的工作

真实示例:模式重构

此功能将元数据从工作空间移动到表。五个波次:

波次1:类型

feat(schema): 添加IconDefinition、CoverDefinition和FieldMetadata类型

- 添加IconDefinition判别联合(emoji | external)
- 添加CoverDefinition判别联合(external)
- 在所有字段类型中添加带可选名称/描述的FieldMetadata
- 更新TableDefinition以使用icon/cover而不是emoji/order

文件:仅types.ts。一切的基础。

波次2:工厂

feat(schema): 在字段工厂函数中添加可选名称/描述

所有工厂函数(id、text、richtext、integer、real、boolean、date、select、tags、json)现在接受可选名称和描述参数。

文件:仅factories.ts。使用波次1的类型。

波次3:合约

feat(schema): 从WorkspaceSchema中移除emoji和description

Workspace现在只是一个包含guid、id、name、tables和kv的容器。
视觉元数据(icon、cover、description)现在位于TableDefinition上。

文件:仅contract.ts。使用新类型的API变更。

波次4:基础设施

feat(schema): 使用slugify生成人类可读的SQL列名

- 添加@sindresorhus/slugify依赖
- 添加使用slugify和'_'分隔符的toSqlIdentifier()辅助函数
- SQLite列现在使用field.name(或从key派生)而不是key

文件:to-drizzle.tspackage.json。使用字段元数据的实用工具。

波次5:消费者

feat(schema): 更新epicenter应用以使用TablesWithMetadata

- WorkspaceSchema现在接受TablesSchema | TablesWithMetadata
- 从包索引导出新类型
- 更新应用以创建带元数据的适当TableDefinition

文件:消费新类型的应用文件。

工作流

  1. 在编码前规划波次

    • 列出需要更改的文件
    • 按层/关注点分组
    • 按依赖顺序排序(基础优先)
  2. 实现一个波次

    • 仅对该波次进行更改
    • 抵制“再修复一件事”的诱惑
  3. 验证波次

    • 运行类型检查:bun run tsc --noEmit
    • 确保没有引入错误
  4. 提交波次

    • 使用常规提交格式
    • 消息描述此波次完成的内容
    • 正文可以列出具体更改
  5. 为下一个波次重复

何时使用波次

场景 使用波次? 原因
单文件bug修复 一个变更,一个提交
添加新类型+工厂 可能 可能是1-2个波次
跨5+文件的重构 需要逻辑分组
破坏性API变更 类型 → API → 消费者
添加依赖并使用它 基础设施波次然后使用波次

反模式

巨型提交

refactor: 更新模式系统

- 添加新类型
- 更新工厂
- 更改合约
- 添加slugify
- 更新应用

问题:一个整体提交。无法二分查找,无法部分回滚,没有故事性。

微提交

feat: 添加IconDefinition类型
feat: 添加CoverDefinition类型
feat: 添加FieldMetadata类型
feat: 更新IdFieldSchema
feat: 更新TextFieldSchema
...

问题:过于细粒度。一个逻辑更改有20个提交。噪音。

错误顺序

波次1: 更新应用以使用新类型  ❌
波次2: 添加类型                 ❌

问题:波次1无法编译。自底向上,而不是自顶向下。

依赖顺序启发法

决定波次顺序时,问:“这个文件导入什么?”

types.ts         → 不导入任何内容(基础)
factories.ts     → 导入types.ts
contract.ts      → 导入types.ts
converters.ts    → 导入types.ts,可能添加依赖
app/             → 导入以上所有内容

不导入任何内容的文件先来。导入所有内容的文件最后。

分支策略

对于多波次工作:

# 创建特性分支
git checkout -b feat/my-feature

# 波次1
# ... 进行更改 ...
git add <files> && git commit -m "feat(scope): 波次1描述"

# 波次2
# ... 进行更改 ...
git add <files> && git commit -m "feat(scope): 波次2描述"

# ... 继续波次 ...

# 完成后,所有波次都是分支上的独立提交
# PR显示特性如何演进的清晰历史

快速参考

开始前:

  • [ ] 列出所有需要更改的文件
  • [ ] 按层分组(类型、工厂、合约、基础设施、消费者)
  • [ ] 按依赖顺序排序

对于每个波次:

  • [ ] 仅更改此波次中的文件
  • [ ] 运行类型检查
  • [ ] 使用描述性消息提交
  • [ ] 移动到下一个波次

所有波次后:

  • [ ] 最终类型检查
  • [ ] 如果适用,运行测试
  • [ ] 创建具有干净提交历史的PR