Jujutsu Revset 语言完全指南:从符号、运算符到函数的提交选择器
2026/9/10 10:01:33 网站建设 项目流程

Jujutsu Revset 语言完全指南:从符号、运算符到函数的提交选择器

【免费下载链接】jjA Git-compatible VCS that is both simple and powerful项目地址: https://gitcode.com/GitHub_Trending/jj/jj

Jujutsu(jj)提供了一门用于选择修订(revision)集合的函数式查询语言,其表达式被称为 "revset"(该思想源自 Mercurial)。无论是jj logjj edit还是jj rebase,绝大多数jj命令都接受一个或多个 revset 作为修订选择参数。本文将以仓库中的 docs/revsets.md 为核心骨架,结合 lib/src/revset.pest 的语法定义、lib/src/revset_parser.rs 的 Pratt 解析器以及 cli/src/config/revsets.toml 的内置别名实现,系统讲解 revset 的符号、运算符、函数、字符串/日期模式与别名机制。读完本文,你将能够熟练编写精确的 revset 表达式,驾驭 jj 的提交选择能力。

需要说明的是:许多jj命令(如jj edit <revset>)期望 revset 解析为单个提交;如果传入的 revset 解析出多个提交(或零个提交),命令会报错。本文中 "revisions" 与 "commits" 两个词含义相同、交替使用。

可见提交与隐藏提交

大多数 revset 只搜索可见提交。其他提交只有在被显式提及时才会进入搜索空间,例如通过提交 ID、<name>@<remote>符号或at_operation()函数提及。

一旦隐藏提交被指定,它们的祖先也会进入搜索空间。这些提交会出现在all()x..~x等表达式中,但不会出现在..visible_heads()等表达式中。例如,hidden_id | all()等价于hidden_id | ::(hidden_id | visible_heads())

从源码看,可见性约束由 lib/src/revset.rs 中的RevsetExpression体系承载,其内部包含visible_headsroot等关键状态(见 lib/src/revset.rs),隐藏提交被引用后通过CommitRef节点(如ChangeIdCommitIdBookmarksTags等,见 lib/src/revset.rs)将目标提交显式带入搜索空间,再交由索引求值。

符号(Symbols)

工作区与远程符号

  • @表示当前工作区的工作副本提交。
  • <workspace name>@表示其他工作区的工作副本提交。
  • <name>@<remote>表示远程跟踪的标签(tag)或书签(bookmark),例如main@origin

ID 符号

  • 完整提交 ID(commit ID)指向单个提交;也支持完整提交 ID 的唯一前缀。使用非唯一前缀会报错。
  • 完整变更 ID(change ID)指向具有该变更 ID 的可见提交;同样支持唯一前缀。使用非唯一前缀或分歧变更 ID 会报错。要引用隐藏提交或分歧变更,可以使用<change ID>/<offset>语法添加变更偏移。

引号与字符串字面量

使用单引号或双引号可以防止符号被解释为表达式。例如"x-"是符号x-,而不是符号x的父提交。考虑 shell 引号的影响,你可能需要写成jj log -r '"x-"'

在语法层面,lib/src/revset.pest 定义了identifier的规则:标识符由identifier_part(包含 Unicode XID_CONTINUE、_*/)构成,并可通过.-+连接多个片段。该文件还定义了string_literal(双引号,支持转义)与raw_string_literal(单引号,不转义),见 lib/src/revset.pest。由于..与区间运算符冲突、++与模板拼接冲突,标识符中避免使用这些组合;而--没有已知冲突,因此被允许。

符号解析优先级

Jujutsu 按以下顺序尝试解析符号:

  1. 标签名(tag name)
  2. 书签名(bookmark name)
  3. 提交 ID 或变更 ID

要覆盖此优先级,请使用对应的 revset 函数。例如,即使恰好存在同名的书签,想将abc解析为提交 ID 时使用commit_id(abc)。这在脚本中尤其有用。

