ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Files.md如何解决文件冲突?LCS动态规划合并算法全拆解

Files.md如何解决文件冲突?LCS动态规划合并算法全拆解 Files.md如何解决文件冲突LCS动态规划合并算法全拆解【免费下载链接】files.md Private, quiet space for thinking. Simple app for .md files.项目地址: https://gitcode.com/GitHub_Trending/fi/files.mdFiles.md 是一个 local-first 的 Markdown 笔记应用它内置的同步服务器在两台设备同时修改同一个.md文件时不会让你手动选保留哪个版本而是用 LCS最长公共子序列动态规划算法自动合并两边的改动——两边新增的内容一个都不丢。本文带你完整拆解 server/sync/merge.go 中的这套冲突合并算法从冲突如何被检测、DP 表怎么填到回溯如何拼出最终结果。文件冲突是怎么产生的Files.md 支持多端同步浏览器 PWA 和 Telegram 机器人可以同时编辑你的笔记数据汇聚到同一个服务器服务器为每个文件记录mtime内容修改时间。当客户端上传文件时服务器会比较两边的时间戳只有客户端改了→ 直接写入客户端版本只有服务器改了→ 把服务器版本发回客户端两边都改了→ 触发Merge(serverContent, clientContent)自动合并后再落盘。这个判断逻辑在批量同步 server/sync/sync.go#L153-L167 和单文件同步 server/sync/sync.go#L334-L351 中各出现一次合并成功后服务器会返回merged状态通知客户端。完整的请求时序可以阅读官方文档 docs/sync-flow.md。合并算法第一步拆行 构建 DP 表Merge()的思路可以概括为一句话找出两边都有的骨架最长公共子序列把各自的独有内容按顺序补回去。第一步很简单把两个字符串按\n拆成行数组。第二步是动态规划。代码创建一个(行数11) × (行数21)的表格lcsLength每个格子lcsLength[i][j]表示lines1 的前 i 行与 lines2 的前 j 行之间的最长公共子序列长度。填表规则只有两条见 server/sync/merge.go#L41-L49当前两行相同lcsLength[i][j] lcsLength[i-1][j-1] 1沿对角线加一当前两行不同lcsLength[i][j] max(lcsLength[i-1][j], lcsLength[i][j-1])取上方和左方的较大值举个例子合并A / B / C和A / C / D⌀ACD⌀0000A0111B0111C0122右下角的 2 就是 LCS 长度A和C而填表过程中的最大值来源则隐含了合并路径信息。第二步回溯 DP 表重建合并结果有了表格backtrack()从右下角(i, j)往左上角走每一步只做一个决策见 server/sync/merge.go#L60-L84两行相同→ 这行只输出一次沿对角线走i-1, j-1——这是去重的关键上方值更大→ 输出 lines1 的当前行向上走左方值更大或相等→ 输出 lines2 的当前行向左走。这样走下来公共行只出现一次独有行按原顺序各就各位。看两个真实测试用例摘自 server/sync/merge_test.go场景一共同前缀、分歧结尾两台设备都在文末加了不同内容版本Aline 1 / line 2 / line 3 / line original 4 版本Bline 1 / line 2 / line 3 / line modified 4 合并后line 1 / line 2 / line 3 / line original 4 / line modified 4场景二中间完全分叉、首尾相同版本Aheader / header / original A / original B / footer / footer 版本Bheader / header / modified X / modified Y / footer / footer 合并后header / header / original A / original B / modified X / modified Y / footer / footerTestJournal用例更贴近真实场景服务器版本里有 ate good客户端版本里有 went for hiking合并后两条都保留且顺序合理——这就是无冲突感知体验的来源你永远不需要面对红色的冲突标记。特殊处理日记日期的 Emoji 自动去重合并Files.md 的日记journal文件按日期分节标题形如#### 23 May, Friday。两台设备给同一天追加不同的习惯 Emoji运动、喝水、步数时朴素 LCS 合并会得到两行几乎相同的日期标题。为此Merge()在拼出结果后还会调用mergeEmojisInJournalHeaders()做后处理用正则把连续的日期标题行分组同时兼容旧的####和新版##两种格式确认组内每行都以同一日期开头把行尾的 Emoji 全部收集起来借助uniseg按 Unicode 字素grapheme去重保证像‍♂️这样的组合 Emoji 不会被拆坏输出一行合并后的标题例如#### 23 May, Friday ‍♂️。#### 23 May, Friday ‍♂️ ← 版本A #### 23 May, Friday ← 版本B #### 23 May, Friday ‍♂️ ← 合并后相关行为由 server/sync/merge_test.go 中一组TestMergeHeaders*用例覆盖包括日期不同不合并、Emoji 重叠去重、含肤色修饰符的组合 Emoji 等边界情况。为什么这样设计这套合并逻辑全部集中在 server/sync/merge.go 一个文件里核心不到 160 行没有引入三方合并3-way merge的 blob 存储没有 AST没有冲突标记语法。这正是项目奉行的低认知负载哲学——代码少一点整个系统就灵活一点。对普通用户而言这套算法的意义在于你在笔记本上写完一段又在手机上给同一天加了个习惯 Emoji下次打开应用时两个改动已经安静地合在了一起。冲突解决从用户要做的作业变成了服务器顺手做的事。想深入的话可以从 server/sync/merge.go 读起配合 server/sync/merge_test.go 里的 40 个测试用例每个用例都是一个现成的算法示例——这也是理解 LCS 动态规划最直观的教材。【免费下载链接】files.md Private, quiet space for thinking. Simple app for .md files.项目地址: https://gitcode.com/GitHub_Trending/fi/files.md创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表