Git 实用技巧:暂存现场、挑选提交与定位 Bug(阅读笔记)
原文作者:imDwAaY
原文:6篇文章讲清楚Git:Git 实用技巧:暂存现场、挑选提交与定位 Bug (5/6)
原文发布于:2026-09-28 19:22
这篇文章涉及的命令,可以按工作目的分成三组:追查历史、保存工作现场,以及调整提交。下面按这个顺序整理阅读笔记,命令示例重新编写,并结合 Git 官方文档补充细节。
追查历史:从线索到复现
| 已掌握的线索 | 选择的工具 | 能回答的问题 |
|---|---|---|
| 文件、关键词、作者或时间 | git log | 哪些提交值得进一步检查? |
| 某段代码所在的行 | git blame | 当前这些行对应哪些修改记录? |
| 一个正常版本和一个异常版本 | git bisect | 哪次提交开始出现可复现的问题? |
筛选提交和查看行历史
git log --oneline -- src/parser.ts
git log -p -S 'parseInput' -- src/parser.ts
git log -p -G 'timeout[[:space:]]*=' -- src/parser.ts
git log --author='Alice' --since='2026-09-01' --grep='parser'
git log -L 30,50:src/parser.ts
git log --merges --oneline
git blame -w -L 30,50 -- src/parser.ts
git blame -C -- src/parser.ts
-S 比较指定字符串在修改前后的出现次数;-G 检查补丁中新增或删除的行是否匹配正则。两者筛选条件不同。例如某个字符串从一行移到另一行,次数未变,未必能被 -S 找到。-L 用于追踪指定行范围的演变。参见 git-log 文档。
blame 的结果反映当前行的修改归属,不能单凭它判断问题责任。-w 能忽略空白差异,-C 有助于寻找跨文件复制的来源;接下来仍需阅读对应提交及其上下文。
用二分验证缩小范围
先确定一条稳定的判断规则,再开始二分。例如:固定输入下解析结果是否正确。假设 v1.2.0 已验证正常,当前版本异常:
git bisect start
git bisect bad HEAD
git bisect good v1.2.0
Git 会检出候选版本。每轮验证后,根据实际结果执行下面两条命令之一:
git bisect good
git bisect bad
对于无法构建或无法判断的候选版本,可以使用 git bisect skip。找到结果后执行 git bisect reset,恢复开始排查前的位置。
如果已有检测脚本,可以让 Git 自动推进:
git bisect start HEAD v1.2.0
git bisect run /tmp/check-parser.sh
git bisect reset
检测脚本返回 0 表示正常,1 至 127 中除 125 外的值表示异常,125 表示跳过。脚本应只检测目标问题,避免把依赖缺失误判为代码回归。退出码约定见 git-bisect 文档。
保存工作现场:临时收起还是同时保留
用 stash 收起未完成的改动
普通 git stash push 保存已跟踪文件的改动;-u 加入未跟踪文件,-a 还会加入被忽略的文件。给记录写明用途,恢复时更容易核对。
git stash push -u -m 'parser: unfinished validation'
git stash list
git stash show -p --include-untracked 'stash@{0}'
git stash apply --index 'stash@{0}'
apply 保留记录,pop 在成功应用后移除记录;若 pop 发生冲突,记录会保留。上例的 --index 会尝试恢复原先的暂存状态。确认工作恢复正确后,再用 git stash drop 'stash@{0}' 删除记录。具体行为见 git-stash 文档。
stash 列表由仓库共享,恢复前要检查当前分支与记录内容。git stash branch recover-parser 'stash@{0}' 可从保存时的基点创建分支并恢复改动。
stash 使用 Git 对象持久保存,重启不会让它消失。但删除记录后只能尝试寻找尚未清理的对象,不能保证找回;重要工作应及时提交到分支。
用 worktree 保留多个工作目录
需要并行维护分支、对比运行结果时,可以增加一个工作目录:
git worktree add -b hotfix/parser ../parser-hotfix main
git worktree list
这样可以在原目录继续开发,在 ../parser-hotfix 修复问题。关联工作目录共享对象数据库等仓库数据,各自拥有工作区、索引和 HEAD。额外检出的文件及安装的依赖仍占磁盘空间,同一分支默认也不能同时检出到多个工作目录。参见 git-worktree 文档。
处理并提交完改动后,用 git worktree remove ../parser-hotfix 移除工作目录;这不会同时删除对应分支。
调整提交:迁移修复和整理历史
cherry-pick:向当前分支引入指定改动
当修复已经形成独立提交,而所在分支还有未完成的功能时,可以只引入修复。下面的哈希为示例,执行时替换为实际提交:
git switch main
git cherry-pick 7a31bc2
它会在当前分支重新应用该提交的改动,通常产生新的提交对象。需要一起迁移的依赖提交,也应按依赖顺序纳入。
| 写法 | 用途 |
|---|---|
git cherry-pick A B | 按给定顺序应用两个提交 |
git cherry-pick A..B | 应用 B 可达、A 不可达的提交集合;线性历史中不含 A |
git cherry-pick A^..B | 在线性历史中包含起点 A;A 需有父提交 |
git cherry-pick -n A | 应用改动但暂不创建提交 |
遇到冲突后,修复文件、用 git add 标记,再执行 git cherry-pick --continue;放弃本轮操作则使用 git cherry-pick --abort。频繁迁移相同修复时,也值得重新检查分支之间的集成方式。
交互式 rebase:整理尚未共享的提交
git rebase -i HEAD~3 会打开最近三个提交的操作列表,按从旧到新的顺序排列。可以调整行顺序,或修改每行的动作:
| 动作 | 含义 |
|---|---|
pick | 应用提交 |
reword | 修改提交说明 |
edit | 暂停以修改提交 |
squash | 合入前一提交,并编辑合并后的说明 |
fixup | 合入前一提交,通常舍弃本条说明 |
drop | 移除该提交 |
exec | 执行指定的 shell 命令 |
例如,一次功能提交后跟着两次小修正,可以保留第一行的 pick,把后两行改成 fixup,将改动整理成一次提交。
rebase 会重写相关历史。整理后需要重新验证代码;涉及他人已使用的分支时,应先协调历史更新。操作受阻时,可解决冲突并暂存后运行 git rebase --continue,或用 git rebase --abort 放弃本轮整理。