源码中这一顺序由 lib/src/revset.rs 体现:TagResolver通过repo.view().get_local_tag(symbol)解析标签,BookmarkResolver通过repo.view().get_local_bookmark(symbol)解析书签,二者组成DEFAULT_RESOLVERS数组依次尝试;随后才是CommitPrefixResolverChangePrefixResolver对 ID 前缀的解析(见 lib/src/revset.rs)。ID 前缀解析依赖IdPrefixIndex,遇到多义前缀会返回AmbiguousCommitIdPrefix/AmbiguousChangeIdPrefix错误,遇到分歧变更 ID 会返回DivergentChangeId错误——这与文档中"非唯一前缀报错"的描述完全一致。

运算符(Operators)

下面的运算符中,xy可以是任意 revset,而不仅是符号。运算符按绑定力从强到弱排列;例如x | y & z会被解释为x | (y & z),因为&的绑定力比|强。同一绑定力的中缀运算符从左到右解析,例如x ~ y & z解释为(x ~ y) & z而不是x ~ (y & z)。括号可以控制求值顺序,如(x & y) | zx & (y | z)

该优先级表与 lib/src/revset_parser.rs 中的PRATTPratt 解析器定义一一对应:从最外层开始依次是并集(union_op)、交集/差集(intersection_op/difference_op)、前缀否定(negate_op)、区间(dag_range_op/range_op及前后缀形式)、邻居(parents_op/children_op,含兼容别名compat_parents_op^)。

  1. f(x):函数调用。
  2. x-x的父提交,可能为空;x+x的子提交,可能为空。
  3. p:x:名为p的字符串/日期模式或模式别名。
  4. 区间运算符:
    • x::x的后代,包含x自身。未提及隐藏提交时等价于x::visible_heads()
    • x..:不是x祖先的提交。等价于~::x;未提及隐藏提交时等价于x..visible_heads()
    • ::xx的祖先,包含x自身,是root()::x的简写。
    • ..xx的祖先,包含x自身,但排除根提交。是root()..x的简写,等价于::x ~ root()
    • x::y:既是x后代又是y祖先的提交,等价于x:: & ::y。这正是 Git 中git log --ancestry-path x..y的语义。
    • x..y:是y祖先但不是x祖先的提交,等价于::y ~ ::x。这与 Git 中x..y的语义相同。注意这不是提交图中xy之间的"路径"——xy不必存在祖先关系。
    • :::仓库中所有可见提交,等价于all();未提及隐藏提交时等价于root()::visible_heads()
    • ..:仓库中所有可见提交,但排除根提交,等价于~root();未提及隐藏提交时等价于root()..visible_heads()
  5. ~x:不在x中的提交。
  6. x & y:同时属于xy的提交;x ~ y:属于x但不属于y的提交。
  7. x | y:属于xy(或两者)的提交。

在语法层,lib/src/revset.pest 明确声明了各运算符的词法规则:parents_op = { "-" }children_op = { "+" }、兼容写法compat_parents_op = { "^" }dag_range_op = { "::" }range_op = { ".." }negate_op = { "~" }union_op = { "|" }intersection_op = { "&" }difference_op = { "~" }等。层级表达式(neighbors_expressionrange_expressionexpression)的嵌套关系见 lib/src/revset.pest。

..对左侧并集的不分配律(重要陷阱)

..运算符不向左侧的并集(|)分配。例如(A | B)..等价于A.. | B..(A | B)..表示"既不是 A 的祖先、也不是 B 的祖先的提交",而A.. | B..表示"不是 A 的祖先、或者不是 B 的祖先的提交"。事实上(A | B).. = A.. & B..。下面用具体例子说明。

运算符示例

给定如下历史:

D |\ B C |/ A | root()

运算符x-(父提交)

  • D-{C,B}
  • B-{A}
  • A-{root()}
  • root()-{}(空集)
  • none()-{}(空集)
  • (D|A)-{C,B,root()}
  • (C|B)-{A}

