第2章 手工修改并提交第一个可验证版本

第1章取得并运行了智能声音识别通知器的稳定基线。本章继续使用同一个仓库,由学生手工调整通知冷却时间,并使用真实输入验证修改前后的行为。

本章仍不使用AI工具。全部操作由学生完成,目的是建立一条可以直接观察的工程链路:

从确定的基线建立分支
→ 手工修改一个产品参数
→ 检查工作区差异
→ 重新构建和烧录
→ 用正例、反例和时间边界验证
→ 暂存合格变化
→ 再次检查
→ 完成第一次提交

一些版本控制工具直接把工作区变化写入版本历史。Git在工作区与提交之间设置暂存区,使开发者能够先组成待提交快照。这样可以从多项变化中选择一个完整意图,但也增加了一种需要辨认的状态。

本章把Git的工作区、暂存区和提交放入一次真实修改中讲解。完成后,应当能够:

2.1 从稳定标签建立练习分支

2.1.1 确认仍在第1章基线

进入课程仓库:

Set-Location D:\course\s1\sound-event-terminal

检查标签、提交号和工作区:

git describe --tags --always
git rev-parse HEAD
git status --short

应满足三个条件:

  1. 当前标签为环境卡规定的 sound-lab-v0.1-baseline;
  2. 完整提交号与第1章运行记录一致;
  3. git status --short没有输出。

如果第1章结束后项目文件已经变化,应先使用 git diff查看原因。没有确认差异前,不要建立分支或覆盖文件。

2.1.2 建立个人分支

执行:

git switch -c lab/ch02-event-policy

检查当前分支:

git branch --show-current

输出应为:

lab/ch02-event-policy

分支是指向某次提交的可移动引用。建立分支不会复制一套文件,也不会立即产生新提交;它只是为后续变化建立一个独立的发展位置。

再次执行:

git log -1 --oneline

此时新分支仍指向第1章基线。分支名称变化了,源代码内容尚未变化。

2.2 明确本次产品变化

2.2.1 通知冷却解决什么问题

声音识别模型会连续处理音频窗口。同一个持续声音可能在多个连续窗口中都得到较高输出。如果每个窗口都立即发送通知,用户可能在几秒内收到多条重复消息。

通知冷却时间规定:

一次事件通知成功形成后,在规定时间内,即使模型再次满足触发条件,也不重复产生同类通知。

课程基线使用30 s冷却。本章为了在课堂内重复验证,将其调整为10 s。修改仅影响事件规则,不改变:

这组“不改变的内容”定义了本章范围。修改范围越明确,后续越容易判断差异是否合格。

2.2.2 写下验收条件

动手前先写出能够观察的结果:

编号 输入与时序 预期结果
A 第一次有效目标声音 产生通知
B 第一次通知后5 s再次输入 不产生第二次通知
C 第一次通知后超过10 s再次输入 允许产生第二次通知
D 输入反例声音 不因本次修改产生目标事件

还要满足代码范围条件:

验收条件决定后续验证方法,也是检查Git差异的依据。

2.3 找到冷却时间的实现位置

2.3.1 使用文本搜索定位

在VS Code中打开整个仓库,搜索:

cooldown

也可以在终端中执行:

git grep -n "cooldown"

git grep只搜索Git工作树中的文件,不会把 .git/ 内部对象和通常被忽略的构建目录混入结果。

课程基线把事件规则放在:

firmware/event_policy.h

其中包含类似配置:

struct EventPolicyConfig {
  float trigger_threshold = 0.90f;
  unsigned long cooldown_ms = 30UL * 1000UL;
};

trigger_threshold决定模型证据达到什么程度时进入事件判断,cooldown_ms决定事件产生后多久允许再次通知。两者属于不同产品规则,本章只修改后者。

2.3.2 阅读调用关系

继续使用:

git grep -n "EventPolicyConfig"
git grep -n "cooldown_ms"

确认配置在哪里创建、传给哪个对象,以及事件策略怎样使用它。典型调用关系为:

主程序创建EventPolicyConfig
→ EventPolicy保存配置
→ update()接收模型输出和当前时间
→ 判断阈值、连续状态与冷却
→ 返回是否形成事件

如果搜索结果显示多个冷却参数,不要同时修改。先根据调用关系确定当前声音事件实际使用哪一个配置。

2.4 手工完成最小修改

把:

unsigned long cooldown_ms = 30UL * 1000UL;

修改为:

unsigned long cooldown_ms = 10UL * 1000UL;

保存文件后不要继续改动,立即查看Git状态:

git status --short

预期输出:

 M firmware/event_policy.h

左侧空格和 M 的位置有意义。M表示文件已经在工作区修改,但尚未进入暂存区。

2.5 检查工作区差异

2.5.1 查看指定文件

执行:

git diff -- firmware/event_policy.h

差异通常包含:

