LabHub
学习 学习路径 课程

Git 实战

从 40 个提交里找出真凶

在 LabHub 中继续学习

目标

从“以前明明能用”出发,定位到一个具体 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 之间移动时,脚本本身也会随之改变。

步骤

  1. 创建历史和基准标签。
  2. 编写 /root/check.sh,准确区分三种退出码。
  3. 执行 git bisect start HEAD v1,再执行 git bisect run sh /root/check.sh。把结果原样保存到 bisect.txt它无法缩小到唯一 commit。
  4. 使用去掉 125 的 wrong-check.sh 再次运行相同二分。这次它自信地给出答案——而答案是错的。把两种结果并列记录。
  5. git show 确认两个候选中谁是罪魁祸首,并写明为什么排除另一个候选
  6. git log -S 一次得到相同答案。
  7. 分别对 app/settings.ini 运行 git blamegit blame -w
  8. 总结何时使用这四种工具。

参考

若第 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 blamegit blame -w

分别运行 git blame app/settings.inigit blame -w app/settings.ini。前者指向只修改缩进的 commit,-w 会跳过它。请并列记录两个哈希。

何时优先使用哪种工具

总结何时使用这四种工具。

这些工具回答的问题不同。还要写下本次亲自经历的内容:自动化在何处停止。