Remotely Save 同步算法 v1 决策表全解析:三类记录源、11 条互斥完备分支与源码印证
数据同步【免费下载链接】remotely-saveSync notes between local and cloud with smart conflict: S3 (Amazon S3/Cloudflare R2/Backblaze B2/...), Dropbox, webdav (NextCloud/InfiniCLOUD/Synology/...), OneDrive, Google Drive (GDrive), Box, pCloud, Yandex Disk, Koofr, Azure Blob Storage.项目地址https://gitcode.com/gh_mirrors/re/remotely-save点击查看免费下载导读Remotely Save 插件在每次同步时都要回答一个核心问题面对同一路径下本地、远端两边各自的状态到底该上传、下载、删除还是什么都不做docs/sync_algorithm/v1/README.md 给出的答案是一张 11 行的互斥且完备mutually exclusive and collectively exhaustive的决策表——以本地文件、远程文件、本地删除/重命名历史三类记录源为输入穷举所有组合并给出唯一决策。本文将完整还原这张决策表的全部语义并结合仓库源码src/baseTypes.ts、pro/src/sync.ts、src/fsAll.ts 等逐条印证其字段来源、判断逻辑与实现分支帮助你理解 Remotely Save 乃至 v2/v3 后续版本同步算法设计的底层脉络。一、算法定位v1 在同步算法文档体系中的位置仓库的 docs/sync_algorithm/README.md 将同步算法按版本组织为三档v1docs/sync_algorithm/v1/README.md——最早的同步决策算法本文主体v2docs/sync_algorithm/v2/README.md——在 v1 基础上增加远程删除历史这一第四类记录源v3面向最终用户的说明见 docs/sync_algorithm/v3/intro.md设计文档见 docs/sync_algorithm/v3/design.md。从源码结构看v1 的决策思想至今仍沉淀在代码中当前核心同步逻辑位于 pro/src/sync.ts其文件头部注释明确写道同步算法基本上遵循 syncrclone 的思路并且内部大量使用decisionBranch编号1~10来标注决策分支——这些编号与 v1 决策表中的 ID 一一对应详见下文第四节。因此读懂 v1 决策表是理解当前实现中大量分支注释的钥匙。二、三类记录源Sourcesv1 算法假设同步时掌握三类记录源1. 本地文件Local files通过扫描 vault 本地全部文件获得。v1 文档特别指出Obsidian 本身提供了直接返回该列表的 API插件无需自行递归遍历目录。在源码层面扫描能力被抽象为FakeFs.walk()接口见 src/fsAll.ts本地文件系统实现fsLocal与所有远程文件系统实现都实现同一套抽象当前同步流程中本地列表通过fsLocal.walk()获取见 pro/src/sync.ts 的syncer函数第 4 步step 4。2. 远程文件Remote files通过扫描远程服务上的全部文件获得。v1 文档强调了两类服务的差异部分服务直接提供列表 API如 Dropbox、OneDrive 等部分服务需要插件递归扫描文件夹如 WebDAV、S3 等对应FakeFs抽象中的walk()/walkPartial()两个接口src/fsAll.ts。在加密模式下远程列表实际经由加密文件系统fsEncrypt.walk()获取见 pro/src/sync.ts以便拿到解密后的逻辑路径与元数据。3. 本地删除/重命名历史Local delete-or-rename history这是 v1 能感知删除的关键来源插件通过Obsidian 的 tracking API记录用户在 vault 内的删除与重命名操作。由此引出 v1 文档明确标注的一个重要前提如果用户在 Obsidian之外删除或重命名文件/文件夹插件无能为力——因为本地删除历史记录不到这类操作。这与实际删除行为互相印证查看 src/main.ts 中trash()的实现本地删除会优先尝试trashSystem()系统回收站失败则回退trashLocal()Obsidian 回收站删除行为本身被 Obsidian 记录成为后续同步决策中删除历史的数据来源。前提假设所有来源可靠v1 文档在列举三类记录源之后明确给出假设Assuming all sources are reliable假设所有来源都是可靠的。这意味着决策表不再为数据源本身出错兜底——本地列表、远程列表、删除历史三者都假定如实反映真实状态。这一假设也是后续理解各种边界行为如加密模式、同名冲突的前提。三、核心决策表11 条互斥且完备的组合v1 算法的核心方法论是把本地是否存在 × 远程是否存在 × 历史是否存在的所有组合全部列出使组合之间互斥mutually exclusive、组合集合完备collectively exhaustive再为每个组合附加额外条件mtime、size、密码模式等给出唯一决策。下表是 v1 文档的完整决策表原样继承IDRemote FilesLocal filesLocal delete rename historyExtraDecision1existexistignoremtime_remote mtime_localdownload remote file, create local folder if not exists, clear local history if exists2existexistignoremtime_remote mtime_localupload local file, create remote folder if not exists, clear local history if exists3existexistignoremtime_remote mtime_local password size_remote size_localclear local history if exists (the file was synced and no changes after last sync)4existexistignoremtime_remote mtime_local password size_remote ! size_localupload local file, clear local history if exists (we always prefer local to remote)5existexistignoremtime_remote mtime_local password ! clear local history if exists (in encryption mode, file sizes are unequal. we can only rely on mtime(s))6existexistignoreIf local is a folder. mtime_local undefinedclear local history if exists. TODO: what if a folder and a previous file share the same name?7existnot existexistmtime_remote delete_time_localdownload remote file, create folder if not exists8existnot existexistmtime_remote delete_time_localdelete remote file, clear local history9existnot existnot exist—download remote file, create folder if not exists10not existexistignorelocal may be folder or fileupload local files recursively, create remote folder if not exists, clear local history if exists11not existnot existignore—clear local history if exists3.1 决策字段说明表格中的 Extra 列使用了几组关键字段它们在源码中的实体定义中都能找到对应字段语义源码对应mtime_remote/mtime_local远程/本地文件的最后修改时间Unix 毫秒时间戳src/baseTypes.ts 中Entity.mtimeCli客户端 mtime、mtimeSvr服务端 mtimedelete_time_local本地删除历史中记录的删除时间src/baseTypes.ts 中FileOrFolderMixedState.deltimeLocalsize_remote/size_local远程/本地文件大小Entity.size、sizeEnc加密后大小、sizeRawpassword是否开启加密密码空串表示未加密src/baseTypes.ts 中RemotelySavePluginSettings.password值得注意 src/baseTypes.ts 中仍保留着一个已标记deprecated的FileOrFolderMixedState接口其中mtimeLocal、mtimeRemote、deltimeLocal、deltimeRemote、sizeLocal、sizeLocalEnc、sizeRemote、sizeRemoteEnc、decision、decisionBranch等字段正是 v1 决策表输入与输出在类型层面的直接投影。3.2 逐类组合的判定逻辑解读情形 A双端都存在ID 1~6——以 mtime 定胜负以 size 和密码模式做补充判据ID 1远程更新mtime_remote mtime_local判定远程文件在本地之后被修改决策为下载远程文件并视需要创建本地文件夹、清理本地历史。ID 2本地更新mtime_remote mtime_local对称地上传本地文件并视需要创建远程文件夹、清理本地历史。ID 3完全一致mtime 相同、未加密、大小也相同说明上次同步后双方都没有变化唯一要做的动作是清理本地删除历史。ID 4mtime 相同但大小不同未加密且 mtime 相同、大小却不同这是一个异常状态。v1 的决策是上传本地文件并注释了核心原则we always prefer local to remote始终优先本地。这样设计可以打破双方各自认为对方不同的循环让同步收敛。ID 5加密模式password ! 时由于加密后密文长度与明文不同大小字段必然不可比密文长度通常不等于明文长度因此 v1 明确我们只能依赖 mtime。决策同样只是清理本地历史。这与当前实现中Entity.sizeEnc加密后大小的设计相呼应——加密场景下大小判据被有意弃用。ID 6本地是文件夹文件夹本身没有 mtimemtime_local undefined此时只需清理本地历史。文档在此处留下一个明确的 TODO如果一个文件夹与之前的某个文件同名该怎么办——说明 v1 对文件/文件夹同名互变这类边界情况并未完全收敛。情形 B仅远程存在ID 7~9——用删除历史区分被删了还是本就没有ID 7本地有删除历史且远程更新mtime_remote delete_time_local说明远程文件在本地删除之后还被修改过比如另一个设备上重新编辑过因此删除历史过期了决策为下载远程文件恢复本地。ID 8本地有删除历史且远程更旧mtime_remote delete_time_local说明远程文件自本地删除以来从未被修改本地删除是最新操作决策为删除远程文件并清理本地历史——这正是删除在设备间传播的机制。ID 9本地无删除历史说明该远程文件是纯新建的直接下载远程文件并视需要创建本地文件夹。情形 C仅本地存在ID 10本地文件可能是文件也可能是文件夹在远程不存在判定为本地新建决策为递归上传本地文件文件夹则整棵递归并视需要创建远程文件夹、清理本地历史。情形 D双端都不存在ID 11只有删除历史中存在该路径说明它早已被删除且同步过唯一动作是清理本地历史避免历史记录无限累积。四、决策表与当前源码的对应关系虽然当前主流程已演进到 v3使用prevSync历史记录但 v1 的决策编号在 pro/src/sync.ts 中仍有清晰的痕迹文件分支的decisionBranch注释保留了 1~10 的编号。结合分支注释与 v1 决策表可以推断出如下对应v1 决策 IDv1 语义源码 decisionBranch可推断对应源码决策名1远程较新 → 下载9remote_is_modified_then_pull2本地较新 → 上传10local_is_modified_then_push3完全一致 → 清历史2equal4mtime 同、size 不同 → 上传本地10local_is_modified_then_push8远程被本地删除且未再修改 → 删除远程4local_is_deleted_thus_also_delete_remote9远程新建 → 下载3remote_is_created_then_pull10本地新建 → 上传6local_is_created_then_push11双端均无 → 清历史1only_history上表中带可推断标注的对应关系是从分支编号与决策名的语义匹配得出的其余如 ID 5、6、7因在 v3 中被prevSync机制重构不再有一一对应的独立分支这正是算法演进的体现。此外v1三类记录源的收集方式在当前实现中同样可追溯ensembleMixedEntiespro/src/sync.ts依次把remoteEntityList远程列表、prevSyncEntityList上次同步记录、localEntityList本地列表汇总为以key为键的MixedEntity映射——一个路径下挂载remote/prevSync/local三个视角再统一决策。这可以看作 v1 决策表在工程实现上的直接延续。五、v1 的边界情况、已知问题与设计取舍5.1 加密模式下的判据退化ID 5 是 v1 中最具代表性的设计取舍开启密码加密后由于密文与明文长度不相等size判据整体失效决策只能退回仅依赖 mtime。这意味着在加密模式下任何依赖大小比较的冲突消解如始终优先本地的 ID 4 逻辑都无法启用只能信任时间戳。5.2 删除历史只覆盖 Obsidian 内的操作三类记录源中只有本地删除/重命名历史依赖 Obsidian 的 tracking API因此在文件管理器或命令行中直接删除/重命名文件是感知不到的。这是 v1 最明显的使用约束要保证删除能正确同步请在 Obsidian 内完成删除/重命名操作。5.3 缺少远程删除历史v1 只有本地删除历史没有远程删除历史。这意味着如果用户在另一台设备或直接在远程服务管理界面删除了文件v1 无法得知远程曾经存在过该文件也就无法把删除传播回本地。这正是 v2 版本引入第四类记录源——远程删除历史的直接动因见 docs/sync_algorithm/v2/README.md其远程删除记录的数据结构在 src/metadataOnRemote.ts 中实现为DeletionOnRemote { key, actionWhen }随元数据文件_remotely-save-metadata-on-remote.json/.bin上传到远端。5.4 文件夹 mtime 缺失与同名 TODOID 6 暴露了两个未闭合的边界文件夹没有可靠的 mtimemtime_local undefined且文件夹与历史文件同名的情形在 v1 中只留下了 TODO。文件夹的决策只能基于是否存在无法参与时间比较。六、从 v1 到 v2/v3决策表如何演进把 v1 与 v2 文档并排对比可以看到同步算法演进的清晰脉络维度v1v2当前实现v3记录源数量3 类本地、远程、本地删除/重命名历史4 类新增远程删除历史以prevSync上次同步记录为核心MixedEntity.prevSync见 src/baseTypes.ts时间判据mtime_remote与mtime_local两两比较收集mtime_remote、mtime_local、deltime_remote、deltime_local四个时间戳尊重最大值及其对应操作基于prevSync的 mtime/size 对比localEqualPrevSync/remoteEqualPrevSync决策组织一张 11 行决策表按文件/文件夹分别组织文件表按四个时间戳排序展开decisionBranch编码 四步执行流水线标记已同步 → 建文件夹 → 删 → 并行上传下载见 pro/src/sync.ts 的doActualSync删除传播仅本地 → 远程依赖本地删除历史双向传播本地 ↔ 远程各自有删除历史双向传播 特殊保护如.obsidian/与bookmarks.json永不删除v2 文档在其文件决策表中保留了一列标注与 v1 决策编号1~10的等价关系例如远程在本地删除后未再修改 → 删除远程文件对应 v1 的 ID 8仅本地存在 → 上传本地对应 v1 的 ID 10下载远程的情形对应 v1 的 ID 1/7/9可见 v1 的决策骨架被 v2 完整继承只是在输入侧增加了远程删除历史、在输出侧细化了双向删除语义。七、总结与实践启示v1 决策表的价值不在于它还活着而在于它用一张穷举表讲清了同步的本质把两端的文件状态与删除历史映射为互斥完备的组合再按时间戳为主、大小与加密模式为辅的规则收敛到唯一动作。结合本仓库源码可以提炼出几条对使用者有直接意义的结论在 Obsidian 内删除/重命名否则本地删除历史缺失v1及后续版本的部分场景将无法把删除传播到远程开启加密密码后大小判据失效同步只信任 mtime因此不同设备间的时间同步NTP质量直接影响冲突判断的准确性始终优先本地ID 4是 v1 消解双方看似都不同僵局的关键设计理解它有助于预判多设备同时编辑时的行为当前主流程已演进到 v3但 pro/src/sync.ts 中 1~10 的decisionBranch注释、src/baseTypes.ts 中已弃用的FileOrFolderMixedState字段都是 v1 决策表留在代码里的历史印记——阅读旧代码与调试历史同步行为时这张表依然是最快的索引。如需进一步探索后续版本可继续阅读 docs/sync_algorithm/v2/README.md四时间戳决策表与 docs/sync_algorithm/v3/design.md当前设计文档。赞分享数据同步【免费下载链接】remotely-saveSync notes between local and cloud with smart conflict: S3 (Amazon S3/Cloudflare R2/Backblaze B2/...), Dropbox, webdav (NextCloud/InfiniCLOUD/Synology/...), OneDrive, Google Drive (GDrive), Box, pCloud, Yandex Disk, Koofr, Azure Blob Storage.项目地址https://gitcode.com/gh_mirrors/re/remotely-save点击查看免费下载相关推荐Agent Zero Launcher 桌面端使用指南安装实例、管理容器与跨大版本升级Agent Zero Launcher 桌面端使用指南安装实例、管理容器与跨大版本升级 Agent Zero Launcher 是 Agent Zero 官方数据同步上一篇Modern-CPP-Programming性能优化三部曲提升C代码效率的完整教程下一篇gh_mirrors/es/es6features实战数组去重最佳实践创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考