-  unsigned long cooldown_ms = 30UL * 1000UL;
+  unsigned long cooldown_ms = 10UL * 1000UL;

以 - 开头的行来自上一次提交,以 + 开头的行是当前工作区内容。它们不是需要复制进程序的符号。

一个合格差异应当只包含一处数值变化。若整个文件都显示变化,常见原因是换行符或格式化设置改变;若出现阈值、模型路径或其他文件变化,应先恢复无关内容。

2.5.2 查看全部差异和格式问题

继续执行:

git diff
git diff --check

第一条命令检查整个仓库的未暂存变化,防止遗漏编辑器自动修改的文件。第二条命令检查常见空白问题,例如行尾多余空格。git diff --check没有输出,只能说明没有发现这类格式问题,不能证明程序逻辑正确。

2.5.3 工作区是什么

当前看到和编辑的项目目录称为工作区。修改文件以后,工作区与上一次提交出现差异,但Git尚未决定哪些变化属于下一次提交。

工作区允许同时存在多个试验。提交前必须主动选择其中合格的一部分,而不是把目录中所有变化一次性保存。

2.6 重新构建和烧录

2.6.1 构建修改后的固件

执行:

python tools/course.py build

保存构建结果,并与第1章基线对照:

项目 基线 本章修改后
构建是否成功
固件大小
RAM使用量
警告数量

冷却时间是一个常量,本次修改不应引起明显资源变化。如果固件大小或警告数量出现异常,应检查是否有其他文件变化或构建配置变化。

2.6.2 烧录和串口观察

使用实际串口:

python tools/course.py flash --port COM5
python tools/course.py monitor --port COM5

启动日志应显示当前策略配置,例如:

[POLICY] threshold=0.90 cooldown_ms=10000

这条日志能够证明设备实际运行的固件读取到了新值。若日志仍显示30000,应检查:

2.7 用时间边界验证修改

2.7.1 第一次有效事件

播放课程正例。记录模型输出、事件编号和通知结果:

T0:
模型输出:
事件编号:
通知状态:

只有当模型输出满足事件规则并产生第一次通知后,才能开始计时验证冷却。若第一次输入没有形成事件,应先排查输入或模型链路。

2.7.2 冷却期内的重复输入

在第一次通知后约5 s再次播放同类正例。记录:

距第一次事件:
模型是否再次满足条件:
事件策略输出:
是否再次通知:

理想结果是模型可以再次识别目标声音,但事件策略显示仍处于冷却期,不产生第二条通知。这样才能证明“未通知”来自冷却规则,而不是第二次输入没有被模型识别。

2.7.3 冷却期结束后的输入

在第一次通知后超过10 s,再次播放正例。若模型输出满足条件,应允许产生新的事件和通知。

验证时应给时间边界留出余量,例如在12 s后输入。恰好在10 000 ms边界上操作,容易受到播放延迟、串口显示和人工计时误差影响。第11章将使用自动测试精确检查边界值。

2.7.4 反例验证

播放反例声音。确认本次修改没有改变模型、阈值和类别关系。反例验证不能证明模型整体性能,只用于检查冷却参数修改没有扩大到其他模块。

2.8 形成验证记录

把四项验收条件填写为实际结果:

编号 实际输入 关键日志 通知结果 结论
A 第一次正例
B 约5 s后正例
C 超过10 s后正例
D 反例

若某项未通过,不要进入暂存和提交。回到失败边界:

没有PCM或音频状态
→ 检查感知输入

有输入但没有模型输出
→ 检查特征与推理

模型输出满足条件但没有事件
→ 检查事件规则

有事件但通知失败
→ 检查网络和服务

一次提交应当保存已经通过规定验证的变化,而不是保存“正在试试看”的中间状态。

2.9 把合格变化放入暂存区

2.9.1 选择文件

验证通过后执行:

git add firmware/event_policy.h

再次查看状态:

git status --short

预期输出:

M  firmware/event_policy.h

此时 M位于左列,表示修改已经进入暂存区。暂存区保存“下一次提交准备包含的项目状态”。

不要在本章使用:

git add .

虽然它能够一次加入多个文件,但不利于学生观察自己究竟选择了什么。明确写出文件路径能够降低把日志、凭据或无关改动带入提交的风险。

2.9.2 检查即将提交的差异

执行:

git diff --staged
git diff --staged --check

git diff检查工作区中尚未暂存的变化,git diff --staged检查暂存区相对于上一次提交的变化。提交前最重要的是第二条,因为它显示即将进入新提交的内容。

本章应确认:

最后一项使用:

git diff

确认没有输出。

2.10 完成第一次提交

2.10.1 编写提交信息

提交信息应当让后来者在不打开全部差异时,理解这次变化的目的。本章采用:

git commit -m "feat(policy): 将通知冷却时间调整为10 s"

这条信息包含:

