本课程由多个顺序开发的章节组成。后一章继承上一章的完成状态,同时保留独立、可构建的工程目录。Git 不只是保存最终代码,还负责固定章节起点、隔离先行验证与教学实施、标记读者里程碑、生成差异补丁,并把完成状态可靠地交给下一章。
本附录以第三章 integrating_simulink_codegen 为贯穿案例,展示以下完整循环:
| |
这里同时存在两种真实性:verify 记录技术路线如何被证明,milestones 记录课程如何被逐步实施。两条历史不必相同,但最终受版本控制的内容必须一致。
1. Git 管理的不是文件夹副本,而是项目状态
1.1 Commit:不可变的项目快照
一个 commit 记录整个受版本控制目录树、父 commit 以及提交说明等元数据。Commit 创建后不会原地改变;后续修改会形成新 commit。
因此,“从 ch02 终点开始”必须对应一个确定的 commit,而不能只依赖某台机器当前目录的外观。
人类排错/高级操作:确认当前提交身份
| |
HEAD 表示当前检出的 commit。git rev-parse HEAD 输出的哈希值是精确的版本身份,可用于课程版本地图、验证记录和问题复现。
1.2 Branch:会向前移动的 commit 名称
Branch 不保存另一份物理源码,只是指向某个 commit 的引用。分支产生新提交后,引用向前移动。
| |
两个分支共享祖先 B,随后保留不同形成过程。切换分支时,Git 按目标 commit 更新同一个工作树,而不是进入另一份仓库。
1.3 HEAD、暂存区与工作树
Git 日常操作同时涉及三层状态:
| |
git diff比较工作树与暂存区;git diff --cached比较暂存区与HEAD;git add <path>把选定内容放入暂存区;git commit用暂存区创建新快照。
人类日常操作:提交前检查三层状态
| |
Pre-commit hook 检查暂存区,因为它需要回答“下一次提交将包含什么”,而不是只看磁盘上有哪些修改。
1.4 Worktree:实际编辑文件的检出目录
仓库默认包含一个主工作树。git worktree add 可以建立 linked worktree,让多个分支同时出现在不同目录;但同一个本地分支通常不能同时检出到两个工作树。
本课程按顺序开发章节,只使用以下根工作树:
| |
Verify、milestones 和 main 在同一目录中按阶段切换。这样 Codex、VS Code、终端和 CMake 始终面对同一个工作目录。
人类排错/高级操作:检查分支是否被额外 worktree 占用
| |
出现“branch is already used by worktree”时,应先定位对应工作树和未提交内容。不得把 linked worktree 放入 build/,也不得在未知状态下直接删除其目录。
1.5 Sparse checkout:减少可见内容,不改变历史
Sparse checkout 只控制工作树展开哪些路径。未展开的章节仍存在于 Git commit 和 refs 中,并未被删除。
人类排错/高级操作:只展开第三章与治理目录
| |
Cone 模式会同时保留仓库根目录文件。Sparse checkout 用于减少视觉干扰,不是权限系统;章节保护还需要分支约定、修改范围检查和 pre-commit hook。
2. 阶段一:把 ch02 完成态固定为 ch03 的来源
第三章起点来自 main 上已经验收的 ch02 完成态。开始之前,根工作树必须没有未提交内容;否则分支切换可能失败,或者把不属于治理基线的文件混入 B。
人类日常操作:确认 main 和工作区状态
| |
成功判据:git status --short 没有输出,最近提交是已经验收的 ch02 完成态。
如果工作区不干净,应先判断改动归属。属于有效工作的内容应在原分支提交;临时内容可以建立明确命名的 stash。不得为方便切换而执行 git reset --hard。
人类排错/高级操作:临时保存尚不能提交的修改
| |
Stash 是临时状态,不是课程里程碑。恢复后仍需整理为正常提交。
2.1 建立治理基线 B
治理基线只登记新章节路径、分支职责、写入范围和课程状态,不包含 ch03 最终技术实现。
完成治理文件修改后,在 main 创建基线提交:
人类日常操作:提交 ch03 治理基线
| |
最后一个命令输出的 commit 即图中的 B。本课程不要求额外建立名为 B 的长期分支;B 是被版本地图记录的确定 commit。
状态变化如下:
| |
3. 阶段二:建立 verify 并搬运上一章终点
3.1 从 B 创建 verify
Agent/CI 验证:建立第三章先行验证分支
| |
成功判据:当前分支为 codex/ch03-verify,最后一条命令退出码为 0。
3.2 使用 Git 导出确定的 ch02 工程树
直接从当前可见目录复制 ch02,可能把构建输出、旧日志或未提交修改一起带入新章。git archive 只导出指定 commit 中已经受版本控制的目录树,更适合建立可审计起点。
以下示例从 main 指向的基线 commit 中读取 ch02 工程,不要求 sparse checkout 长期展开 ch02。
Agent/CI 验证:导出 ch02 完成态并建立 ch03 工程目录
| |
这一步的 Git 输入是 commit B 中的 ch02 子树,输出是 ch03 目录中的普通文件。Git 不保存“复制关系”;下一次 commit 只会记录新目录树。因为 ch02 原目录仍然存在,git diff 通常把这些文件显示为 ch03 新增文件,而不是 rename。
Agent/CI 验证:确认来源树和新目录状态
| |
第一条命令列出 commit 中的真实来源,第二条命令检查归档内容,第三条命令显示新目录尚未提交的状态。
3.3 完成当前章工程身份重命名
独立化至少覆盖:
- 工程目录;
.ioc文件名与 CubeMXProjectName;- CMake target 与 preset;
- VS Code task;
- Host test 工程身份;
- ELF、MAP、HEX 等产物名;
- 工程宏、脚本参数和文档路径。
重命名后,ch03 不得在 Configure、Build、测试或生成过程中读取 ch02 目录。
人类日常操作:检查并提交 verify 起点
| |
成功判据:提交只包含 chapters/ch03/**,新工程能够独立 Configure 和 Build,旧机器日志与构建输出没有进入暂存区。
4. 阶段三:在 verify 中证明完整技术路线
Verify 用于尽早消除技术不确定性。Agent 可以在这里执行环境发现、自动化构建、接口验证、测试和证据采集,也可以保留必要的失败与修正提交。
Agent/CI 验证:观察 verify 独有历史
| |
第一条命令显示 verify 在 B 之后增加了哪些 commit;第二条命令显示 verify 完成态相对基线改变了哪些文件。
Verify 提交应按真实技术问题划分,例如:
| |
自动化成功只证明技术入口成立,不代表人工已经理解或完成 GUI 操作。因此 verify 走通后,不能把这些提交重命名为教学里程碑。
5. 阶段四:从 B 重新建立 milestones
5.1 从 main 而不是 verify 创建教学分支
Verify 完成并提交后,工作区必须恢复干净状态。
人类日常操作:确认切换条件
| |
成功判据:第一条命令没有输出,第二条命令对应已经走通的 verify 完成态。
随后从仍指向 B 的 main 创建 milestones:
人类日常操作:创建教学里程碑分支
| |
人类排错/高级操作:不得从 verify 创建 milestones
| |
从 verify 创建 milestones 会继承 Agent 的探索历史,无法证明教学步骤确实从治理基线重新实施。
5.2 重新建立并提交 m0
在 milestones 分支重复“从 ch02 commit 导出、建立 ch03 独立工程、完成技术身份重命名”的操作。重复实施不是浪费:它证明章节起点可以从 Git 中确定性重建。
m0 必须满足:
- 已继承 ch02 正式完成态;
- 已完成 ch03 工程身份重命名;
- 能够独立 Configure 和 Build;
- 尚未包含 ch03 最终教学实现;
- 不含旧机器日志、构建输出和历史硬件证据;
- 不依赖
chapters/ch02作为构建输入。
人类日常操作:提交并固定 m0
| |
Tag 通常不会随新提交移动,适合固定读者里程碑;branch 会继续向前移动,适合持续开发。
5.3 让 teaching 分支精确指向 m0
教学入口不需要检出到第二个 worktree。可以直接建立一个未检出的 branch 引用:
人类日常操作:建立教学入口
| |
两个 rev-parse 输出必须相同。若 teaching 分支已经存在,应先检查它的当前指向;不得用 git branch -f 掩盖未知状态。
6. 阶段五:人工与 Agent 协同形成 m1 到 mN
每个 milestone 都是一个可理解、可验证、可暂停的教学节点。每个节点同时保留人工入口和 Agent/CI 入口,两者使用同一份源码和配置。
人类日常操作:形成一个教学里程碑
| |
一个 milestone 的文档至少回答:
- 本步建立什么能力;
- 人工从哪个 GUI 或短命令入口开始;
- Agent/CI 从哪个确定性入口验证;
- 会修改和生成什么;
- 成功判据和产物位置;
- 在哪里暂停并检查结果。
Milestones 由人工与 Agent 协同形成,可能发现 verify 中没有暴露的接口、命名、GUI 顺序或验证问题。此时 milestone 内容暂时领先 verify 是正常状态,但这种分叉必须在本章验收前收敛。
7. 阶段六:把 milestone 改进同步回 verify
Git 提供多种同步手段。选择依据是改进如何形成,而不是追求统一命令。
7.1 Cherry-pick:同步边界清楚的独立提交
当某个 milestone commit 只包含一项可独立复用的修正时,可以把它重放到 verify:
Agent/CI 验证:检查并同步独立改进
| |
Cherry-pick 会创建新的 commit;新 commit 与原 commit 内容可能相同,但哈希通常不同,因为父提交和元数据不同。
7.2 收敛 patch:同步两棵完成态目录树的差异
当 milestone 改进跨越多个教学提交,而 verify 需要一个集中同步提交时,可以生成从 verify 到 milestones 的 patch。
Agent/CI 验证:生成内容收敛 patch
| |
git diff A B 描述把状态 A 变成状态 B 所需的修改,因此这里的方向不能颠倒。
Agent/CI 验证:预检、应用并提交同步
| |
git apply --check 只验证 patch 能否干净应用,不修改文件;检查通过后再使用 --index 同时更新工作树和暂存区。
7.3 为什么不直接合并 milestones
git merge course/ch03-milestones 会把完整教学历史并入 verify,表达“两条历史在这里汇合”。但本课程需要保留 verify 的探索叙事和 milestones 的教学叙事,因此通常采用 cherry-pick、收敛 patch 或按 verify 粒度重新实施。
Merge 不是技术上错误,而是多数情况下不符合这里需要表达的历史语义。
7.4 验证“内容相同、历史不同”
Agent/CI 验证:比较完成态内容和 commit 身份
| |
前两个哈希可以不同;最后一条命令必须以退出码 0 结束。该结果表示两个 ref 的受版本控制目录树一致,并不表示历史相同。
8. 阶段七:生成相邻教学里程碑 patch
git diff A B 可以保存为 patch,描述两个教学状态之间的文件变化。Patch 用于准确参考、排错和自动化重放,不替代人工操作。
Agent/CI 验证:生成 m0 到 m1 的课程 patch
| |
二进制差异通过 --binary 保留。使用 --output 而不是 PowerShell 文本重定向,可避免旧版 PowerShell 编码改变 patch 内容。
每个 patch 的说明至少记录:
- 来源与目标 milestone;
- 涉及文件;
- 教学目的;
- 人工操作入口;
- Agent/CI 验证入口;
- 成功判据与产物位置;
- 完成后的暂停点。
git diff patch 主要配合 git apply,描述文件内容变化;git format-patch 生成的邮件格式 patch 主要配合 git am,还携带作者和提交说明。相邻教学状态强调“发生了哪些内容变化”,因此采用 git diff patch。
人类排错/高级操作:在干净起点预检课程 patch
| |
Detached HEAD 适合临时检查固定 commit,但不适合持续开发。测试结束后应切回明确分支。
9. 阶段八:验收本章并推进 main
最终课程历史采用 milestones 轨道,因为它记录人工与 Agent 实际形成的教学顺序。Verify 提供独立的技术证明,不直接合入 main。
推进前必须满足:
- milestones 已到达
mN; - verify 与
mN的受控内容一致; codex/ch03-teaching仍指向m0;- 所有里程碑 tag 已记录;
- 工作区干净;
- 版本地图和章节 README 已更新。
Agent/CI 验证:最终引用与内容检查
| |
人类日常操作:将 main 快进到教学完成态
| |
Fast-forward 不创建额外 merge commit,只把 main 指针沿现有 milestones 历史向前移动。--ff-only 会在历史意外分叉时停止,避免自动制造未经设计的合并。
| |
此时 main 是 ch03 的累计完成态,也是 ch04 唯一正式起点。
10. 用 Hook 和检查命令保护章节边界
当前分支决定唯一技术写入根:
| |
仓库把 hook 版本化到 .githooks/,再通过以下配置启用:
人类日常操作:启用并检查仓库 hook
| |
Pre-commit hook 从当前分支解析 chXX,再检查暂存路径。新增章节时复用同一套逻辑,不为每章复制脚本。
Agent/CI 验证:提交前范围检查
| |
本地 hook 是快速反馈入口,不能成为唯一防线。CI 应执行同等的暂存路径或 commit 差异范围检查。
11. 常见 Git 问题与安全处理
分支切换被未提交修改阻止
先运行 git status --short,确定改动属于有效工作、临时实验还是构建输出。有效工作应提交到正确分支;临时实验可使用明确命名的 stash;构建输出应进入忽略目录。
文件被错误加入暂存区
人类排错/高级操作:只取消暂存,不丢弃工作区内容
| |
这不会删除磁盘上的修改,只把文件移出下一次提交快照。
分支已被另一个 worktree 占用
运行 git worktree list,检查实际目录、分支和未提交内容。只有在确认 linked worktree 已不再需要且工作区干净后,才设计移除步骤。本课程的正常开发流程不创建 linked worktree。
Patch 无法应用
先确认当前 commit 是否为 patch 声明的来源里程碑,再运行 git apply --check。Patch 应用失败通常表示起点不一致、相同区域已经修改,或者生成方向颠倒。
Verify 与 milestones 比较仍有差异
Agent/CI 验证:逐层缩小差异范围
| |
差异必须被同步或明确排除后才能推进 main。机器日志和构建输出应位于忽略目录,不应参与受控文件树比较。
12. 每章都重复同一个 Git 闭环
章节编号和工程内容会变化,Git 闭环保持不变:
main固定上一章已验收完成态;- 在
main创建当前章治理基线B; - 从
B建立 verify,并用 Git 导出上一章受控工程树; - Verify 先行证明完整技术路线;
- 从
B重新建立 milestones 和干净m0; - 人工与 Agent 协同形成
m1到mN; - Milestone 改进同步回 verify;
- Git 验证两条轨道内容一致;
- 生成相邻里程碑 patch;
main以 fast-forward 前进到mN。
这套流程让 Git 同时承担四种职责:版本数据库、章节边界、教学状态机和复现工具。最终结果不仅是一个可以构建的工程,也是一条可以审计、学习、验证和继续继承的课程历史。