从 40 个提交里找出真凶
目标
从“以前明明能用”出发,定位到一个具体 commit,并亲自遇到自动化能力的边界。
环境
/root/repo 中有一个空仓库。历史需要亲自创建——请原样粘贴并运行下面内容。这部分是准备工作,不是任务。
cd /root/repo && mkdir -p app
i=1
while [ "$i" -le 40 ]; do
if [ "$i" -eq 22 ]; then printf '#!/bin/sh
echo $(( 100 +
' > app/calc.sh
elif [ "$i" -lt 23 ]; then printf '#!/bin/sh
echo 100
' > app/calc.sh
else printf '#!/bin/sh
echo 250
' > app/calc.sh; fi
echo "note $i" > app/notes.txt
git add -A && git commit -qm "change $i"
i=$((i + 1))
done
printf 'timeout=3
retries=2
' > app/settings.ini
git add -A && git commit -qm "설정값 도입"
printf ' timeout=3
retries=2
' > app/settings.ini
git add -A && git commit -qm "포매터 실행"
git tag -f v1 "$(git rev-list --reverse HEAD | sed -n '2p')"
mkdir -p /root/arch
app/calc.sh 原本应输出 100,现在输出 250。历史中还有一个语法损坏、根本无法运行的 commit。
要创建的内容
/root/check.sh 판정 스크립트 — 종료코드로 good/bad/skip 을 말한다
/root/wrong-check.sh 125 를 일부러 뺀 것 (4단계에서 비교용)
/root/arch/bisect.txt bisect run 의 결과
/root/arch/wrong.txt 125 를 뺐을 때의 결과
/root/arch/culprit.txt 범인과 배제 근거
/root/arch/pickaxe.txt log -S 로 찾은 결과
/root/arch/blame.txt blame 과 blame -w 의 차이
/root/arch/report.md 정리
把判定脚本放在仓库外部是有原因的:若放在仓库内,bisect 在 commit 之间移动时,脚本本身也会随之改变。
步骤
- 创建历史和基准标签。
- 编写
/root/check.sh,准确区分三种退出码。 - 执行
git bisect start HEAD v1,再执行git bisect run sh /root/check.sh。把结果原样保存到bisect.txt。它无法缩小到唯一 commit。 - 使用去掉 125 的
wrong-check.sh再次运行相同二分。这次它自信地给出答案——而答案是错的。把两种结果并列记录。 - 用
git show确认两个候选中谁是罪魁祸首,并写明为什么排除另一个候选。 - 用
git log -S一次得到相同答案。 - 分别对
app/settings.ini运行git blame和git blame -w。 - 总结何时使用这四种工具。
参考
若第 3 步屏幕出现 The first bad commit could be any of:,说明操作正确。这不是失败,而是诚实的答案——只要仍有无法判定的 commit,bisect 就只能确定到这里。与第 4 步对比,就会清楚看到,这比自信地给出错误答案更好。
创建历史与基准点
创建历史和基准标签。
原样运行说明中的准备代码块。v1 标签表示“当时还能工作”的基准;二分必须有一个 good 和一个 bad 才能开始。
用退出码表达结果的判定脚本
编写 /root/check.sh,准确区分三种退出码。
0 表示 good,1~124 表示 bad,125 表示无法判定(skip)。若把根本无法运行的 commit 也算作 bad,二分会收敛到错误位置。先用 sh -n 只检查语法。
运行二分能缩小到什么程度
执行 git bisect start HEAD v1,再执行 git bisect run sh /root/check.sh。
把结果原样保存到 bisect.txt。它无法缩小到唯一 commit。
用 git bisect start HEAD v1 开始,再运行 git bisect run sh /root/check.sh。结束后务必用 git bisect reset 清理。不要概括结果,应按屏幕原样记录。
去掉 125 会发生什么
使用去掉 125 的 wrong-check.sh 再次运行相同二分。这次它自信地给出答案——而答案是错的。把两种结果并列记录。
另写一个对无法判定也返回 1(bad) 的脚本,并再次运行相同二分。这次它会自信地指出一个 commit,**但答案是错的。**请并列记录两种结果。
最后一步由人判断
用 git show 确认两个候选中谁是罪魁祸首,并写明为什么排除另一个候选。
若有两个候选,可用 git show <해시>:app/calc.sh 查看各自内容。还要写明排除另一个候选的依据,后续人员才不必重新核查。
已知查找内容时的捷径
用 git log -S 一次得到相同答案。
git log -S <문자열> 只显示添加或删除该字符串的 commit,无需运行六次二分便能直接找到——但前提是已经知道要找什么。
跨过格式化 commit 找到确定值的 commit
分别对 app/settings.ini 运行 git blame 和 git blame -w。
分别运行 git blame app/settings.ini 和 git blame -w app/settings.ini。前者指向只修改缩进的 commit,-w 会跳过它。请并列记录两个哈希。
何时优先使用哪种工具
总结何时使用这四种工具。
这些工具回答的问题不同。还要写下本次亲自经历的内容:自动化在何处停止。