第4章将系统讲解原子提交和提交信息规范。本章先遵守三个基本要求:

  1. 一次提交只说明一项变化;
  2. 描述具体对象和结果;
  3. 不使用“更新”“修改一下”“完成作业”等无信息量表达。

2.10.2 检查提交结果

执行:

git log -1 --oneline
git show --stat HEAD
git show HEAD

依次确认:

再执行:

git status --short

没有输出表示工作区和暂存区都与当前提交一致。

2.11 Git怎样保存这次变化

有些版本工具把“修改了哪些行”作为主要观察对象。Git的基本对象则是项目快照。每次提交都指向一组确定的文件状态,并记录它与父提交的关系。行级差异是比较两个快照后得到的结果。

暂存区位于工作区与提交之间,使开发者能够决定哪些变化进入下一次快照。工作区可能同时包含试验、调试和正式修改。一次提交只应保存其中一个完整意图。暂存区增加了一次选择,也要求提交前再次检查。

本章实际经历了三个状态:

工作区修改
    git add
        ↓
暂存区选择
    git commit
        ↓
版本库中的新提交

Git提交保存的是项目状态和相关元数据,包括父提交、作者、时间和提交信息。新提交以第1章基线为父提交,因此项目历史形成一条连续路径:

sound-lab-v0.1-baseline
        ↓
feat(policy): 将通知冷却时间调整为10 s

差异是比较两个状态得到的结果,而不是Git只保存了“一行修改”。理解这一点有助于后续学习分支、合并和回退。

2.12 在不同状态下恢复

恢复前必须先执行 git status 和 git diff,明确当前变化位于哪里。

2.12.1 修改尚未暂存

若只想放弃 event_policy.h 的工作区修改:

git restore firmware/event_policy.h

该命令会用暂存区中的版本覆盖工作区。未保存的本地修改会消失,因此执行前必须确认差异。

2.12.2 修改已经暂存但尚未提交

先把文件移出暂存区:

git restore --staged firmware/event_policy.h

文件修改仍保留在工作区。此时可以继续编辑,或再使用 git restore放弃修改。

2.12.3 修改已经提交

若提交尚未共享,初学阶段也不建议随意重写历史。可以先建立备份分支:

git branch backup/ch02-before-recovery

第4章将学习使用 git revert通过新提交撤销已经进入共享历史的变化。不要在不了解后果时使用 git reset --hard,也不要通过删除整个仓库回避状态判断。

2.13 常见问题

修改后 git diff没有输出

检查是否保存文件、是否位于正确仓库、文件是否被Git跟踪,以及修改是否已经暂存。已经暂存的变化需要使用 git diff --staged查看。

git status显示多个文件变化

逐个使用 git diff -- <文件>检查。只保留与本章目标有关的变化,编辑器配置和自动格式化问题应单独处理。

构建通过,但串口仍显示30 s

检查是否重新烧录、设备是否为当前串口、构建产物是否来自当前仓库。使用提交号和固件版本日志对照。

第二次没有通知

先确认第二次模型输出确实满足事件条件,再判断冷却。若模型没有识别到正例,本次输入不能用于证明冷却生效。

冷却期内仍重复通知

确认代码实际使用 cooldown_ms。检查事件时间记录是否在第一次事件后更新。还要检查同类事件是否共享同一个策略对象。

提交后发现带入无关文件

不要立即推送。先保留当前状态并向教师说明,使用第4章将讲解的提交修正或撤销方法处理。处理前不要删除证据。

2.14 本章小结

本章手工完成了第一项产品规则变化,并使用Git保存了经过验证的结果。核心过程是:

明确范围
→ 修改工作区
→ 检查差异
→ 构建和实机验证
→ 选择进入暂存区的内容
→ 检查暂存差异
→ 提交
→ 复查提交

Git不是在修改结束后补做的记录工具。状态检查和差异检查贯穿修改前、验证前、暂存后和提交后。学生由此能够说明起始状态、实际变化、验证依据和保存结果。

第3章将在这个干净提交上配置VS Code、Cline和校内Qwen服务。AI先读取限定文件,解释当前工程并提出修改计划。该章不实施代码修改,而是建立受控协同基础。Git差异和实机验证仍是判断AI成果能否进入仓库的依据。

2.15 综合实践

  1. 在不改变模型和网络协议的条件下,选择另一项可安全调整的事件参数。形成变更说明、保持不变项和不少于4个边界测试。
  2. 以本章提交为对象,画出工作区、暂存区和提交之间的状态图。每条转换注明命令、可观察现象和数据风险。
  3. 在个人分支设计3种恢复情境:未暂存、已暂存、已提交但未共享。执行前预测结果,执行后记录差异,并形成恢复建议。
  4. 根据一次策略验证形成分层证据报告。报告分别说明构建、模型输出、事件判断和用户通知能够证明什么。