我们已经找到了首个坏提交。最后一节会修复配置、发布 v1.1.0,再查看提交内部引用的对象。之后从标签取得一个全新副本,检查版本、对象完整性和工作区状态。
确认二分过程已经结束:
git status输出开头应显示 On branch main。修复任务上限并运行检查:
printf 'max_tasks=10\n' > settings.conf
./scripts/check-settings.sh
printf '退出码:%s\n' "$?"结果:
退出码:0查看并提交修复:
git diff -- settings.conf
git add settings.conf
git commit -m "fix: restore positive task limit"创建新版本标签并发布:
git tag -a v1.1.0 -m "团队任务板 1.1.0"
git push origin main --tags检查当前提交正好被哪个标签指向:
git describe --tags --exact-match结果:
v1.1.0Git 的高层命令最终都在操作对象和引用。先询问 HEAD 的对象类型:
git cat-file -t HEAD结果:
commit再查看提交对象内容:
git cat-file -p HEAD结果示例:
tree a4bb2b08107c49a9024506f24693e364d72fa444
parent 764744f67c6dd743c43da93f761a87bf024cc3d9
author 小林 <xiaolin@example.com> 1784083742 +0000
committer 小林 <xiaolin@example.com> 1784083742 +0000
fix: restore positive task limit提交对象没有直接塞入所有文件内容。它主要记录:
tree,代表这次提交的目录快照;parent,连接历史;第一次提交没有父提交,普通提交有一个父提交,合并提交通常有两个父提交。
列出 HEAD 快照里的文件:
git ls-tree -r --name-only HEAD结果:
.gitignore
README.md
scripts/check-settings.sh
settings.conf
tasks.mdtree 保存目录结构、文件名、模式和子对象 ID;文件内容通常存为 blob。查看一个文件对应的对象:
git ls-tree HEAD settings.conf结果会类似:
100644 blob 0d9f... settings.conf把输出中的对象 ID 交给 cat-file:
git cat-file -p <blob-id>会得到:
max_tasks=10分支和标签属于引用。main 指向提交,HEAD 通常指向当前分支。创建新提交时,新对象被写入数据库,然后当前分支引用向前移动。
这也解释了前面的现象:创建分支很快,因为它主要是增加一个引用;reset 会移动引用;reflog 记录引用曾经指向的位置。
git count-objects -v结果示例:
count: 72
size: 288
in-pack: 0
packs: 0
size-pack: 0
prune-packable: 0
garbage: 0
size-garbage: 0小仓库可能仍以松散对象保存数据。仓库增长后,git gc 会整理对象并生成包文件。日常使用通常不需要手动干预;理解这层结构主要用于排错、恢复和解释命令行为。
不要直接删除 .git/objects、.git/refs 或其他内部文件。想检查完整性应使用 git fsck,想维护对象应使用 Git 提供的命令。
从课程总目录执行:
cd ..
git clone --branch v1.1.0 remote/task-board.git acceptance因为检出的是标签,Git 可能提示当前处于 detached HEAD。这适合只读验收;如果还要继续提交,应先创建分支:
git -C acceptance switch -c verify/v1.1.0本次只做验收,不创建新提交。确认标签:
git -C acceptance describe --tags --exact-match结果:
v1.1.0检查对象连通性:
git -C acceptance fsck --full没有输出表示没有发现对象错误。最后检查工作区:
git -C acceptance status --short同样应没有输出。
回到主项目并查看最近提交图:
cd team-task-board
git log --oneline --graph --decorate --all -10结果示例:
* 96fedbd (HEAD -> main, tag: v1.1.0, origin/main) fix: restore positive task limit
* 764744f chore: tune task limit
* f5bae60 refactor: allow unlimited tasks
* 62e1987 feat: raise task limit
* 2d6a3e2 feat: add positive task limit
* 0631382 (tag: v1.0.0) docs: clarify project description
* b9ce389 feat: document task filters
* 427a9c7 Revert "chore: add temporary debug switch"
* e67b83e chore: add temporary debug switch
* bc6bde7 merge: resolve task owner conflict
|\这条历史同时保留了功能提交、合并、公共撤销、两次发布和一次有意制造的错误。Git 的价值不在于让历史看起来永远完美,而在于让变化可检查、可协作、可恢复。
完成课程后,不看答案尝试完成这组验收:
安装并配置 Git,从空目录初始化仓库,创建 .gitignore,完成经过 status 与 diff 检查的首次提交。
为一个功能创建分支,提交修改,合并回 main,并能从提交图解释分支与合并关系。
添加远程地址,完成 push、fetch、pull --ff-only,并能处理同一行产生的合并冲突。
根据错误所在区域选择 、、、 或 ,说明每个选择会保留和丢弃什么。
如果其中某一步还需要照抄命令,回到对应章节再做一次,但换一个文件名或分支名。能在变化后的情境中完成同样的判断,才算真正掌握。
status、diff 和 diff --cached;origin/main..HEAD;revert,个人未共享历史才考虑重写;restore、reset --hard、clean -fd 前先确认将丢弃什么;restorerevertresetreflogstash整理未共享提交、创建版本标签,用 grep、log -S、blame 和 bisect 定位变化,最后从标签验收交付物。