1实现原理 · 为什么它能做到
整体定位是『编排层 SKILL.md + 6 个自带 bash 取证脚本』,且所有脚本在破坏动作前一律非破坏性/增量式——恢复动作本身绝不能让损失更大。
until a step is explicitly labeled destructive — recovery must never make the loss worse.
『我会丢掉什么』的权威检查由 git_loss_audit.sh 实现:先 fetch 刷新远端跟踪 ref,再枚举每个 linked worktree 的 HEAD 与 status、本地分支/标签、stash、dangling commit,任何本地独有 commit 或脏/不可读 worktree 会 exit 1。
# It refreshes remote-tracking refs with `fetch`, then only inspects repository/worktree state. It
防『审计干净但东西其实在另一个 clone 里』:git_find_all_checkouts.sh 做机器级 checkout 发现,靠规范化 remote URL(SSH/HTTPS 等价)或『共享任一 commit 历史』来识别同一仓库的其他 checkout,从不按目录名匹配。
# not `git worktree list`, not `git branch -a`, not `git fsck`, not `git stash list`,
合并判定不数 commit,而是做内容级试验合并:`git merge-tree --write-tree <base> <branch>` 在内存里把分支合回 base,若结果树 == base 树则判 MERGED (content contained),否则 NEEDS REVIEW。
# 2. Else do a trial 3-way merge of the branch INTO the base (git merge-tree, in memory, no
gc 前保全:把每个 dangling commit 钉在隐藏 ref 命名空间 refs/dangling-backup/<sha> 下——有 ref 指着的对象永不被 gc 回收,且不会污染 `git branch`/`git stash list`。
git update-ref "refs/dangling-backup/$sha" "$sha"
销毁前导出:stash 导出为完整二进制 patch,并额外用 `git archive "$ref^3"` 把 stash 的第三父提交(`git stash -u/-a` 携带的未跟踪文件)打成 tar——因为 `git stash show -p` 不显示这些文件,只导 patch 会静默丢失它们。
# -> stashN-untracked.tar the often-forgotten THIRD parent:
删除前的 compare-and-swap 门禁:`--verify-current BUNDLE` 逐条比对 bundle 里记录的 ref 与仓库当前值,任何 ref 移动/消失即 exit 1。
echo "REFSET_CHANGED checked=$CHECKED changed=$MOVED — rebuild and re-verify the bundle before deletion" >&2
独立 clone 退役准备:脚本先按一长串拒绝清单核查(脏/未跟踪/ignored 字节、stash、reflog-only commit、clone 独有不可达对象、linked worktree、本地 submodule、partial/promisor clone、Git LFS/annex 对象库、tracked content filter、include.path/hooksPath 间接层、浅克隆),全部通过才冻结证据。
# It refuses to certify a clone that still has working-tree bytes, ignored files,
写操作面被硬性限制:备份目录必须是绝对路径、必须不存在(拒绝覆盖)、必须在 clone 与 survivor 两个仓库之外;脚本自身从不移动/删除 checkout、从不改 ref。
"$CLONE/"*|"$SURVIVOR/"*) die "backup directory must be outside both repositories" ;;
工作流纪律以『Outcome contract + 授权边界』写进 prompt:证据范围 ≠ 行动范围,授权是按对象而非按仓库的,保留/退役分相,完成标准用用户世界的可观测事实(远端已有该 commit、分支/worktree 状态正确)而非 bundle 数量或校验和。
- **Evidence scope is not action scope.** A read-only audit may discover another clone, ref,
脚本清单表逐条标注『是否变更』(read-only / 只动 remote-tracking refs / 只加 refs / 只写备份文件),并声明所有 helper 都不授权 checkout、reset、push、stash drop、branch -d、gc。
the helpers authorizes `checkout`, `reset`, `push`, `stash drop`, `branch -d`, or `gc`.
2核心能力
3外部依赖
| Kind | Dependency / Endpoint |
|---|---|
| cli | git(含 worktree/fsck/reflog/bundle/merge-tree/update-ref/for-each-ref 等) |
| cli | git ≥ 2.38(merge-tree --write-tree 是内容级判定的前提) |
| cli | uv(仅维护者跑回归测试时用到) |
| cli | sha256sum / shasum(bundle 指纹;缺失即报错) |
| cli | tar(打包 config/hooks/info 元数据) |
| cli | stat(跨 BSD/GNU 的 mtime 与文件模式读取) |
| cli | lsof(macOS 上探测 clone 是否仍被进程占用;仅文档步骤,不在脚本内) |
| network | git fetch(由脚本执行;对远端主机发起出站连接,可能触发宿主 git 凭证助手) |
| network | git fetch --all --prune(合并判定脚本执行,同一出站面) |
| network | git push(仅文本指导:把 backup 分支推到 origin 做三重备份;无脚本执行) |
| package | 无第三方包(Git + 标准 Bash 即可;测试仅用 Python 标准库 unittest/tempfile/subprocess) |
4管控建议与判定依据
- scripts/: 执行本地脚本或命令: git-safety-net/scripts/git_find_all_checkouts.sh —— 只读:find + 只读 git 查询(sta
- scripts/ 或 SKILL.md: 声明动态安装外部语言包依赖
- 网络端点: 声明外部网络访问: 入站/出站:`git fetch "$REMOTE" --prune`(git-safety-net/scripts/git_loss_audit.s
- 本地工作目录: 文件读写与工件输出: 仓库外备份:git_export_before_drop.sh 默认 `$HOME/.git-backups/<YYYY-MM-DD>-<repo>/
5第二遍独立确认
- [ok] external_deps 逐条反查调用点:git 系列命令 — 在 6 个脚本里逐条确认 git 子命令的真实执行点(fetch/worktree list/fsck --dangling/stash list/bundle create+verify/list-heads/reflog/for-each-ref/update-ref/check-attr/submodule status/cat-file/rev-list)。全部为执行路径上的真实调用,非注释。
- [ok] external_deps:uv 与 lsof 是否真在脚本里 — 两者都不在脚本中执行:`uv run python -m unittest discover -s tests -p 'test_*.py'` 只出现在 SKILL.md § Maintainer verification(维护者本地跑测试);lsof 只出现在 references/merge_verification.md 的退役 READ-DO 序列(macOS 探测进程占用)。已按『文档层工具』而非『运行时依赖』归类。
- [ok] 网络调用面是否存在遗漏(隐藏脚本/混淆内容) — glob 全量(hidden=true, gitignore=false)显示该 skill 目录仅 17 个文件:SKILL.md、3 个 references、6 个 scripts、6 个 tests、evals/evals.json、agents/openai.yaml,无可执行二进制、无压缩/混淆内容。全目录 grep curl/wget/gh/nc/python -c 零命中;唯一 https:// 出现在 find_all_checkouts 的 URL 归一化注释示例里。
- [ok] 反例排查:git_find_all_checkouts.sh 号称不联网,但脚本里有 `git fetch --all --prune` — 该行(第 317 行)位于 `cat <<'FRESHNESS'` heredoc 内,是打印给用户看的建议,不在执行路径上;脚本头部 `# NON-DESTRUCTIVE: runs only \`find\` plus read-only Git queries` 与代码一致(唯一外部命令是 find 与只读 git 查询)。不构成 discrepancy,因此改为标注为『文本指导』。
- [ok] 反查『脚本非破坏性』是否属实(是否有删除/改写仓库对象) — 对 rm -rf / branch -D / reset --hard / stash drop / worktree remove / update-ref -d / gc 全目录扫描:脚本内仅两处相关文本——preserve_danglers 的 `rm -f "$OUT"`(删除自己刚写失败的 patch 文件)与 loss_audit 的提示字符串 `never use 'git worktree remove --force'`。所有真正的破坏性命令(`git update-ref -d`、`git worktree remove`、`--force-with-lease` 等)只出现在 SKILL.md/references 供人执行的步骤中。
- [ok] 『写操作面』核对:备份到底写到哪里、是否会覆盖既有数据 — export 脚本默认写 $HOME/.git-backups/<date>-<repo> 并可 --out 覆盖;preserve_danglers 的 --patch-dir 先绝对化再 mkdir -p;prepare_clone_retirement 强制 `[ ! -e "$OUT" ]`(拒绝已存在目录)、要求绝对路径、并 `case "$OUT/" in "$CLONE/"*|"$SURVIVOR/"*) die ...` 拒绝把备份放进被退役/保留的仓库内。三处均与 SKILL.md『备份放仓库外』一致。
- [ok] 凭证/全局配置读取面反查 — 全目录 grep --global / .gitconfig / credential / HOME 的结果:脚本内只有 export 的 `$HOME/.git-backups/...` 默认输出路径;`git config --global` 只出现在两处 references 中作为给用户的建议命令。prepare_clone_retirement 读取仓库私有配置时显式用 `git config --file <config> --no-includes`(不吞并用户全局配置)。无 token/env 读取。
- [ok] 实现原理是否夸大:『内容级判定是 sound 而非启发式』 — 与脚本逐行比对成立:脚本先 `git merge-base --is-ancestor` 判 ancestor,再 `git merge-tree --write-tree <base> <branch>` 比较结果树与 base 树;MT_RC>=128(git<2.38)时明确退回 NEEDS REVIEW 而非猜 MERGED。声明与实现一致,未夸大。
6结论
b2b944f8a078459b…