19 版本控制
把代码、配置、文档和自动化纳入可追溯的版本链,使每个可发布状态都能定位、比较和恢复。
学习目标
- 能把一次发布拆成变更、原子提交、审查、发布标签和灾难恢复五个可复核节点
- 能用差异、提交元数据与标签证明某个发布状态由哪次决定引入,并指出证据的缺口
- 能在干净环境重建一个历史版本,解释单故障出现时的首个拒绝点与恢复动作
为什么这一单元不可压缩
一次“保存代码”的动作,常常还包含配置、数据库迁移、文档、构建脚本和发布说明。如果这些东西没有共同的历史边界,团队只能看到“现在是什么”,却无法回答“谁在什么时候改变了什么”“这个线上状态能否重新得到”。本单元把这些问题压缩成一条可观察的发布链:每一步都留下对象、责任人、输入、输出和拒绝条件。
本页依据 David Thomas、Andrew Hunt《程序员修炼之道:通向务实的最高境界(第2版)》,云风译,电子工业出版社,2020年4月,ISBN 9787121384356的公开完整中文目录,独立重构 19 版本控制 与 提示28:永远使用版本控制。课程不复制原书正文、插图、练习答案或代码,而把目录命题转写为新的因果模型、交互实验、故障样本与复核证据。
先看问题:能找到“现在”不等于能恢复“过去”
设想一个发布在周五晚上失败。工程师手里有一个压缩包、一条聊天消息和一台没有同步的构建机;服务也许能被临时修好,但没人能确认修复是否包含了数据库迁移,或者这个状态以后还能否再次构建。真正的验收问题不是“仓库里有没有文件”,而是“从一个明确入口出发,另一位工程师能否得到同一个状态”。
先预测:如果保留了发布标签,却删除了提交元数据,链路中的哪一站最先应该拒绝?点击下面实验台的“注入单故障”,再用单步推进核对你的预测;如果答案只停在“最终构建失败”,说明中间的审查证据没有被当成门槛。
常见误区
19 版本控制:把状态变成可定位的历史
本单元的核心是 ↡记录一组相关对象的历史状态、变更关系和恢复入口,使团队能够比较、审查并重建过去的状态。。它的边界不只是源码目录:配置、迁移、文档、测试夹具和自动化脚本,只要会影响可发布结果,就需要进入同一套可定位的历史。
一条可靠的链从变更开始,经由 ↡只包含一个可解释目的、并且能够独立验证或回退的一组历史变更。 保留最小决定单元,再以 ↡把变更、作者、时间、审查决定、构建结果和发布入口关联起来的证据关系。 连接审查。通过后使用 ↡指向一个已验证提交的稳定发布入口;它必须与提交身份和产物摘要一起记录。 公开可重建的版本,最后以 ↡在原环境不可用时,从受控历史、依赖和工件重建并验证发布状态的过程。 检验这条链是否真的有用。
下面的图把五个节点和它们传递的对象画在同一条线上。先注意一个细节:标签不是恢复本身,它只是一个入口;恢复还需要提交对象、构建输入和验证结果。
变更与原子提交:先保留决定,再保留噪声
工作树中的差异是观察材料,不是发布证据。一个好的提交应该能用一句话说明“为了哪个目的改变了什么”,并能独立运行针对性检查。例如,“增加订单超时策略”可以包含实现、测试和配置,但不应顺便带入未相关的格式化或个人调试输出。
提交记录至少要保留作者、时间、父提交、差异范围和验证命令。可以把提交对象理解成一张带身份的快照,而不是一个会自动告诉你意图的备份文件;意图仍要由提交说明、代码审查和测试结果共同解释。
本章的第六个术语是 ↡带有唯一身份并指向父历史、内容和元数据的提交记录;它让一组差异能够被定位、比较和重新引用。。提交对象丢失时,文件也许还在,但“这是谁、基于什么、为何发布”的关系会断开。
审查与发布标签:让发布入口指向事实
审查不是在发布之后补一条“看过了”的评论,而是确认差异、验证结果和风险决定是否组成了一个可交接的证据包。审查者至少应能从提交身份找到改动范围、关联需求、测试结果和未覆盖边界;不能解释的变化应该阻止标签生成。
发布标签的命名可以采用 v1.4.0 这样的约定,但名字本身不是事实。发布记录还应写明提交对象、构建工具版本、配置来源、产物哈希和回退命令。若标签可以被悄悄移动,原发布的可追溯性就已经降低;更安全的做法是创建新的标签或发布记录,并让旧入口保持可读。
提示28:永远使用版本控制
提示28的可执行解释是:凡是会影响结果的东西都应进入能被比较和恢复的历史,且操作边界要小到足以审查。使用版本控制并不等于无条件保存每一个临时文件;它要求团队明确哪些输入属于发布合同,哪些生成物可由固定输入重建,哪些秘密必须由受控配置管理而不是直接提交。
对一个发布事件,可以保留如下最小记录:
release:
change: "add order timeout policy"
commit: "8f31c2a"
review: "approved with integration-test evidence"
tag: "v1.4.0"
artifacts: "sha256:..."
recovery: "clean-room rebuild verified"这个记录的价值在于连接决定和结果,而不是增加格式。若 review 缺失,标签不能自动变成可信发布;若 artifacts 缺失,构建机上重新生成的包也不能证明与线上包相同;若 recovery 从未演练,所谓备份仍然只是待验证的假设。
三步观察:从变更走到恢复
每一步都重复使用本章图示,但关注点不同。动手前先写下你预计的首个拒绝点;图中高亮只表示当前教学位置,不会替你证明真实仓库已经满足条件。
1. 冻结变更边界
选一个真实但可控的发布切片,列出源码、配置、迁移、文档、测试和自动化输入。把“要改变什么”和“明确不改变什么”写成审查条件,再观察链路的第一站。
运行实验:故障必须在首个缺口处暴露
下面的实验台只改变一个条件:删除提交元数据。正常样本会沿五个节点闭合;故障样本可以继续显示标签名,却必须在审查处暴露“无法证明它指向什么”。点击播放或单步,再点击“重置版本控制实验台”比较两条轨迹。
当前:第 1 步 · 变更:先确认工作树中的对象和边界
第 1 / 5 步 · 变更:先确认工作树中的对象和边界
先猜首个拒绝点,再用单步或播放验证。
回顾:版本历史是交接协议
- 版本控制把源码、配置、文档和自动化放进可比较的历史边界。
- 原子提交让一个决定可以被审查、独立验证和安全回退。
- 可追溯性把差异、作者、审查、构建和发布入口连成证据链。
- 发布标签提供稳定入口,但不可替代提交身份、产物摘要和恢复演练。
- 灾难恢复的验收结果是在干净环境重建,而不是备份任务显示成功。
完成本章不等于记住一个 Git 命令;你应能指出某个发布状态的来源、边界、拒绝条件和恢复路径,并让另一位工程师用同一份证据重放结论。
练习:把“永远使用”变成可检验动作
练习
问题 1:原子提交边界。 下面四项改动同时出现在一次发布中:订单超时策略、全仓格式化、依赖升级和调试日志。请设计拆分方案,并说明每个提交的验证与回退入口。
问题 2:标签与可追溯性。 标签 v1.4.0 仍然存在,但它指向的提交对象已无法从镜像中找到。发布是否可以继续?请写出拒绝原因和恢复动作。
问题 3:灾难恢复演练。 在实验台中注入单故障,然后写出一份四行证据:正常、故障、修复、重置。每行必须包含首个差异、拒绝原因或恢复结果。
名词解释
名词解释
本章出现的专业名词,用大白话再讲一遍。
- 版本控制
记录一组相关对象的历史状态、变更关系和恢复入口,使团队能够比较、审查并重建过去的状态。
- 原子提交
只包含一个可解释目的、并且能够独立验证或回退的一组历史变更。
- 可追溯性
把变更、作者、时间、审查决定、构建结果和发布入口关联起来的证据关系。
- 发布标签
指向一个已验证提交的稳定发布入口,必须与提交身份和产物摘要一起记录。
- 灾难恢复
在原环境不可用时,从受控历史、依赖和工件重建并验证发布状态的过程。
- 提交对象
带有唯一身份并指向父历史、内容和元数据的提交记录,让一组差异能够被定位、比较和重新引用。
来源与改写范围
- 作者与英文版页面:核对 20 周年版书名、版本和 Topic 范围。
- 中文公开目录:核对“19 版本控制”和提示28的目录坐标。
- Git 官方书籍:Git 基础: 核对提交对象、历史比较、标签与恢复语义的工具事实边界。
- Git 官方文档:git-tag:核对标签指向提交和发布入口的命令语义;课程模型、案例、交互和练习均为独立改写。