jj 与 Sapling 对比指南:从工作副本快照到冲突处理、撤销与 Forge 工作流的全面差异分析
发布时间:2026/9/10 12:56:41 作者:尧图编辑部 阅读量:1,286

jj 与 Sapling 对比指南从工作副本快照到冲突处理、撤销与 Forge 工作流的全面差异分析【免费下载链接】jjA Git-compatible VCS that is both simple and powerful项目地址: https://gitcode.com/GitHub_Trending/jj/jj本指南围绕 Jujutsujj官方文档中的《Comparison with Sapling》展开系统梳理 jj 与 Meta 开源的版本控制系统 Sapling 在设计理念上的共同点与关键差异。Sapling 是 Mercurial 的重度分支而 jj 大量借鉴了 Mercurial 的思想两者表面相似却在工作副本、冲突建模、撤销机制、Git 互操作与 Forge 集成等核心环节走了截然不同的路线。读完本文你将理解 jj 的自动快照工作副本可提交冲突操作日志撤销等设计如何带来更一致的 CLI 体验并能直接上手jj git push --change、jj op log、jj resolve等命令完成实战操作。背景两条从 Mercurial 出发的技术路线根据 docs/sapling-comparison.mdSapling 是 Meta 开发的版本控制系统在 jj 启动开发约三年后才对外发布。它本质上是 Mercurial 的重度修改分支而 jj 也把 Mercurial 的很多理念吸收为自身设计的基础。因此两款工具存在大量相似之处主要体现在用户友好的 CLI都试图降低日常版本操作的心智负担revset 语言都提供函数式语言用于选择修订集jj 的实现见 revsets 文档支持符号、运算符、函数与别名例如jj log -r -查看工作副本的父提交对堆叠提交stacked commits的良好支持都支持跟踪匿名 heads没有 Git 那种detached HEAD状态都提供split等命令在修改amend一个提交后会自动 rebase 其后代提交灵活的模板自定义输出都允许通过模板定制日志、diff 等输出格式jj 的模板语法见 templates.md。这些相似点说明两款工具面对的是同一类痛点。真正的分歧在于实现哲学下面逐条展开。差异一工作副本是否自动提交这是 jj 与 Sapling以及绝大多数 VCS最根本的分歧点。Sapling 的方式传统方式用户必须显式告诉工具何时创建提交、包含哪些文件。存在暂存环节且工作副本有未提交改动时某些操作会失败。jj 的方式工作副本由每个命令自动快照见 working-copy.md。新文件被自动跟踪删除的文件被自动取消跟踪。自动快照带来三个直接收益每次运行命令都等于备份了工作副本。即使你忘了提交改动也不会停留在无状态的工作区里。没有任何命令会因为工作副本有改动而失败。不会出现 Sapling 中abort: 1 conflicting file changes: ...之类的报错也不需要sl shelve这类暂存避难所命令。CLI 更简单、更一致工作副本被当作普通提交对待符号就是一个指向工作副本提交的 revset 表达式。实战控制自动跟踪的范围snapshot.auto-track配置项控制哪些路径会被自动跟踪语法见 filesets 文档。默认情况下被忽略文件匹配 ignore 规则永远不会被自动跟踪如果你把snapshot.auto-track改为非默认值未被自动跟踪的未跟踪文件可以用jj file track手动跟踪而jj file untrack可以在保留文件于工作副本的同时取消跟踪。ignore 规则沿用 Git 生态的.gitignore格式jj 目前没有.jjignore支持工作副本任意目录下的.gitignore、$XDG_CONFIG_HOME/git/ignore与$GIT_DIR/info/exclude详见 git-compatibility.md 中关于.gitignore: Yes的说明。注意已被跟踪的文件即使匹配 ignore 规则也仍保持跟踪需要jj file untrack才能排除。实战多个工作副本与陈旧工作副本由于快照机制与操作日志绑定jj 还支持单个仓库挂多个工作副本jj workspace add以及用jj workspace update-stale修复陈旧工作副本。后者常用于你在工作区 B 重写了工作区 A 的提交、或命令被^C中断导致第三步更新工作副本未完成的场景——详见 working-copy.md 的 Stale working copy 一节。差异二冲突能否被提交传统 VCS包括 Sapling要求用户在提交前必须解决冲突。jj 则允许你把冲突提交到提交对象里详见 conflicts.md。需要强调的是提交的是冲突的逻辑表示而不是之类的冲突标记文本本身。这一设计带来一系列连锁优势合并冲突不会阻止你检出任一提交。你可以带着冲突继续切换分支、继续开发。你可以在想解决的时候再解决。比如把全部进行中的工作持续 rebase 到上游头部冲突先放着。Rebase 后代提交永远成功。Sapling 虽然也自动 rebase但一旦遇到冲突就会失败jj 则把冲突记录下来并继续。合并提交可以被正确 rebaseSapling 有时会失败。jj 把合并提交中的改动定义为相对自动合并后的父提交的变更因此连在合并提交里做的冲突解决也能被 rebase覆盖了 Git 生态中git rerere的大部分使用场景。你可以 rebase冲突本身以及冲突的解决结果。因为冲突是数据对象而非标记文本冲突被 rebase 时不会产生嵌套的冲突标记技术原理见 technical/conflicts.md。实战冲突标记的三种风格冲突在写入工作副本或出现在 diff 输出中时会被物化为冲突标记。默认diff 风格标记长这样 conflict 1 of 1 %%%%%%% diff from: vpxusssl 38d49363 merge base \\\\\\\ to: rtsqusxu 2768b0b9 commit A apple -grape grapefruit orange ysrnknol 7a20f389 commit B APPLE GRAPE ORANGE conflict 1 of 1 ends/标记冲突起止标记快照snapshot起始%%%%%%%标记要应用到快照上的 diff 起始\\\\\\\只是让标签换行更易读。解决冲突 把 diff 应用或不应用到快照上。如果你更喜欢直接看每一侧的内容可以把ui.conflict-marker-style设为snapshot如果团队工具期望 Git 风格则可设为git即 diff3 风格但该风格只支持两侧冲突多于两侧时自动回退到 snapshot 风格。此外当文件内容可能被误认为冲突标记时jj 会使用更长的冲突标记以保证无歧义当冲突涉及缺失结尾换行符的文件时jj 会给每个冲突项补换行并从结尾标记省略终止换行——这些边界细节都记录在 conflicts.md 中。实战解决冲突的命令流程用jj new commit在冲突提交之上创建工作副本提交冲突会同步出现在工作副本中解决后用jj diff检查再jj squash把解决结果并入原冲突提交。或直接jj edit commit在冲突提交上编辑缺点是难以单独检查解决结果。两侧冲突可用jj resolve调用外部合并工具目录/文件/符号链接之间的冲突目前还没有很好的解决方式。jj restore可用于直接选择冲突的一侧。差异三撤销能力——操作日志 vs MetaLogjj 的撤销由**操作日志operation log**驱动见 operation-log.md它记录了仓库随时间变化的历史每次操作都保存一份view快照bookmark、tag、Git ref 指向、repo heads、各工作区的工作副本提交外加父操作指针与时间戳、用户名、主机名、描述等元数据。Sapling 的 MetaLog 功能看似接近但关键区别在于jj 通过jj op log把日志暴露给用户你可以明确看到要回退多少步Sapling 的sl debugmetalog更像是展示单次提交的历史而非整个仓库的演变。得益于工作副本快照jj可以撤销工作副本的改动jj undo掉一次jj commit后jj diff仍显示提交前的改动而sl undo掉sl commit后工作副本是干净的改动已丢失在提交里。围绕操作日志的命令jj undo逐条撤销最近操作、jj op revert回退某个非最新操作、jj op restore把整个仓库恢复到某次操作的状态。顶层选项--at-op/--at-operation可把仓库加载到指定操作时刻此时自动快照被跳过适合分析仓库怎么变成现在这样的。操作引用中表示当前操作x-表示其父操作x表示子操作。实现证据并发安全从源码看操作日志也是 jj 实现无锁并发的基础operation-log.md 的 Divergent operations 一节并发命令各自从最新操作加载仓库、各自写入新操作随后由jj st/jj log提示分叉divergence。在 Git 后端下git的改动会被记录为 import git refs 操作因此也可以用jj undo/jj op restore撤销 Git 命令的结果见 git-compatibility.md。差异四Git 互操作——colocated 工作区两者都支持从远程 Git 仓库克隆、推送、拉取。jj 的独特之处在于可以与 Git 仓库共享同一个工作副本让jj和git在同一仓库中互换使用。这得益于 Git 后端与colocated 工作区设计详见 git-compatibility.mdjj git init name创建同时含.jj与.git的 colocated 工作区每个jj命令会自动与 Git 仓库互相导入导出 refsjj git init --git-repopath可基于已有 Git 仓库建 jj 仓库行为类似 Git worktreejj git clone URL则克隆远程仓库默认 remote 名为origin可用--remote改名。实践建议与注意事项来自 git-compatibility.md混合使用jj与git命令时推荐多用只读 Git 命令改动交给 jjjj 通常会让 Git 仓库处于 detached HEAD 状态执行会改动的 Git 命令前可能需要先git switch。可用--no-colocate或git.colocate false关闭 colocationjj git colocation status/enable/disable可随时查看与切换。关闭 colocation 可避免 IDE 后台git fetch引发的分支冲突/change-id 分叉、巨型 refs 仓库下每次命令的自动 import 开销以及 Git 工具面对冲突提交以.jjconflict-base-*/、.jjconflict-side-*/根目录和非标准jj:treescommit header 存储时的困惑。在 NFS/Dropbox 上共享 colocated 仓库的并发韧性也较弱。差异五打磨程度与 Web UI文档坦率地承认Sapling 更成熟、功能更完整Polish: Sapling is more polished and feature-complete。Sapling 内置了名为 Interactive SmartlogISL的优质 Web UI支持拖拽提交进行 rebase 等操作。这是 Sapling 在开箱即用体验上的显著优势jj 目前没有对等的内置图形界面。差异六Forge 工作流——sl pr submit --stackvsjj git push --change在代码托管平台forge集成上两者取向完全不同Sapling 的sl pr submit --stack可以把一叠提交分别推成独立的 GitHub PR并自动设置 base 分支但只支持 GitHub。jj没有与 GitHub 或其他 forge 的直接集成但提供jj git push --change为指定提交自动创建分支bookmark再按常规流程推送成 PR。实战jj git push --change的用法文档给出的工作流要点为每个需要分支的提交显式指定jj git push --change X --change Y ...base 分支需要你在 GitHub或 GitLab 等的 UI 里手动设置后续推送可一次更新全部jj git push -r main..推送当前提交栈从main分叉点以来的所有分支。从源码可以印证命令行为cli/src/commands/git/push.rsGitPushArgs定义了互斥的分组--bookmark/--tag/--revision/--change/--named属于specific组--all/--tracked/--deleted属于what组push.rs 第 119-238 行。--change的参数注释明确写道Push this commit by creating a bookmark生成的 bookmark 名可用templates.git_push_bookmark模板自定义默认是push- change_id.short()新生成的 bookmark 会被自动跟踪push.rs 第 210-221 行。命令注释还说明默认推送范围是remote_bookmarks(remoteremote)..并强调 jj 不像 Git 那样从 tracked remote bookmark 推断推送目标必须用--remote显式指定push.rs 第 87-106 行。集成测试覆盖了--change的多场景cli/tests/test_git_push.rs如jj git push --change 、--change(|root())、同 bookmark 重复--change等用例说明按 change 生成分支并推动是经过验证的稳定路径。总结如何理解 jj 与 Sapling 的选择一句话概括Sapling 更贴近传统 VCS 的心智模型并做了精致打磨而 jj 选择重构底层模型以换取一致性与可组合性。四条核心差异环环相扣——工作副本自动快照是撤销工作副本改动的前提可提交冲突让 rebase 永不因冲突失败操作日志为撤销与无锁并发提供统一基础Git 互操作让 jj 可以无缝嵌入既有 Git 生态。如果你的团队深度依赖 GitHub 的 PR 栈工作流并看重开箱即用的 Web UISapling 的sl pr submit --stack更具吸引力如果你更在意无论仓库处于什么状态命令都不失败、改动永不丢失、事后随时可撤销的一致性体验并且愿意接受 forge 集成需要手动拼装那么 jj 的这些设计值得认真评估。进一步阅读本文所有结论均可回溯至仓库内的原始文档与源码——sapling-comparison.md本文蓝本、working-copy.md、conflicts.md、operation-log.md、git-comparison.md、git-compatibility.md、revsets.md 以及 push.rs 与对应测试。【免费下载链接】jjA Git-compatible VCS that is both simple and powerful项目地址: https://gitcode.com/GitHub_Trending/jj/jj创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考