尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Beads 仓库上下文解析(RepoContext)指南:BEADS_DIR 重定向、Git Worktree 与统一 Git 执行路径

Beads 仓库上下文解析(RepoContext)指南:BEADS_DIR 重定向、Git Worktree 与统一 Git 执行路径 Beads 仓库上下文解析RepoContext指南BEADS_DIR 重定向、Git Worktree 与统一 Git 执行路径【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beads导读Beads 是运行在 Git 仓库之上的编码代理记忆系统其全部数据操作如bd dolt push、bd sync、git add .beads/都依赖在正确的仓库里执行 Git 命令。但当用户从子目录、Git Worktree、甚至通过BEADS_DIR环境变量指向另一个仓库来运行bd命令时CWD当前工作目录与.beads/所在的仓库会分离导致 Git 命令在错误的目录中执行。本指南以 engdocs/REPO_CONTEXT.md 为骨架结合 internal/beads/context.go 源码实现系统讲解 RepoContext API 的解析规则、四大典型场景、安全机制与服务端模式的差异帮助你理解并安全使用这一单一事实来源的仓库解析层。问题背景50 条 Git 命令的共同假设在引入 RepoContext 之前代码库中 50 余条 Git 命令都隐含地假设CWD 就是仓库根目录见 internal/beads/context.go 的包注释。但实际运行bd时存在三种典型偏差跨仓库运行通过BEADS_DIR环境变量管理另一个仓库的.beads/数据如 fork 贡献者的追踪器放在独立仓库Git Worktree工作目录与主仓库分离共享同一个.git与.beads/仓库子目录在仓库的任意子目录如/project/src中发起命令。如果没有集中式处理每个命令都要各自实现路径解析逻辑一旦假设与现实不符就会产生难以排查的 Bug。RepoContext 的诞生正是为了解决这一问题——它作为仓库路径解析的唯一事实来源确保所有 Git 命令无论从何处发起都落到正确的仓库。RepoContext API 概览核心入口是beads.GetRepoContext()它返回一个包含已解析路径的RepoContext结构体import github.com/steveyegge/beads/internal/beads rc, err : beads.GetRepoContext() if err ! nil { return err } // Run git in beads repository (not CWD) cmd : rc.GitCmd(ctx, status) output, err : cmd.Output()从源码结构看internal/beads/context.goGetRepoContext()通过sync.Once实现进程内缓存首次调用时执行buildRepoContext()完成全部路径解析后续调用直接复用结果。三个核心方法的选用标准方法使用场景典型示例GitCmd()针对 beads 仓库自身的 Git 操作git add .beads/、git push提交/推送 beads 数据GitCmdCWD()针对用户当前工作仓库的操作git status展示用户的未提交改动RelPath()将绝对路径转换为仓库相对路径在输出中统一展示路径RelPath()的实现非常直接本质是对仓库根目录做filepath.Rel见 internal/beads/context.go当路径不在仓库内时返回错误。GitCmd() 与 GitCmdCWD() 的本质区别当BEADS_DIR指向另一个仓库时两者的差异至关重要rc, _ : beads.GetRepoContext() // GitCmd: runs in the beads repository // Use for: committing .beads/, pushing/pulling beads data cmd : rc.GitCmd(ctx, add, .beads/issues.jsonl) // GitCmdCWD: runs in users current repository // Use for: checking users uncommitted changes, status display cmd : rc.GitCmdCWD(ctx, status, --porcelain)从源码看internal/beads/context.goGitCmd()实际做了三件事设置工作目录cmd.Dir rc.RepoRoot等价于cd $RepoRoot git ...禁用 hooks追加-c core.hooksPath参数并设置环境变量GIT_TEMPLATE_DIR锁定目标仓库显式设置GIT_DIRRepoRoot/.git与GIT_WORK_TREERepoRoot。第 3 点对应源码注释中的 GH#2538 修复当从 worktree 运行时git 可能继承指向 worktree.git的环境变量导致 pathspec outside repository 错误显式指定GIT_DIR/GIT_WORK_TREE能确保 git 始终作用于包含.beads/的那个仓库。而GitCmdCWD()internal/beads/context.go只在CWDRepoRoot非空时设置cmd.Dir如果 CWD 根本不在 git 仓库内则保持cmd.Dir为空使用进程自身的 CWD。此外文档与源码都强调实现采用cmd.Dir模式而非-C标志这是更符合 Go 惯用法的执行方式。四大典型场景解析场景一普通仓库子目录运行CWD 位于包含.beads/的仓库内部只是不在根目录/project/ ├── .beads/ ├── src/ └── README.md $ cd /project/src $ bd dolt push # GitCmd() runs in /project (correct)buildRepoContext()通过git.GetMainRepoRoot()向上解析出仓库根/project因此即使从src/子目录发起命令Git 操作也落在正确位置internal/beads/context.go。场景二BEADS_DIR 重定向用户在一个仓库工作但管理的是另一个仓库中的 beads 数据$ cd /repo-a # Has uncommitted changes $ export BEADS_DIR/repo-b/.beads $ bd dolt push # GitCmd() runs in /repo-b (correct, not /repo-a)这种模式在以下场景中非常常见Fork 贡献追踪贡献者的追踪器tracker放在独立仓库本地工作仓库保持干净共享团队数据库多个成员共用同一个 beads 数据库仓库Monorepo 布局数据仓库与代码仓库分离。源码层面FindBeadsDir()会优先读取BEADS_DIR环境变量internal/beads/beads.go 相关实现随后buildRepoContext()通过isExternalBeadsDir()比较 CWD 与 beads 目录的git rev-parse --git-common-dir结果——两者不同即判定为重定向internal/beads/context.go此时RepoRoot取 beads 目录所在仓库的根。场景三Git Worktree用户在 worktree 中但.beads/存在于主仓库/project/ # Main repo ├── .beads/ ├── .worktrees/ │ └── feature-branch/ # Worktree (CWD) └── src/ $ cd /project/.worktrees/feature-branch $ bd dolt push # GitCmd() runs in /project (main repo, where .beads lives)git.GetMainRepoRoot()是 worktree 感知的它使用git rev-parse --git-common-dir找到共享的.git目录其父目录即主仓库根对应源码 GH#509 修复见 internal/git/gitdir.go。这样即便CWDRepoRoot指向 worktreeRepoRoot依然正确指向主仓库。场景四Worktree 与重定向叠加两者可以同时生效此时BEADS_DIR具有最高优先级$ cd /repo-a/.worktrees/branch-x $ export BEADS_DIR/repo-b/.beads $ bd dolt push # GitCmd() runs in /repo-b (BEADS_DIR takes precedence)RepoContext结构体通过CWDRepoRoot用户实际所在仓库与RepoRootbeads 数据所在仓库两个字段天然支持这种叠加状态两者可以不同。RepoContext 字段与解析流程字段一览字段说明BeadsDir实际的.beads/目录路径跟随重定向之后的结果RepoRoot包含BeadsDir的仓库根目录beads 的 Git 操作在此执行CWDRepoRoot用户 CWD 所在的仓库根可能不同IsRedirected为 true 表示BEADS_DIR/重定向文件指向了与 CWD 不同的仓库IsWorktree为 true 表示 CWD 位于 git worktree 中buildRepoContext 的六步解析流水线从 internal/beads/context.go 的实现可以还原完整解析顺序查找.beads/FindBeadsDir()依次检查BEADS_DIR环境变量、worktree 局部重定向、向上逐级目录遍历安全边界校验isPathInSafeBoundary()拒绝指向敏感系统目录的路径SEC-003检查重定向文件GetRedirectInfo()查找仓库本地.beads/redirect文件。值得注意的一个细节是bd-wayc3即使BEADS_DIR已被预设为重定向目标也会先检查仓库本地的.beads目录以探测重定向是否存在internal/beads/beads.go确定RepoRoot判定为重定向/外部仓库时取 beads 目录所在仓库根否则通过git.GetMainRepoRoot()解析获取CWDRepoRootgit.GetRepoRoot()不在 git 仓库时返回空串检查 worktree 状态git.IsWorktree()。重定向文件格式FollowRedirect()internal/beads/beads.go定义了重定向文件.beads/redirect的解析规则文件内容为目标路径支持空行与#注释相对路径以.beads/的父目录即项目根为基准解析只支持单层重定向不跟随重定向链以防止无限循环并保持行为可预测。安全机制hooks 禁用与路径边界校验Git Hooks 与模板禁用GitCmd()会显式禁用 git hooks 与模板防止在可疑仓库中执行任意代码对应源码注释中的 SEC-001/SEC-002cmd.Env append(os.Environ(), GIT_HOOKS_PATH, // Disable hooks GIT_TEMPLATE_DIR, // Disable templates )从源码实现看禁用 hooks 实际通过两条途径完成命令行参数-c core.hooksPath与环境变量GIT_TEMPLATE_DIRinternal/beads/context.go。这一保护针对的场景是BEADS_DIR指向不受信任的仓库其中可能包含恶意的.git/hooks/脚本。路径边界校验SEC-003GetRepoContext()会验证BEADS_DIR不得指向敏感系统目录。源码中 internal/beads/context.go 定义的unsafePrefixes黑名单包括Unix 系统目录/etc、/usr、/var、/root、/bin、/sbin、/opt、/privatemacOS 系统目录/System、/Library其他用户的 home 目录/Users/*与/home/*下非当前用户的路径。同时保留以下明确豁免从注释与代码演化可见这些是历次安全加固的成果系统临时目录os.TempDir()及其物理解析路径macOS 上/var/folders符号链接到/private/var/folders以及 FHS 标准的/var/tmpbe-odye4/var/homeFedora Silverblue / Bluefin 等系统的合法用户 homebe-kghzr/Users/SharedmacOS 官方指定的共享目录be-vc1。在判定其他用户 home时源码特意从系统账户数据库user.Current()读取当前用户 home 而非信任$HOME环境变量避免$HOME被篡改导致校验失效比较时按路径边界进行防止/home/aliceXX被误判为/home/alice的子路径。更精细的防护体现在符号链接处理上isPathInSafeBoundary()通过resolveLongestExistingAncestor()与resolvedPathWithinRoot()internal/beads/context.go先解析符号链接再判定包含关系。这是因为/var/folders、/Users/Shared、/var/tmp均为世界可写目录drwxrwxrwt攻击者可能在其中放置指向系统目录的符号链接——必须先解析再比较才能封堵这类路径穿越向量resolveLongestExistingAncestor还会沿路径向上解析最长已存在祖先使尚未创建的BEADS_DIR也能被安全规范化。服务端模式的上下文处理为什么 CLI 与服务端策略不同CLI 命令中GetRepoContext()使用sync.Once缓存结果因为CWD 在单次命令执行期间不会变化BEADS_DIR在命令执行期间不会变化反复进行文件系统访问是浪费的。但对于Dolt 服务器这类长驻进程缓存不再适用用户可能创建新的 worktreeBEADS_DIR可能通过 direnv 等工具动态变化多个 workspace 可能同时处于活跃状态。GetRepoContextForWorkspace按工作区全新解析服务端使用GetRepoContextForWorkspace()获得每次操作的全新上下文internal/beads/context.go// For server mode: fresh resolution per-operation (no caching) rc, err : beads.GetRepoContextForWorkspace(workspacePath) // Validation hook for detecting stale contexts if err : rc.Validate(); err ! nil { // Context is stale, need fresh resolution }该函数具备以下特性不做缓存每次调用都重新解析忽略BEADS_DIRworkspace 路径是显式传入的源码注释标记为 DMN-001测试TestGetRepoContextForWorkspace_IgnoresBEADS_DIR专门验证了这一点见 internal/beads/context_test.go正确解析 worktree 关系worktree 场景下通过GetMainRepoRoot()定位主仓库根验证路径仍存在并在结束时做安全边界与项目文件校验。实现上它先临时chdir到 workspace 目录完成后恢复原目录重置 git 缓存以保证全新解析再调用buildRepoContextForWorkspace()internal/beads/context.go。与 CLI 版相比该路径额外要求 workspace 必须位于 git 仓库内、.beads/目录必须真实存在且包含必要项目文件hasBeadsProjectFiles校验且返回的IsRedirected恒为 false——因为 workspace 上下文永远不视为重定向。Validate()方法internal/beads/context.go则用于检测缓存上下文是否过期只要BeadsDir或RepoRoot不再存在即返回错误DMN-002长驻进程可据此决定是否重新解析。迁移指南从分散解析到集中处理重构前分散解析func doGitOperation(ctx context.Context) error { // Each function resolved paths differently beadsDir : beads.FindBeadsDir() redirectInfo : beads.GetRedirectInfo() var repoRoot string if redirectInfo.IsRedirected { repoRoot filepath.Dir(beadsDir) } else { repoRoot getRepoRootForWorktree(ctx) } cmd : exec.CommandContext(ctx, git, -C, repoRoot, status) // ... }重构后集中处理func doGitOperation(ctx context.Context) error { rc, err : beads.GetRepoContext() if err ! nil { return err } cmd : rc.GitCmd(ctx, status) // ... }贡献者需要遵守的关键变更替换直接exec.Command调用一律使用rc.GitCmd()或rc.GitCmdCWD()移除手动路径解析RepoContext 已覆盖所有场景重定向、worktree、子目录测试中清理缓存在测试清理阶段调用beads.ResetCaches()。角色Role语义的补充说明源码还展示了 RepoContext 与用户角色模型的联动internal/beads/context.go当IsRedirected为 true 时Role()隐式返回Contributor外部仓库模式必然对应贡献者工作流否则从 git config 的beads.role读取。RequireRole()在角色未配置时返回ErrRoleNotConfigured以此触发初始化引导提示。这解释了为什么重定向场景与 fork 贡献追踪天然契合。测试策略缓存隔离由于GetRepoContext()使用进程级缓存测试之间必须显式清理否则前一个用例设置的 CWD 或BEADS_DIR会污染后续用例。官方推荐模式见 internal/beads/context_test.gofunc TestSomething(t *testing.T) { t.Cleanup(func() { beads.ResetCaches() git.ResetCaches() }) // Test code... }注意源码中的明确警告ResetCaches()不是线程安全的只能在单线程测试上下文调用internal/beads/context.go。git.ResetCaches()同理用于重置仓库根、worktree 状态等 git 信息的缓存internal/git/gitdir.go。相关文档Git Worktree 集成多仓库路由BEADS_DIR 与环境变量配置实现要点总结CLI 路径结果通过sync.Once缓存因为命令执行期间 CWD 与BEADS_DIR都不会变化使用cmd.Dir模式而非-C标志执行 Git 命令更符合 Go 惯用法针对 git hooks 执行与路径穿越分别实现了 SEC-001/SEC-002禁用 hooks/模板与 SEC-003路径边界校验两层安全缓解GIT_DIR/GIT_WORK_TREE的显式设置修复了 worktree 场景下的 pathspec outside repository 问题GH#2538长驻服务必须使用GetRepoContextForWorkspace()做无缓存、按工作区的全新解析并辅以Validate()检测上下文过期。【免费下载链接】beadsBeads - A memory upgrade for your coding agent项目地址: https://gitcode.com/GitHub_Trending/beads1/beads创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表