Hotdry.
systems

无提交版本控制:基于持久化对象图的分支与合并实现

借鉴 Unfudged 的连续记录理念,探讨如何用持久化对象图实现无需快照提交的分支、合并,支持无缝历史导航与协作。

在传统版本控制系统中,如 Git,每一次提交都会创建一个快照,导致工作区与历史分离,用户需显式 commit 来保存状态。这种 “提交导向” 的范式在 AI 代理频繁修改代码、配置文件等场景下容易丢失中间状态。Unfudged 等工具通过文件系统事件监控实现连续记录,但仍停留在文件层面,缺乏真正的对象级分支与合并能力。本文聚焦单一技术点:使用持久化对象图(persistent object graphs)构建 “无提交” 版本控制系统,实现工作状态即历史状态的无缝导航与协作。

持久化对象图的核心原理

持久化对象图是一种纯函数式数据结构,每一次修改不就地变异原有对象,而是创建新版本的对象图,与旧版本通过结构共享(structural sharing)高效共存。每个对象节点包含内容哈希、字段值及引用指针,根节点代表整个图的版本标识。

例如,在 Rust 中,可使用 im 库或自定义 hash consing 实现:

  • 对象 ID:BLAKE3 哈希(32 字节,防碰撞)。
  • 版本根:指向根对象的哈希。
  • 修改时:递归复制变更路径,仅新分配受影响节点(路径复制,amortized O (log N))。

这种设计避免了 “fudging”(临时序列化黑客),直接在内存 / 存储中操作图结构,支持任意时间点查询。

证据显示,这种方法在知识图谱版本控制中已验证有效:ConVer-G 等系统使用类似并发版本化图存储,实现多用户协作无锁访问。[1]

无提交工作流程:工作区即版本

传统 Git 有 staging area 与 commit 间隙;无提交 VC 将工作区视为 “活版本”(live version),每改动自动推入版本图。

实现参数:

  • 监控 debounce:3 秒智能去抖(如 Unfudged),融合连续编辑为单版本,CPU 占用 <1%。
  • 变更捕获:内存中对象引用追踪(RC/GC hooks)或代理(proxy objects)拦截 setField 操作。
  • 自动版本化:每 debounce 周期,计算新根哈希,追加到线性历史链(或 DAG)。

示例伪代码(Go):

type Obj struct {
    Hash   [32]byte
    Fields map[string]any
    Refs   []HashRef
}

func Update(root Hash, path []string, newVal any) Hash {
    // 路径复制:仅更新叶节点
    newObj := copyPath(root, path)
    newObj.Fields[path[len(path)-1]] = newVal
    return hashObj(newObj)
}

工作区始终指向最新根哈希,git status 等价 current_root := workspace.Root()

分支与合并:版本图而非快照链

无需显式 commit,快照隐含在对象图中。分支即新根从父根衍生:

  • 分支branch := Update(parent_root, path, val),新根引用共享父图。
  • 历史导航:从根回溯父指针,O (1) 访问任意祖先。
  • 合并:三路合并(ancestor, left, right),递归 diff 图节点:
    • 相同哈希:共享,无冲突。
    • 字段 diff:应用自定义 resolver(如 last-write-win 或 CRDT)。

落地清单:

  1. 存储后端:SQLite + content-addressed blobs(ACID 事务,每版本一事务)。
    • 表:objects(hash PRIMARY KEY, data BLOB)versions(root_hash, parent_hash, ts, author)
    • 保留策略:24h 全版本、7 天小时级、30 天日级(自动 prune)。
  2. 并发控制:乐观锁(版本哈希比较),冲突时重试或提示。
  3. 协作参数
    • P2P 同步:Gossip 根哈希 + delta patches(仅变更路径序列)。
    • 冲突阈值:>10% 图变更时要求手动 merge。
  4. 性能阈值
    指标 阈值 监控点
    CPU <1% 采样 debounce 周期
    RAM <100MB / 项目 对象池复用
    存储增长 <1MB / 小时 去重率 >90%
    Merge 时间 <5s (10k 节点) 图 diff 深度优先

回滚策略:restore(root_hash) 原子切换工作根,支持 dry-run 预览 diff。

优势与风险限界

相较文件级 Unfudged(仅线性回放),对象图支持语义合并:如两个分支同时改 User#123 的 email,自动检测并提示,而非文件覆盖。

风险:

  • 循环引用:显式 ref 计数 + cycle detection。
  • 大图膨胀:强制共享 + 定期 compact(rewrite DAG)。

在 AI 协作场景:代理修改对象图,人类 log --since 30m 查看 bursts,restore --at burst_start 回退。

此方案参数化强,可嵌入 IDE(如 VSCode extension)或 LangChain agents,支持多模型无缝切换上下文。

资料来源: [1] https://unfudged.io - Unfudged 文件记录器实现参考。 [2] https://news.ycombinator.com/item?id=36952796 - Jujutsu commitless 讨论,启发工作区即 commit 理念。

(正文字数:1028)

查看归档