在 Monorepo 架构中,由于所有项目或模块共享同一个代码库,清晰的 Git 提交记录至关重要。以下是一些保证提交清晰性的关键实践:


1. 提交信息规范化

  • 使用约定式提交(Conventional Commits)
    格式:<type>(<scope>): <subject>,例如:
    feat(ui): add login button
    fix(api): handle null response

    • type 表示变更类型(如 feat, fix, docs, style, refactor, test, chore)。

    • scope 可指定模块名(如 frontend, backend, config),帮助快速定位变更范围。

  • 工具支持:
    通过 commitlint + husky 在提交时自动校验信息格式。


2. 模块化提交(原子性提交)

  • 一个提交只做一件事:避免将多个无关的修改(如 UI 改动和 API 调整)混在同一提交中。

  • 按功能/模块拆分:通过 git add -p 交互式暂存,选择性提交文件中的部分变更。


3. 利用工具管理变更

  • Changesets:
    记录变更描述、版本影响(如 major/minor/patch),自动生成 CHANGELOG 和版本号。
    适合多包管理的 Monorepo(如 pnpm/Yarn workspace)。

  • Lerna:
    通过 lerna version 根据提交历史自动确定包版本。


4. 分支策略

  • 功能分支(Feature Branches):
    每个功能/修复单独分支,合并时通过 Squash Merge 压缩为单一提交,保持主线清晰。

  • 分支命名规范:
    例如 feat/login-button、fix/api-null-check,与提交信息的 scope 保持一致。


5. 代码所有权(Codeowners)

  • 通过 .gitignore 文件标记模块负责人,确保变更经过相关团队审核:

    packages/frontend/  @frontend-team
    packages/backend/  @backend-team

6. 可视化与工具辅助

  • 查看模块级变更:

    git log --oneline -- packages/frontend  # 仅查看前端模块提交
  • 图形化工具:
    使用 git log --graph 或 Tig 工具可视化分支和提交关系。


7. Monorepo 友好工具链

  • PNPM/Yarn/Nx:
    这些工具支持仅运行受影响的模块测试或构建(例如 nx affected:build),提交时关联变更范围更清晰。

  • 提交模板:
    在 .gitmessage 中预定义提交模板,提示开发者填写规范信息。


示例流程

  1. 开发者在 feat/user-profile 分支修改 packages/frontend 和 packages/api。

  2. 通过 git add -p 分别暂存前后端变更,提交两条信息:

    bash

    复制

    下载

    feat(frontend): add user profile page
    feat(api): add /user/profile endpoint
  3. 合并到 main 分支时使用 Squash Merge,生成一条清晰记录。


通过以上实践,Monorepo 的提交历史可以做到模块分明、目的明确,便于回溯、协作和自动化工具(如语义化版本)运行。

在 约定式提交(Conventional Commits) 中,scope 是一个可选的字段,用于明确指示代码变更的影响范围(通常是模块、包或功能区域)。它的配置主要依赖于 工具链支持 和 团队约定,而非 Git 本身的配置。以下是具体实现方法:


1. 手动维护 Scope 列表(团队约定)

  • 在项目的 CONTRIBUTING.md 或文档中定义固定的 scope 选项,例如:

    ## 允许的 Scope
    - `frontend`: 前端相关修改(Next.js 应用)
    - `backend`: 后端相关修改(NestJS 服务)
    - `config`: 配置文件或脚本变更
    - `ci`: CI/CD 流程修改
  • 开发者提交时根据实际修改选择对应的 scope。


2. 通过工具自动化校验(推荐)

使用工具(如 commitlint)强制校验 scope 的合法性:

步骤 1:安装依赖
npm install --save-dev @commitlint/cli @commitlint/config-conventional
步骤 2:创建 commitlint.config.js
module.exports = {
  extends: ["@commitlint/config-conventional"],
  rules: {
    "scope-enum": [2, "always", ["frontend", "backend", "config", "ci"]], // 定义允许的 scope 列表
    "scope-case": [2, "always", "kebab-case"], // scope 的命名格式(如 kebab-case)
  },
};
步骤 3:通过 Husky 添加 Git 钩子
npx husky add .husky/commit-msg 'npx commitlint --edit "$1"'
  • 当提交信息中的 scope 不在预定义列表中时,提交会被拒绝。


3. 动态生成 Scope(适用于大型 Monorepo)

如果 Monorepo 包含大量包(如 packages/*),可以通过脚本动态生成 scope 列表:

示例:基于项目目录自动生成 Scope
// commitlint.config.js
const fs = require("fs");
const packages = fs.readdirSync("packages"); // 读取 packages 目录下的所有模块名

module.exports = {
  extends: ["@commitlint/config-conventional"],
  rules: {
    "scope-enum": [2, "always", [...packages, "global"]], // 允许的 scope 包括所有子包 + "global"
  },
};

4. 与 Monorepo 工具集成

  • Changesets:
    在 .changeset/config.json 中定义 scope 关联的包:

    {
      "linked": [["packages/frontend", "packages/backend"]],
      "ignore": ["docs"]
    }
  • Lerna/Nx:
    通过 lerna ls 或 nx show projects 获取所有包名,作为 scope 的候选值。


5. IDE 插件辅助

  • VSCode 插件(如 Commit Message Editor)可提供 scope 的自动补全。

  • 结合 package.json 的 name 字段生成提示。


示例场景

假设 Monorepo 结构如下:

packages/
  ├── frontend/  # scope: "frontend"
  ├── backend/   # scope: "backend"
  └── shared/    # scope: "shared"

提交时:

git commit -m "feat(frontend): add dark mode toggle"
git commit -m "fix(backend): handle auth token expiry"

总结

  • 小型团队:手动维护固定的 scope 列表 + commitlint 校验。

  • 大型 Monorepo:通过脚本动态生成 scope(如读取 packages/* 目录)。

  • 开发者体验:通过 IDE 插件或工具(如 Changesets)降低记忆成本。

这样既能保证提交规范性,又能灵活适应项目规模的变化。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