前言¶
智能体操作多个 git 仓库时,最贵的错误不是写错了代码,而是在误读的状态下把 git 命令跑对了。一个落后远端的 checkout、一份本机另存的同名 clone、被并发进程挪走的 HEAD,都不会让 git 报错——它只是按你的描述执行,然后你事后才发现改动不见了、推送被拒、提交压在了别人的半成品上。
这类问题的共同点是:出错之前,其实有一个能回答”我现在到底在哪个仓库、哪个分支、相对远端什么位置”的时刻。dsh-checkout-guard 就是在那个时刻介入。
这是什么¶
一个 DSH 插件,由 YaoleWu 维护(插件目录页的开发者字段,package.json 里的 author 仍写着 LeslieWylie,仓库已迁移到 YaoleWu/dsh-checkout-guard 名下)。它读取一个 git 工作副本,在编辑、提交、推送之前告诉你:分支身份、与远端的分叉/落后程度、这个数据的陈旧度、下一个提交会署哪个邮箱、暂存区里已有什么、以及本机是否还有同仓库的另一份 clone 已经跑在你前面。
只读,默认不联网,从不占用 index 锁。
核心功能¶
插件提供两个工具。
checkout_guard 对单个工作副本做一次深度检查,输出结构化结果,字段包括:
branch:当前分支、是否 detached、upstream、是否 linked worktreesync:ahead/behind、fetchAgeSeconds、本次是否真的联网取过remote:归一化后的host/owner/repo、host、是否公开 forgeidentity:下一个提交的邮箱、这个邮箱来自哪份配置、仓库是否有本地覆盖workingTree:staged/unstaged/untracked与cleanduplicateCheckouts:本机同仓库的其他 checkout,各自的路径与领先情况blockers/warnings/verdict/safeToWrite
调用方只需要看一个字段:safeToWrite。blockers 是继续下去会丢失或错置工作的状态,warnings 是值得知道但不必然是错的情况。
checkout_guard_scan 对某个根目录下的所有工作副本各返回一行同样的摘要,外加两个分组结果:
needsAttention:落后、分叉、detached、或没有 upstream 的那些 checkoutduplicateRemotes:按远端分组的路径,同仓库有几份 clone 一眼可见
典型用法是先跑 checkout_guard_scan 找到值得细看的副本,再把路径逐个交给 checkout_guard。
安装与启用¶
需要 Node 22.19+ 或 24+,运行期唯一依赖是 git 在 PATH 上。
npm install dsh-checkout-guard
然后在 profile 的 cordis.yml 追加:
- id: checkout-guard
name: 'dsh-checkout-guard'
如果要限定插件的搜索范围,加上 config.roots:
- id: checkout-guard
name: 'dsh-checkout-guard'
config:
roots:
- /Users/you/projects
roots 限定插件会看的一切范围,不设时默认为你的 home 目录。当前版本为 0.1.0,许可证 MIT,peerDependencies 要求 @deepseek-ai/cordis ^4.0.1 与 @deepseek-ai/dsh-tools ^0.1.0-rc.6。
典型用法¶
对单个副本做断言式检查。传入 expectBranch 时,实际分支与预期不符直接记为 blocker:
{ "path": "/abs/path/to/repo", "expectBranch": "main" }
返回示例(节选):
{
"branch": { "current": "main", "detached": false, "upstream": "origin/main", "linkedWorktree": false },
"sync": { "ahead": 1, "behind": 1, "fetchAgeSeconds": 255600, "fetchedNow": false },
"remote": { "normalized": "github.com/acme/widget", "host": "github.com", "publicForge": true },
"identity": { "email": "you@example.com", "source": "global", "repositoryOverride": false },
"blockers": [
"diverged from origin/main by 1 commit(s) (and 1 ahead); ... a push will be rejected",
"another checkout of the same remote is ahead of this one: /elsewhere/widget ..."
],
"verdict": "blocked",
"safeToWrite": false
}
checkout_guard 可用参数:
| 参数 | 作用 |
|---|---|
path(必填) |
工作副本的绝对路径,或其内部任意子目录 |
fetch |
先联系远端让 ahead/behind 反映当下。唯一联网的选项,默认 false |
expectBranch |
你认为自己在哪个分支,不符即 blocker |
expectIdentity |
你期望的提交署名邮箱,不符即 blocker |
remote |
对比哪个远端,默认 origin |
staleAfterHours |
远端 ref 多久算陈旧,默认 24 |
findDuplicates / duplicateRoots / maxDepth / maxRepos |
控制重复 clone 的搜索范围 |
expectBranch 和 expectIdentity 是把报告变成断言的开关。不传,插件只描述;传了,插件会拒绝。
把断言关闭、只看静默扫描结果的场景:
{ "roots": ["/abs/path/to/projects"], "maxDepth": 3 }
安全性质与适用场景¶
插件的安全约束与其问题域一致——它假设此刻可能还有另一个 agent 在同一个 checkout 里:
- 不对任何仓库写入。唯一可能修改东西的命令是
git fetch --dry-run,仅在fetch: true时执行,且只动FETCH_HEAD。 - 每次调用都带
GIT_OPTIONAL_LOCKS=0,避免git status骗取.git/index.lock而卡住并发进程。 GIT_TERMINAL_PROMPT=0、空GIT_ASKPASS,对私有远端 fetch 快速失败,不会停在无人可见的凭据提示上。- URL 归一化时先剥掉内嵌凭据,凭据无法进入结果对象或日志。
- 路径白名单两侧都做
realpath,放在允许目录里的软链接读不到外面。
适合谁:在 DSH 里同时操作多个仓库的智能体与开发者。它刻意不做三件事:不告诉你远端是否已归档(那需要 forge API,插件不发带认证的 HTTP 请求,remote.normalized 给你 host/owner/repo 自己去查);不修任何东西(不 pull、不 rebase、不 stash、不 checkout,”落后”和”分叉”的正确恢复方式不同,这个选择不该由守卫替你做);不评判身份(只报告署名来自哪份配置,传了 expectIdentity 就严格按你给的值比对,从不猜测邮箱”应该”是什么)。
插件以当前 dsh 进程的权限运行,安装前应检查源码与许可证(本插件为 MIT)。
结尾¶
dsh-checkout-guard 做的事很小:在你下一个写操作之前回答一次”我到底在哪儿”。它不会替你修仓库,也无法阻止你在拿到 safeToWrite: false 之后继续,但至少让那个误读的状态在日志里留下一条明确记录,而不是事后在推送失败里才被发现。
- 目录页:https://www.skillhub.cn/plugins/YaoleWu/dsh-checkout-guard
- GitHub:https://github.com/YaoleWu/dsh-checkout-guard
DSH 理念是”一切皆插件”,社区目录是独立站点,与 DeepSeek / 幻方无官方从属关系。