背景

我的 Hugo 博客使用了 PaperMod 主题,在一次升级后(从 10d3dcc 升到 154d006),右上角的导航菜单出现了样式错乱:菜单项间距变大、悬停效果失效、整体位置偏移。

一开始我尝试在 custom.css 中覆盖样式,改了好几轮都没完全解决。因为我不确定是主题的哪次提交引入了问题——中间可能隔了几十个甚至上百个 commit。

传统的做法是逐个 commit 回退测试,但效率太低。这时候,git bisect 就是最好的工具

什么是 git bisect?

git bisect 是 Git 自带的二分查找命令。它的工作原理是:

  1. 你告诉 Git 一个“好”的版本(没有 bug)和一个“坏”的版本(有 bug)
  2. Git 自动切换到这两个版本正中间的 commit
  3. 你测试这个版本,告诉 Git 它是“好”还是“坏”
  4. Git 根据你的反馈,缩小范围,继续切到中间点
  5. 重复 7~8 次后,精确定位到引入 bug 的那一次提交

假设区间内有 128 个 commit,普通逐个测试需要 128 次,而 git bisect 只需要 7 次(因为 2^7 = 128)。

实战:定位 PaperMod 主题的 Bug

第一步:进入主题目录

PaperMod 通常以 Git Submodule 形式引入,所以需要先进入主题目录:

cd "G:\Hugo-Blog - PaperMod\themes\PaperMod"

第二步:启动 bisect

git bisect start
git bisect bad 154d006      # 当前有问题的版本(坏)
git bisect good 10d3dcc     # 之前正常的版本(好)

执行完后,Git 会自动切换到区间中间的某个 commit,并提示类似:

Bisecting: 87 revisions left to test after this (roughly 7 steps)

第三步:每轮测试

每次 Git 切到一个新的 commit,你都需要做三件事:

  1. 清理缓存并重启 Hugo

    回到项目根目录,删除 resources/_gen 文件夹(避免主题缓存干扰),然后启动 Hugo:

    hugo server -D -F --renderToMemory
    

    建议用无痕/隐私模式打开浏览器访问 http://localhost:1313,避免缓存干扰。

  2. 检查菜单是否正常

    观察右上角菜单的样式——是紧凑正常,还是错乱稀疏?

  3. 标记结果

    回到 themes/PaperMod 目录,根据测试结果输入:

    git bisect good   # 如果这个版本菜单正常
    # 或
    git bisect bad    # 如果这个版本菜单依然有问题
    

    Git 会自动跳到下一个待测试的 commit,重复第 1~3 步。

第四步:得到结果

大约 7~8 轮后,Git 会输出类似:

65335d0f4a3e2b1c9d8e7f6a5b4c3d2e1f0a9b8c is the first bad commit

这个 commit hash 就是精确引入 bug 的那一次提交

第五步:重置

排查完后,记得退出 bisect 模式:

git bisect reset

这会将子模块恢复到 bisect 开始前的状态(也就是最新的 154d006),不会留下任何中间状态。

我找到了什么?

通过 git bisect,我定位到罪魁祸首是这个 commit:

style(header): simplify margin and padding for header navigation

对比发现,新版将菜单的间距控制从:

/* 旧版 */
menu li + li {
    margin-inline-start: var(--gap);
}

改成了:

/* 新版 */
menu {
    column-gap: var(--gap);
}

这个改动导致了菜单位置偏移和间距异常。有了这个信息,修复就变得非常简单——只需要在 custom.css 中覆盖 column-gap: 0,并恢复 li + li 的间距控制即可。

如果我习惯用图形界面呢?

如果你用的是 TortoiseGit,同样支持 bisect 操作:

  1. 在主题目录(themes/PaperMod)右键 → TortoiseGitBisectStart
  2. 在对话框中填写:
    • Bad commit154d006(当前有问题的版本)
    • Good commit10d3dcc(之前正常的版本)
  3. 点击 OK,TortoiseGit 会自动切换到一个中间 commit
  4. 测试 Hugo 站点,然后回到 TortoiseGit → Bisect → 根据结果点击 GoodBad
  5. 重复直到找到第一个 bad commit

不同版本的 TortoiseGit 菜单位置可能略有差异,但核心逻辑完全一样。

为什么这个方法比盲改 CSS 高效?

在不确定问题根源时,直接修改 CSS 是,而 git bisect。前者可能改十几轮都碰不到核心,后者最多 7~8 次就能锁定目标。一旦知道是哪个 commit 改了什么,修复方案的准确性就从“碰运气”变成了“精准打击”。

一点小建议

  • 每次测试前记得删 resources/_gen,Hugo 的主题缓存可能会干扰测试结果
  • 务必用无痕窗口,浏览器缓存也可能是“帮倒忙”的元凶
  • 如果测试过程中搞混了,可以用 git bisect log 查看操作历史,或者用 git bisect reset 重新开始

结语

git bisect 是一个被很多人忽略但极其强大的调试工具。这次经历让我深刻体会到,与其在 CSS 文件里漫无目的地改来改去,不如花 10 分钟用二分法精确锁定问题。希望这篇文章能帮你省下未来几小时甚至几天的排查时间。