Git apply 补丁验收:--check 只预检,--cached 只修改暂存区

10-01 3阅读

拿到一份补丁后,直接应用到正在工作的目录,会让原有修改与外来修改混在一起。更可靠的第一步是看补丁将改哪些文件,再用与最终应用模式一致的选项预检。检查通过只说明当前内容能匹配,并不意味着补丁已经生效。

git apply 默认修改工作区,--cached 只修改索引,--index 则要求并更新索引与工作区。三者的目标不同,预检也要跟随目标选择。尤其在暂存区和工作文件已经不一致时,不能拿一种模式的检查结果替另一种模式作保证。

用一个两行文件对照三个区域

下面保存为 demo.sh,用 Bash 和 Git 运行。脚本只在新建临时仓库演示,先提交 red,再把它改成 blue 生成本地补丁。生成后恢复工作文件,从同一基础状态检查应用过程;补丁保存在仓库外的临时目录。

第一次只应用到索引,分别读取索引、工作文件和 HEAD,核对三个区域的内容。随后在另一份新克隆里使用 --index,同步更新两处;最后在第三份克隆里制造上下文不匹配,确认预检失败且文件保持原样。

Git apply 补丁验收:--check 只预检,--cached 只修改暂存区

AI概念示意图:以抽象物件说明本文主题,不代表真实界面或运行结果。

set -euo pipefail
apply_demo=$(mktemp -d)
git init -q -b main "$apply_demo/source"
cd "$apply_demo/source"
git config user.name Demo
git config user.email demo@example.invalid
git config commit.gpgsign false
printf 'red\nkeep\n' > color.txt
git add color.txt
git commit -qm base
printf 'blue\nkeep\n' > color.txt
git diff -- color.txt > "$apply_demo/change.patch"
git restore -- color.txt

git apply --cached --check "$apply_demo/change.patch"
test "$(git show :color.txt)" = $'red\nkeep'
git apply --cached "$apply_demo/change.patch"
test "$(git show :color.txt)" = $'blue\nkeep'
test "$(cat color.txt)" = $'red\nkeep'
test "$(git show HEAD:color.txt)" = $'red\nkeep'
printf 'cached: index=blue; worktree=red; HEAD=red\n'

git clone -q "$apply_demo/source" "$apply_demo/both"
cd "$apply_demo/both"
git apply --index --check "$apply_demo/change.patch"
git apply --index "$apply_demo/change.patch"
test "$(git show :color.txt)" = $'blue\nkeep'
test "$(cat color.txt)" = $'blue\nkeep'
printf 'index: index=blue; worktree=blue\n'

git clone -q "$apply_demo/source" "$apply_demo/mismatch"
cd "$apply_demo/mismatch"
printf 'green\nkeep\n' > color.txt
if git apply --check "$apply_demo/change.patch" \
    > "$apply_demo/check.log" 2>&1; then
  printf 'unexpected check success\n' >&2
  exit 1
fi
test "$(cat color.txt)" = $'green\nkeep'
printf 'check failure: worktree remains green\n'

预检的对象要与应用动作一致

第一行应显示索引为 blue,而工作区和 HEAD 仍为 red。--check 没有修改索引,真正变化发生在下一次 apply。第二行确认 --index 同时更新索引与工作文件。第三行说明不匹配的补丁被拒绝,预检没有把 green 改成别的内容。

只改索引之后,普通 git diff 会比较工作区与索引,看起来可能像准备把 blue 改回 red;git diff --cached 才是准备提交的改动。遇到这种状态先确认比较方向,不要把差异方向误当成补丁应用反了,也不要立即执行批量暂存覆盖索引。

--index 还要求相关路径的索引与工作区内容一致,即使补丁看上去能分别套用,也可能因两处状态不同而拒绝。把两处当成一个需要保持一致的操作目标,有助于理解这个约束;已有本地修改时应先隔离并检查。

能应用不等于行为正确

预检通过后,到真正应用之前如果文件又变化,结果仍可能不同。因此检查和应用应尽量在同一个受控现场连续进行。自动化流程还应核对补丁来源、涉及路径与预期基础版本,不能把一次成功退出当成永久有效的许可。

默认应用遇到无法匹配的片段会失败。--reject 会改变处理方式,可能应用部分片段并留下拒绝文件,不适合在不了解差异时随手加入。本例没有使用它,也没有通过忽略空白或扩大模糊匹配来掩盖基础版本的问题。

git apply 本身不创建提交,也不完整导入作者和提交说明。若拿到的是带提交信息的邮件补丁系列,应评估 git am 的对应流程。文件差异传输与提交历史传输有不同目标,先确认收到的材料类型,再选择处理工具。

验收时先看改动范围,再看索引和工作区差异,最后运行与变化有关的测试。补丁能够成功定位文本,只证明机械套用成立;是否保持功能、是否漏改关联文件,以及结果该不该提交,都需要独立判断。

参考资料

  1. Git 官方文档:apply 预检与索引选项

  2. Git 官方文档:diff 补丁格式

  3. Git 官方文档:am 邮件补丁导入

文章版权声明:除非注明,否则均为云鹊BLOG原创文章,转载或复制请以超链接形式并注明出处。