24 讲路线 · 与 S12 配对
D12: Git Integration 深挖 · Git 集成
本讲在 S12 主线之上,聚焦实现细节、边界条件与自测;导图与主线相同模块,便于对照。
建议:先读完 S12,再按下方顺序走读源码与练习。
📌 S 与 D 的分工(为何答案在 D12?)
S 线(如 S12):讲清楚「是什么、典型流程、配置长什么样」,篇幅尽量短,一般不铺长篇标答,避免主线臃肿。
D 线(如本页):同一主题的深挖 + 边界 + 自测参考答案刻意放在这里——你可以先闭卷想,再展开下文对照。这不是「S 只出题、D 只给答案」的硬性协议,而是本站的排版约定:S 轻、D 重。
不建议去掉 D 里的答案:去掉后 D 与 S 的差异被抹平,深挖页失去「对答案」这一用途;若你希望某讲在 S 侧多一句提示,更合适的是像 S12 那样链到对应的 D,而不是删掉 D 的解析。
🔬 深挖目标
Git 集成是「自动化刀锋」:何时只读、何时写索引、何时提交、冲突由谁解决。本讲对齐宿主命令与 git 状态机,避免静默破坏仓库。
🌳 操作分级
| 级别 | 示例 | 权限建议 |
|---|---|---|
| 只读 | status、diff、log | 可默认 auto |
| 索引变更 | add、restore | ask 或项目策略 |
| 历史改写 | commit --amend、push --force | 强确认 + 日志 |
🔀 合并与冲突
- 模型生成 patch 与 git 冲突 marker 同时出现时,优先让人类看清「三方」。
- 自动化 merge 失败必须非零退出并把冲突路径喂回上下文。
- 与 D09:IDE buffer 未保存 vs git checkout 冲突检测。
📖 走读顺序
- 搜索
git封装层(spawn 包装),列出所有子命令白名单。 - 确认是否过滤
--force、update-ref等高危参数。 - 走读一次「自动 commit」:默认 message 规则、是否 GPG、是否 hook。
✏️ 自测 1 · 参考答案:git commit 放行、git push 禁止
题干
设计策略:允许模型 git commit 但禁止 git push,在权限层如何实现?
思路概览
权限函数(如 canUseTool)必须在真正 spawn 之前看到结构化或可解析的调用意图。实现上有三条常见路线,可叠加:
- 专用 Git 工具:不把「任意 shell」暴露给模型,而是
git工具参数里带subcommand(或等价枚举)。策略表:commit/add/status→ auto 或 ask;push、fetch、含remote的网络写 → 一律 deny 或未开放该枚举值。 - Bash 工具 + 命令解析:若仍走通用 Bash,必须在权限层对整行命令做 token 化(注意引号、
git -C path、env GIT_DIR=…):识别首个git子命令;push、push --force、remote set-url等命中高危规则 → deny;commit在允许列表 → 继续走工作区与路径校验。 - PreToolUse Hook(辅助):对 Bash 的
command字符串做二次审计(正则 / 小型解析器),命中git push返回 deny 或blockingError。Hook 不能替代权限门,只做纵深防御。
务必兜住的坑
git -C ../outside-repo push:除子命令外还要校验-C与当前工作目录是否落在允许的工作区根内。git的别名、脚本包装:alias gp=git push—— 若无法静态解析,应对「未列入白名单的任意 git 调用」默认 ask/deny。gh pr create等间接 push:若策略是「禁止出网写远程」,需在网络类工具另表约束,与 git 子命令策略合并考虑。
// 权限层伪代码:仅示意「在 spawn 之前」如何分支
function gitPolicyFromArgv(argv: string[]): 'allow' | 'deny' | 'ask' {
// 1. 归一化:去掉 env 赋值、取到 git 与其子命令(需真实解析器,勿只靠 split)
const sub = extractGitSubcommand(argv) // e.g. 'commit' | 'push' | null
if (sub === 'push') return 'deny'
if (sub === 'commit') return 'allow' // 还可再检查 -C、文件路径
return 'ask'
}
✏️ 自测 2 · 参考答案:.git 在 workspace 外与 symlink
题干
若 .git 在 workspace 外,工具应拒绝还是跟随 symlink?
结论(教学上的安全默认)
默认应拒绝在未额外授权的情况下对「解析后真实路径不在工作区内的 Git 仓库」执行写操作。理由:跟随 symlink 会把工作区内的路径映射到盘外任意 Git 目录,等价于隐蔽的目录穿越 —— 模型以为在改课程仓库,实际在改用户别的项目。
推荐实现步骤
- 对涉及仓库的路径(含
.git、gitdir、worktree)做realpath/ 等价解析,得到规范绝对路径。 - 校验
realpath(repoRoot)是否以配置的 workspace 根为前缀(注意尾随/与大小写/Unicode 规范化)。不通过 → deny 或只读降级。 - Symlink:可在「允许跟随」与「拒绝跟随」二选一作为产品策略;若跟随,必须在权限判断里用解析后的目标路径,而不是用户可见的 symlink 路径字符串。
何时可以「跟随」
仅当产品明确把 workspace 定义为「逻辑视图」且用户显式勾选「允许通过 symlink 指向外部裸仓」并写入审计日志时,才可放行;否则一律按上面默认拒绝写、或仅允许 status/log 等只读且仍记录真实路径。