架构导读
本指南回答一个问题:应用提交了一次 SQLite 事务以后,那次变化如何变成可比较、 可合并、可同步、可恢复的 Graft 历史?
它不是 CLI 命令手册,也不要求你先熟悉源码。我们会一直使用同一个例子:仓库里有
app.sqlite、settings.json 和一个较大的 attachments/report.pdf。你会看到这三类
内容如何经过不同的数据通道,最后在同一个 repository commit 中汇合。
先记住:Graft 同时维护两层历史
Section titled “先记住:Graft 同时维护两层历史”这是理解整个项目最重要的一句话。
应用层历史(repository history)branch/ref -> repository commit -> tree -> path blob | +-- file bytes / external pointer +-- SQLite snapshot descriptor
SQLite 存储历史(storage history)snapshot descriptor -> VolumeId + ordered log ranges | +-- (LogId, LSN) storage commits | +-- changed 4 KiB pagesrepository commit 回答“这个应用版本包含哪些路径”;storage commit 回答“某个 SQLite 快照相对前态改变了哪些 4 KiB 页”。二者不是同一个 commit,也没有一一对应关系:
- 一次 repository commit 可以同时引用多个 SQLite 快照和普通文件;
- 一次
graft add app.sqlite可能先产生 storage commit,但还没有 repository commit; - 如果数据库没有净变化,
add可以直接复用旧 snapshot,不产生新的 storage commit; - branch ref 只指向 repository commit,不直接指向 LSN。
一次变化经过的五个状态
Section titled “一次变化经过的五个状态”以向 notes 表插入一行为例:
1. worktree app.sqlite 与可能存在的 app.sqlite-wal 被普通 SQLite 修改 | | graft add v2. consistent private image(临时) SQLite online backup 得到只含已提交 transaction 的独立镜像 | | 4 KiB chunk comparison v3. storage snapshot 变化页进入 immutable segment,产生 (LogId, LSN) storage commit | | snapshot descriptor v4. index path app.sqlite 的 stage 0 指向这份精确 snapshot | | graft commit v5. repository history 写 blob -> tree -> commit,最后移动 refs/heads/main第 2 步只是捕获边界,不是新的工作区文件。第 4 步才是“下一次 commit 会提交什么”的
权威答案。commit 不会重新读取后来又发生变化的 app.sqlite。
用四个问题读任何操作
Section titled “用四个问题读任何操作”当你不确定一条命令是否安全,依次问:
- 它读哪个状态? worktree、index、某个 commit,还是 active merge 的一侧?
- 它写哪个状态? storage、objects、index、ref、merge journal、remote,还是物理 worktree?
- 谁是结果的 identity? 文件字节 hash、object ID、snapshot descriptor,还是
(LogId, LSN)? - 失败后哪个记录仍是权威的? ref、index、merge journal,还是可重建的 cache?
后续各章都会沿用这四个问题。
完成 CLI 或 SDK 快速开始后再阅读本节。这里解释安全接入所需的系统边界;具体操作 顺序以任务指南为准,精确契约以规范为准。
- 仓库、对象与引用:先看
.graft/里谁是数据、谁是指针、谁只是 cache。 - SQLite 如何变成快照:理解一致备份、4 KiB page、log 与 LSN。
- 从 add 到 commit:逐文件观察命令前后的状态变化。
- 从快照回到工作区:理解 hydrate 与 materialize,以及为什么要关闭数据库 handle。
- Diff、Merge 与冲突:看物理 page 如何重新被解释成 row/schema 变化。
- 远端、失败与恢复:理解发布顺序、CAS 和可恢复边界。
- 动手观察每一次变化:亲手建立一个仓库并检查
.graft/。
什么是契约,什么只是当前实现
Section titled “什么是契约,什么只是当前实现”路径身份、object/ref/index 语义、快照内容、merge 结果和 remote publication 顺序是 可观察契约。Fjall 的私有 key encoding、cache 文件名、Rust 模块边界和临时文件命名是 实现细节。调试时可以观察它们,但不要让产品集成依赖它们。
当前代码中也不存在“通过 SQLite PRAGMA 控制仓库”这条兼容层。受支持的集成面是
graft CLI 及其 JSON 输出、Node.js SDK 和 HTTP remote packages。