Written with Claude.
理解 Git 不应止步于记住命令。本文记录了两个动手实验——把 .git 目录的对象存进数据库、用 JS 重新实现 Git 核心操作——以此真正搞清楚 Git 在保存什么、操作什么。第二部分结合团队实际诉求,设计了一套基于 Git Flow 的分支管理方案。
原理理解
将 .git 存储到数据库(git-db)
Git 的存储本质是一个内容寻址的 KV 数据库,所有对象(blob / tree / commit / tag)都以 SHA-1 为 key,以 zlib 压缩后的二进制为 value。这个实验将 .git/objects 下的所有对象导入关系型数据库,直观地验证了这一点:每一次 git add 产生一个 blob 对象,每一次 git commit 产生一个 commit 对象和若干 tree 对象,分支和 HEAD 不过是指向某个 commit SHA-1 的指针文件。理解这一层之后,Git 的大多数"魔法"行为都变得可以推导。
用 JS 重新实现 Git(git-js)
在理解存储结构之后,用 JavaScript 重新实现 Git 的核心操作(init / add / commit / log / checkout),目的是验证对操作语义的理解是否准确。这个过程中最重要的收获是:Git 的"分支切换"本质上只是修改 HEAD 指针 + 用目标 tree 对象还原工作区,与任何网络、服务端都无关;而 merge 的核心是找到两个 commit 的公共祖先,再做三方合并。
分支管理
主流工作流对比
分支管理策略大体分为两类:Trunk Based Development 和 Git Flow。
- Git Flow:维持
master和develop两个长期分支,分别对应生产和测试环境。多版本场景下可从对应节点再建新的主分支并行推进。节奏稳健,适合有明确发布窗口的团队。 - GitHub Flow:主干持续发布,feature 分支直接合入 main 并部署。是 Trunk Based 的精华版,对团队整体工程能力要求高,不适合多版本并行管理。
- GitLab Flow:GitHub Flow 的演进版,在主干之外增加环境分支(如
production、staging),主干代码向下游环境分支合并。相比 GitHub Flow 更适合有多套环境的团队,但对合并纪律要求较高。
代码管理诉求
- 按窗口(功能清单)发布、单一生产版本
- 支持多版本并行开发
- 尽可能保留所有 commit
操作方案:基于 Git Flow
多版本并行开发策略
多版本并行开发通过以下约束保证代码继承关系的正确性:
核心规则:所有测试分支不得落后于 master
无论当前有多少个版本在并行开发,每个测试分支在提 MR 合入时,必须已将最新的 master 内容 merge 进来。这一约束确保了:
- 任何一个版本上线后,其他版本的测试分支都不会丢失已上线的内容
- 版本间的代码继承关系通过这条规则自然保证,不需要额外维护分支间的同步逻辑
执行时机:同步要求在提 MR 合入测试分支时即生效,而非上线前才处理,避免冲突在打包阶段集中爆发。
CR 注意事项:feature 分支 merge master 后,MR 的 diff 中会包含与本次功能无关的内容。开发者须在 MR 描述中注明是否包含 master 同步,方便 reviewer 聚焦在本次功能变更上,避免 CR 流于形式。
分支命名
master:最新 commit id 等价于当前生产版本develop:单版本场景下的测试主分支develop_vX:多版本并行时各版本独立的测试分支,对应独立的测试环境release_日期:窗口裁剪的标准路径,当测试分支上同时存在多个功能但本次只上线部分时使用
代码提交
所有分支合入测试主分支前须经过 Code Review。通过拓宽 CR 参与人数来分散审查压力,保证代码质量。feature 分支合入时禁止 squash merge,保留完整的 commit 历史。
打包上线
若存在 release 分支则以 release 为打包源,否则以本次测试分支为打包分支。打包产物的后缀 commit id 即为 master 合并与 tag 的目标节点。
master 分支只允许 --ff-only 合并,确保其仅作为映射指标存在——所有 commit 均来源于经过复核的测试主分支。
典型生命周期(单测试分支场景):
feature → develop → (build & verify) → master (ff-only) + tag
平台配置
以下规则须在 GitLab/GitHub 分支保护里显式配置,不依赖约定:
| 规则 | 作用 |
|---|---|
develop_* 只允许 merge commit,禁止 squash |
保留所有 commit 历史 |
master 只允许 ff-only |
保证 master 是线性映射 |
feature 分支合入 develop_* 必须通过 MR + CR |
保证上线质量 |
develop_* 不允许直接 push,必须走 MR |
防止绕过 CR |
最终效果
master与 tag 共同构成所有历史发布版本的完整索引,每个 tag 对应一次上线的代码与变更内容- 打包的代码一定包含
master的全部提交(Jenkins 增加检查脚本保障) - 所有上线代码均经过 Code Review
- 最小化 commit 在不同分支间的流动:仅有单测试分支时,整个流程只存在测试分支向
master的一次合并
这套方案的核心取舍是:以 Git Flow 的双主干结构换取发布安全性,以 --ff-only 合并保证 master 的可追溯性,以 CR 门禁换取上线质量。对于单一生产版本、按窗口发布的团队,这是目前成本最低、风险最可控的路径。
最后修改于 2024-09-30