运算符x+(子提交)

  • D+{}(空集)
  • B+{D}
  • A+{B,C}
  • root()+{A}
  • none()+{}(空集)
  • (C|B)+{D}
  • (B|root())+{D,A}

运算符x::(后代)

  • D::{D}
  • B::{D,B}
  • A::{D,C,B,A}
  • root()::{D,C,B,A,root()}
  • none()::{}(空集)
  • (C|B)::{D,C,B}

运算符x..(非祖先)

  • D..{}(空集)
  • B..{D,C}(注意与B::不同,这里包含C
  • A..{D,C,B}
  • root()..{D,C,B,A}
  • none()..{D,C,B,A,root()}
  • (C|B)..{D}

运算符::x(祖先)

  • ::D{D,C,B,A,root()}
  • ::B{B,A,root()}
  • ::A{A,root()}
  • ::root(){root()}
  • ::none(){}(空集)
  • ::(C|B){C,B,A,root()}

运算符..x(祖先,排除根)

  • ..D{D,C,B,A}
  • ..B{B,A}
  • ..A{A}
  • ..root(){}(空集)
  • ..none(){}(空集)
  • ..(C|B){C,B,A}

运算符x::y(祖先路径)

  • D::D{D}
  • B::D{D,B}(注意与B..D不同,这里包含B、排除C
  • B::C{}(空集)(注意与B..C不同,这里排除C
  • A::D{D,C,B,A}
  • root()::D{D,C,B,A,root()}
  • none()::D{}(空集)
  • D::B{}(空集)
  • (C|B)::(C|B){C,B}

运算符x..y(差异)

  • D..D{}(空集)
  • B..D{D,C}(注意与B::D不同,这里包含C、排除B
  • B..C{C}(注意与B::C不同,这里包含C
  • A..D{D,C,B}
  • root()..D{D,C,B,A}
  • none()..D{D,C,B,A,root()}
  • D..B{}(空集)
  • (C|B)..(C|B){}(空集)

..对左侧并集的非分配性

仍用同一张图,观察(C|B)..{D},但:

  • C..{D,B}
  • B..{D,C}
  • C.. | B..{D,C,B}
  • C.. & B..{D}

因此(C|B)..C.. | B..,而(C|B).. = C.. & B....运算符会把其左侧的并集转换为交集。

函数(Functions)

除了运算符,还可以用函数指定提交。部分函数接受其他 revset(表达式)作为参数。例如reachable(@, mutable())返回你正在工作的提交栈。

函数参数语法:本文档中可选参数用方括号表示,如[arg]。部分参数带可选标签,可用来在不必指定前面所有参数的情况下指定该参数。例如remote_bookmarks([name_pattern], [[remote=]remote_pattern])意味着以下写法全部合法:

  • remote_bookmarks()
  • remote_bookmarks("main")
  • remote_bookmarks("main", "origin")
  • remote_bookmarks("main", remote="origin")
  • remote_bookmarks(remote="origin")

在词法层面,lib/src/revset.pest 定义了function(函数名 + 括号参数列表)、keyword_argumentstrict_identifier = expression形式的键值参数)与function_arguments(逗号分隔,可带尾随逗号)。

图遍历类函数

  • parents(x, [depth])parents(x)x-相同。parents(x, depth)返回给定深度处的父提交,例如parents(x, 3)等价于x---
  • children(x, [depth])children(x)x+相同。children(x, depth)返回给定深度处的子提交,例如children(x, 3)等价于x+++
  • ancestors(x, [depth])ancestors(x)::x相同。ancestors(x, depth)返回限于给定深度的祖先。
  • descendants(x, [depth])descendants(x)x::相同。descendants(x, depth)返回限于给定深度的后代。
  • first_parent(x, [depth])first_parent(x)parents(x)类似,但对合并提交只返回第一父提交。depth参数用法类似,first_parent(x, 2)等价于first_parent(first_parent(x))
  • first_ancestors(x, [depth]):与ancestors(x, [depth])类似,但只沿每个提交的第一父提交遍历。在 Git 中合并提交的第一父提交通常是合并目标分支,因此first_ancestors()可用来排除其他分支上的改动。
  • reachable(srcs, domain):从srcs出发、沿所有父边和子边遍历、但整个路径都限制在domain内的所有提交。这对于在限定范围内查找分支或功能相关的所有提交很有用。例如reachable(@, mutable())返回你正在工作的提交栈。
  • connected(x):等价于x::x。当x包含多个提交时很有用。
  • all():所有可见提交以及被显式提及提交的祖先。
  • none():空提交集。此函数很少用,仅为完整性而提供。

ID、书签与标签类函数

  • change_id(prefix):具有给定变更 ID 前缀的提交。如果指定变更发生分歧,会解析出多个提交。使用非唯一前缀会报错;前缀未匹配不算错误。
  • commit_id(prefix):具有给定提交 ID 前缀的提交。使用非唯一前缀会报错;前缀未匹配不算错误。
  • bookmarks([pattern]):所有本地书签目标。若指定pattern,则选择名称匹配给定字符串模式的书签。例如bookmarks(*push*)会匹配书签push-123repushed,但不会匹配main。如果书签处于冲突状态,其所有可能目标都会被包含。
  • remote_bookmarks([name_pattern], [[remote=]remote_pattern]):所有远程书签在各远程上的目标。若只指定name_pattern,则选择所有远程上名称匹配字符串模式的书签;若同时指定name_patternremote_pattern,则进一步限制为名称匹配remote_pattern的远程。例如remote_bookmarks(*push*, *ri*)会匹配push-123@originrepushed@private,但不会匹配push-123@upstreammain@originmain@upstream。书签冲突时包含其所有可能目标。Git 跟踪书签默认被排除,可用remote="git"remote="*"选择包含@git的书签。
  • tracked_remote_bookmarks([name_pattern], [[remote=]remote_pattern]):所有已跟踪远程书签的目标,可选参数同remote_bookmarks()
  • untracked_remote_bookmarks([name_pattern], [[remote=]remote_pattern]):所有未跟踪远程书签的目标,可选参数同remote_bookmarks()
  • tags([pattern]):所有标签目标。若指定pattern,选择名称匹配字符串模式的标签。例如tags(*v1*)匹配v123rev1,但不匹配v2。标签冲突时包含其所有可能目标。
  • remote_tags([name_pattern], [[remote=]remote_pattern]):所有远程标签在各远程上的目标。参数同remote_bookmarks()
  • tracked_remote_tags([name_pattern], [[remote=]remote_pattern]):所有已跟踪远程标签的目标,参数同remote_tags()
  • untracked_remote_tags([name_pattern], [[remote=]remote_pattern]):所有未跟踪远程标签的目标,参数同remote_tags()

图论与集合类函数

  • visible_heads():所有可见头(未提及隐藏提交时与heads(all())相同)。
  • root():所有其他提交最古老祖先的虚拟提交。
  • heads(x)x中不是x内其他提交祖先的提交,等价于x ~ ::x-。注意这与 Mercurial 的heads(x)(等价于x ~ x-)不同。
  • roots(x)x中不是x内其他提交后代的提交,等价于x ~ x+::。注意这与 Mercurial 的roots(x)(等价于x ~ x+)不同。
  • latest(x, [count]):基于提交者时间戳,x中最新的count个提交。默认count为 1。
  • fork_point(x)x中所有提交的分叉点。分叉点是指x中所有提交的共同祖先中、自身没有任何后代也是x中所有提交共同祖先的那些提交。等价于heads(::x_1 & ::x_2 & ... & ::x_N)x_{1..N}x中的提交)。若x解析为单个提交,fork_point(x)解析为该提交本身。
  • merge_point(x)x中所有提交的汇合点。与分叉点类似,汇合点是指x中所有提交的共同后代中、自身没有任何祖先也是x中所有提交共同后代的那些提交。等价于roots(x_1:: & x_2:: & ... & x_N::)。若x解析为单个提交,merge_point(x)解析为该提交本身。
  • bisect(x):在输入集中找出"约一半输入集是其后代"的提交。当前实现处理非线性历史的能力有限。
  • exactly(x, count):对x求值,若大小不是恰好count则报错,否则返回xcount=1时尤其有用,用于确保某个 revset 表达式恰好有一个目标。
  • merges():合并提交。
  • forks():分叉提交,即子提交多于 1 个的提交。
  • present(x):与x相同,但如果x中任何提交不存在(例如未知书签名),则求值为none()
  • coalesce(revsets...):列表中第一个不求值为none()的 revset 中的提交;若所有 revset 都求值为none(),则结果为none()
  • working_copies():所有工作区的工作副本提交。
  • at_operation(op, x):在指定操作处求值x。例如at_operation(@-, visible_heads())返回在上一操作时可见的所有头。 由于at_operation(op, x)会把该操作处可见的所有提交带入搜索空间,at_operation(op, x) | all()等价于at_operation(op, x) | ::(at_operation(op, x | visible_heads()) | visible_heads())

内容与元数据类函数

  • description(pattern):描述匹配给定字符串模式的提交。非空描述通常以换行符结尾,例如description("")匹配无描述的提交,description("foo\n")匹配描述为"foo\n"的提交。
  • subject(pattern):主题(描述的第一行,不含换行符)匹配给定字符串模式的提交。
  • author(pattern):作者姓名或邮箱匹配给定字符串模式的提交,等价于author_name(pattern) | author_email(pattern)
  • author_name(pattern):作者姓名匹配的提交。
  • author_email(pattern):作者邮箱匹配的提交。
  • author_date(pattern):作者日期匹配指定日期模式的提交。
  • mine():作者邮箱与当前用户邮箱一致的提交,等价于author_email(exact-i:<user-email>)
  • committer(pattern):提交者姓名或邮箱匹配的提交,等价于committer_name(pattern) | committer_email(pattern)
  • committer_name(pattern):提交者姓名匹配的提交。
  • committer_email(pattern):提交者邮箱匹配的提交。
  • committer_date(pattern):提交者日期匹配指定日期模式的提交。
  • signed():经过加密签名的提交。
  • empty():不修改任何文件的提交。也包含无用户修改的merges()root()
  • files(expression):修改了匹配给定 fileset 表达式的路径的提交。 路径相对于jj被调用时的目录;目录名会匹配该目录及其子目录下的所有文件。例如files(foo)匹配文件foofoo/barfoo/bar/baz,但不匹配foobarbar/foo。某些文件模式需要加引号,因为expression还必须能被解析为 revset,例如files(".")中的.必须加引号。
  • diff_lines(text, [files]):包含与给定text模式逐行匹配的 diff 的提交。搜索路径可用files表达式收窄;默认扫描所有被修改的文件(未来版本可能改为尊重命令行路径参数)。例如diff_lines("*TODO*", "src")搜索在 "src" 下添加或删除了 "TODO" 的修订。
  • diff_lines_added(text, [files]):同diff_lines(),但只匹配 diff 的"新增"一侧。
  • diff_lines_removed(text, [files]):同diff_lines(),但只匹配 diff 的"删除"一侧。
  • conflicts():存在冲突状态文件的提交。
  • divergent():分歧的提交。

函数示例

给定如下历史:

E | D |/| B C |/ A | root()

函数reachable()reachable(srcs, domain)srcs出发沿父边或子边查找所有可达提交,但限制在domain内。

  • reachable(E, A..){E,D,C,B}—— A 之后的所有提交都可达
  • reachable(E, B..){E}—— E 被隔离:它唯一的边(到 B)离开了 domain
  • reachable(C, B..){D,C}—— C 与 D 连通;C→A 的边离开了 domain
  • reachable(D, B..){D,C}—— 结果相同:D 可达 C,但 D→B 离开了 domain
  • reachable(A, A..){}(空集)—— A 不在 domainA..中,因此被忽略

函数connected()

  • connected(E|A){E,B,A}
  • connected(D|A){D,C,B,A}
  • connected(A){A}

函数heads()

  • heads(E|D){E,D}
  • heads(E|C){E,C}
  • heads(E|B){E}
  • heads(E|A){E}
  • heads(A){A}

函数roots()

  • roots(E|D){E,D}
  • roots(E|C){E,C}
  • roots(E|B){B}
  • roots(E|A){A}
  • roots(A){A}

函数fork_point()

  • fork_point(E|D){B}
  • fork_point(E|C){A}
  • fork_point(E|B){B}
  • fork_point(E|A){A}
  • fork_point(D|C){C}
  • fork_point(D|B){B}
  • fork_point(B|C){A}
  • fork_point(A){A}
  • fork_point(none()){}

字符串模式(String Patterns)

执行字符串匹配的函数支持以下模式语法(引号可选)。默认情况下,"string"被解析为glob:模式。

  • exact:"string":匹配与string完全相等的字符串。
  • glob:"pattern":使用 Unix shell 风格通配符pattern匹配字符串。
  • regex:"pattern":使用正则表达式pattern匹配子串。
  • substring:"string":匹配包含string的字符串。

在 kind 后追加-i可实现大小写不敏感匹配(例如glob-i:"fix*jpeg*")。

字符串模式可以用逻辑运算符组合(例如bookmarks(~glob:"ci/*")):

  • ~x:匹配除x之外的所有内容。
  • x & y:同时匹配xy
  • x ~ y:匹配x但不匹配y
  • x | y:匹配xy(或两者)。

从实现上看,字符串模式被 lib/src/revset.rs 中的StringPattern类型承载,并编译为 globset / regex 匹配器,再由RevsetFilterPredicate参与求值过滤(参见 cli/src/commands/log.rs 中RevsetFilterPredicate的使用)。

日期模式(Date Patterns)

执行日期匹配的函数支持以下模式语法:

  • after:"string":匹配恰好在该日期或之后。
  • before:"string":匹配该日期之前(不包含该日期)。

日期字符串支持多种写法,包括:

  • 2024-02-01
  • 2024-02-01T12:00:00
  • 2024-02-01T12:00:00-08:00
  • 2024-02-01 12:00:00
  • 2 days ago
  • 5 minutes ago
  • yesterday
  • yesterday 5pm
  • yesterday 10:30
  • yesterday 15:30

别名(Aliases)

可以在配置文件中定义新的符号、函数和<name>:<value>模式,可以使用预定义符号/函数及其他别名的任意组合。别名函数可以按参数个数重载;但内置函数会被同名别名遮蔽,无法与别名共存。

例如:

[revset-aliases] HEAD = '@-' 'user()' = 'user("me@example.org")' 'user(x)' = 'author(x) | committer(x)' 'grep:x' = 'description(regex:x)'

别名描述(Alias descriptions)

通过将别名定义为带.doc.definition属性的表,可以让别名描述出现在 shell 补全中。例如:

[revset-aliases] HEAD = { definition = '@-', doc = 'The parent of the working-copy commit' }

也可以使用点分键语法:

[revset-aliases] HEAD.definition = '@-' HEAD.doc = 'The parent of the working-copy commit'

内置别名(Built-in Aliases)

以下别名是内置的,用于某些特定操作。它们被定义为别名,是为了允许你按需覆盖。完整清单见 cli/src/config/revsets.toml。

  • trunk():解析为默认远程默认书签的头提交,或名为upstreamorigin的远程的头提交。这在 Jujutsu 仓库初始化时在仓库级别设置。 若初始化时无法解析默认书签,默认全局配置会尝试upstreamorigin远程上的mainmastertrunk书签。若存在多个候选 trunk 提交,选择最新的一个;若这些书签都不存在,则 revset 求值为root()。其实际定义为 cli/src/config/revsets.toml 中的latest(remote_bookmarks(exact:"main", exact:"origin") | ... | root())。 你可以按需覆盖它,但务必保证始终解析为恰好一个提交。例如:

    [revset-aliases] 'trunk()' = 'your-bookmark@your-remote'
  • builtin_log():解析为present(@) | ancestors(immutable_heads().., 2) | trunk()(见 cli/src/config/revsets.toml)。它用作revsets.log的默认值——即jj log在未指定修订或路径时显示的提交集合。jj log的默认 revset 读取逻辑见 cli/src/commands/log.rs:未指定修订与路径时,从设置读取revsets.log字符串并解析求值。

  • builtin_immutable_heads():解析为trunk() | tags() | untracked_remote_bookmarks() | untracked_remote_tags()(见 cli/src/config/revsets.toml),用作下面immutable_heads()的默认定义。不建议重定义此别名,推荐改为重定义immutable_heads()

  • immutable_heads():不可变提交集合的头(不是"不可变的头")。默认解析为trunk() | tags() | untracked_remote_bookmarks() | untracked_remote_tags()。实际定义为builtin_immutable_heads(),可按需覆盖。不可变提交的完整集合是::immutable_heads()(即immutable())。详见不可变提交集合。

  • immutable()jj视为不可变的提交集合,等价于::(immutable_heads() | root())。不建议重定义此别名。注意修改它不会改变提交是否不可变,要改变需编辑immutable_heads()

  • mutable()jj视为可变的提交集合,等价于~immutable()。不建议重定义此别名。注意修改它不会改变提交是否不可变,要改变需编辑immutable_heads()

  • visible():可见提交集合,解析为::visible_heads()。除非 revset 包含隐藏提交,否则与all()相等。

  • hidden():隐藏提交集合,解析为~visible()。除非 revset 包含隐藏提交,否则为空。注意这不是所有曾经可见提交的集合。

上述内置别名与revsets配置节(如revsets.logrevsets.log-graph-prioritize = "present(@)"revsets.arrangerevsets.convergerevsets.fixrevsets.runrevsets.sign等)共同定义在 cli/src/config/revsets.toml 中。该文件头部有明确注释:"修改/新增这些别名时,请确保同步更新 docs/revsets.md 的文档"——文档与实现的一致性由此得到保障。

实战示例(Examples)

展示工作副本提交的父提交(类似git log -1 HEAD):

jj log -r @-

展示工作副本的所有祖先(类似普通git log):

jj log -r ::@

展示不在任何远程书签上的提交:

jj log -r 'remote_bookmarks()..'

展示不在origin上的提交(如果你还有其他远程如fork):

jj log -r 'remote_bookmarks(remote=origin)..'

展示仓库中的初始提交(Git 称之为 "root commits"):

jj log -r 'root()+'

展示一些重要提交(类似git log --simplify-by-decoration):

jj log -r 'tags() | bookmarks()'

展示通向工作副本的本地提交,以及这些提交的后代:

jj log -r '(remote_bookmarks()..@)::'

展示作者为 "martinvonz" 且描述包含单词 "reset" 的提交:

jj log -r 'author(*martinvonz*) & description(*reset*)'

小结

revset 是 jj 中最强大也最需要精确掌握的查询语言:符号解析遵循"标签 → 书签 → ID 前缀"的固定优先级,可通过commit_id()/change_id()等函数强制指定命名空间;运算符体系(-/+邻居、::/..区间、~/&/~/|集合运算)由 lib/src/revset.pest 的词法规则与 lib/src/revset_parser.rs 的 Pratt 优先级表决定,且..对左侧并集不分配;丰富的函数族(图遍历、ID/书签/标签、图论、内容元数据)配合字符串/日期模式与可覆盖的内置别名,让 "选择哪些提交" 这件事变得既可读又可控。掌握本文的运算符示例与别名机制,你就能在脚本与日常命令中写出精确、稳健的 revset 表达式。

【免费下载链接】jjA Git-compatible VCS that is both simple and powerful项目地址: https://gitcode.com/GitHub_Trending/jj/jj

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询