这个项目能做什么
9vcs 是一款基于 9P(Plan 9 协议)并利用作者的 Go 9p 库构建的版本控制系统。它面向希望在没有托管平台的情况下实现真实版本控制的小型信任团队。历史记录被保存为一组内容寻址的补丁而非快照;README 指向 PLAN.md 以说明设计原理,而自身则专注于实际用法。其词汇表刻意避开了 GitHub 风格:没有 clone、push、pull、fork 或 pull request,一份速查表将常用术语映射到了 9vcs 的等效项上。
要求与安装
唯一要求是 Go 1.26.5 或更高版本。9vcs 及其构建所需的 9p 库均为纯 Go 编写,仅使用标准库,无需守护进程、数据库或外部服务。预编译的二进制文件以 tar.gz 归档形式发布在 Releases 页面,支持 linux/amd64、linux/arm64、darwin/arm64 和 darwin/amd64,每份均附有 .sha256 校验和用于验证;归档包含二进制文件、LICENSE 和 README。从源码构建只需针对 cmd/9vcs 执行一条 go build 命令。此外,还提供了一些安装程序。
单人工作流
在目录中运行 init 后,status 会为每个变更路径打印一行:A 表示新增,M 表示修改,D 表示删除,U 表示未解决的冲突;它仅报告脏数据,而非 diff。移动操作不被追踪为重命名——移动的文件显示为一次删除加上一次新增,这与底层补丁的存储方式一致。diff 显示工作树中的行级变更,可以针对另一个点进行 diff,或者在不涉及工作树的情况下直接比较两个点。log 按时间倒序列出记录的补丁,包含作者、指纹或签名状态以及受影响的路径。record 创建补丁。这里没有暂存区(staging area)或索引步骤:工作树本身就是暂存区,record 将其与当前 head 进行 diff。
分支与合并
branch 用于列出、从 HEAD 或另一个分支/哈希创建分支;checkout 用于切换分支,或通过 -b 标志创建并切换。合并会产生真实的补丁图冲突——行级分叉、带有比较侧边文件的二进制冲突以及修改/删除竞争——而非浅层的三路文本 diff。干净的合并通过 record 完成。存在冲突的合并会在受影响的文本文件中留下行内冲突标记,而二进制冲突则会生成一个以路径和短哈希命名的侧边文件用于比较;用户编辑后执行 record。合并可以被中止(abort),将工作树完全恢复到合并前的状态并清除合并状态,无论该合并是通过 merge 命令还是 apply 触发的。
忽略模式
仓库根目录下的 .9vcsignore 文件作用类似于 .gitignore,旨在被记录并共享。每行一个模式;空行和井号注释会被跳过。不含斜杠的模式在任何深度匹配,包含斜杠的模式锚定在仓库根目录,末尾斜杠将匹配限制在真实目录。明确不支持感叹号否定模式和双星号模式。忽略模式仅使新文件不进入 record 和 diff,且后续添加模式不会使已记录的文件显示为删除。
团队使用
每次安装在首次使用时会生成一对长期的 Ed25519 密钥对,identity show 会打印出指纹,团队成员通过带外方式交换指纹以相互授权。托管共享历史的人运行 serve 监听端口;服务器读取当前仓库中的 .9vcs/authorized-peers 文件,每行包含一个成员的指纹和权限:read(仅拉取历史)、propose(读取并向 offers 区域提交签名 bundle)和 write(所有权限,包括移动分支引用)。没有 add-collaborator 命令——只需编辑该文件即可——且该文件在启动时读取一次,因此更改需要重启,这在撤销可能泄露的密钥时至关重要。
serve 在前台运行,没有后台守护进程。网络 push 会拒绝移动服务机上当前检出的分支,因此主机应检出不同分支或使用专门用于服务的目录。新成员通过 init 和 import 开始,传入对端指纹、主机、端口及分支名;import 仅支持快进(fast-forward),拉取该分支及其传递依赖的所有补丁和 blob。reconcile 是持续同步命令:如果对端领先则拉取,如果本地领先则推送;在出现真实分叉时,它会获取缺失内容并要求用户在本地检出并合并,而非在网络上传输时解决。这两个命令都可以显式指定对端指纹,否则使用本地的“首次使用信任”(TOFU)存储,对新地址提示一次,静默检查已知地址,并在指纹突然变更时发出强烈警告。
离线交换与提案
Bundle 可以在没有运行服务器的情况下移动变更:export 将签名 bundle 写入文件,用于通过电子邮件、聊天或可移动介质传输;接收者使用 bundle show(查看签名者、消息、补丁)检查,使用 import 验证签名并本地存储而不触动任何引用,使用 diff 审查,然后 apply 并 record。import 本身绝不会移动引用。仅具有 propose 权限的成员可以使用 offer 直接提交,将签名 bundle 发布到维护者的服务器;维护者列出待处理的 offer,通过 apply 获取、验证并本地存储,随后将其集成并从队列中删除。由于 offer 仅存在于活动服务器命名空间中,维护者需连接到自己的 serve 实例,因此其自身的指纹也需要出现在 authorized-peers 文件中。
9sh 窗口与命名空间处理
通常仓库通过从当前目录向上遍历来解析,类似于 git。在 9sh(Plan 9 风格的 shell)中,放在命令名前的 -C 标志提供了第二条路径:它首先尝试 9sh 命名空间中的路径——local 下的相对路径,或可能解析为 9sh 已绑定内容的绝对路径——然后再回退到字面 OS 路径。在 9sh 会话之外,该标志的行为与 git -C 一致。
恢复
记录的恢复路径包括:中止 merge 或 apply;记录或手动丢弃阻塞 checkout、merge 或 apply 的未提交变更;使用 restore 将单个路径恢复到 head 的记录状态(无记录状态的路径将被删除,因此还原重命名意味着需要指定两个路径);以及重新绑定合法变更的对端指纹。
项目状态
README 将使用 9vcs 与开发该工具区分开。工具开发遵循基于主干(trunk-based)的开发模式,使用短寿命分支,设有禁止直接 push 的受保护 master 分支,在合并前通过涵盖 build、vet、gofmt 和启用 race 检测的测试 CI。仅支持 squash 合并并自动删除分支。版本控制遵循 release 的 semver 规范,目前处于 v0.x 阶段。在 v1.0.0 之前,不对磁盘上的补丁和 bundle 格式提供兼容性承诺,这些格式可能会在没有迁移路径的情况下直接变更;从 v1.0.0 开始,任何在已发布格式下记录的补丁或 bundle 都将被声明为可解码,未来的不兼容变更将通过真实版本分发和随附的迁移工具来处理。changelog 记录了每个版本的发布内容。
评论
0 评分人数达到10人后显示
登录后参与讨论。