just playing with tangled
1# Revsets
2
3Jujutsu supports a functional language for selecting a set of revisions.
4Expressions in this language are called "revsets" (the idea comes from
5[Mercurial](https://www.mercurial-scm.org/repo/hg/help/revsets)). The language
6consists of symbols, operators, and functions.
7
8Most `jj` commands accept a revset (or multiple). Many commands, such as
9`jj diff -r <revset>` expect the revset to resolve to a single commit; it is
10an error to pass a revset that resolves to more than one commit (or zero
11commits) to such commands.
12
13The words "revisions" and "commits" are used interchangeably in this document.
14
15Most revsets search only the [visible commits](glossary.md#visible-commits).
16Other commits are only included if you explicitly mention them (e.g. by commit
17ID or a Git ref pointing to them).
18
19## Symbols
20
21The `@` expression refers to the working copy commit in the current workspace.
22Use `<workspace name>@` to refer to the working-copy commit in another
23workspace. Use `<name>@<remote>` to refer to a remote-tracking bookmark.
24
25A full commit ID refers to a single commit. A unique prefix of the full commit
26ID can also be used. It is an error to use a non-unique prefix.
27
28A full change ID refers to all visible commits with that change ID (there is
29typically only one visible commit with a given change ID). A unique prefix of
30the full change ID can also be used. It is an error to use a non-unique prefix.
31
32Use [single or double quotes][string-literals] to prevent a symbol from being
33interpreted as an expression. For example, `"x-"` is the symbol `x-`, not the
34parents of symbol `x`. Taking shell quoting into account, you may need to use
35something like `jj log -r '"x-"'`.
36
37[string-literals]: templates.md#string-literals
38
39### Priority
40
41Jujutsu attempts to resolve a symbol in the following order:
42
431. Tag name
442. Bookmark name
453. Git ref
464. Commit ID or change ID
47
48## Operators
49
50The following operators are supported. `x` and `y` below can be any revset, not
51only symbols.
52
53* `x-`: Parents of `x`, can be empty.
54* `x+`: Children of `x`, can be empty.
55* `x::`: Descendants of `x`, including the commits in `x` itself. Shorthand for
56 `x::visible_heads()`.
57* `x..`: Revisions that are not ancestors of `x`. Shorthand for
58 `x..visible_heads()`.
59* `::x`: Ancestors of `x`, including the commits in `x` itself. Shorthand for
60 `root()::x`.
61* `..x`: Ancestors of `x`, including the commits in `x` itself, but excluding
62 the root commit. Shorthand for `root()..x`. Equivalent to `::x ~ root()`.
63* `x::y`: Descendants of `x` that are also ancestors of `y`. Equivalent
64 to `x:: & ::y`. This is what `git log` calls `--ancestry-path x..y`.
65* `x..y`: Ancestors of `y` that are not also ancestors of `x`. Equivalent to
66 `::y ~ ::x`. This is what `git log` calls `x..y` (i.e. the same as we call it).
67* `::`: All visible commits in the repo. Shorthand for
68 `root()::visible_heads()`. Equivalent to `all()`.
69* `..`: All visible commits in the repo, but excluding the root commit.
70 Shorthand for `root()..visible_heads()`. Equivalent to `~root()`.
71* `~x`: Revisions that are not in `x`.
72* `x & y`: Revisions that are in both `x` and `y`.
73* `x ~ y`: Revisions that are in `x` but not in `y`.
74* `x | y`: Revisions that are in either `x` or `y` (or both).
75
76(listed in order of binding strengths)
77
78You can use parentheses to control evaluation order, such as `(x & y) | z` or
79`x & (y | z)`.
80
81<!-- The following format will be understood by the web site generator, and will
82 generate a folded section that can be unfolded at will. -->
83
84??? examples
85
86 Given this history:
87 ```
88 o D
89 |\
90 | o C
91 | |
92 o | B
93 |/
94 o A
95 |
96 o root()
97 ```
98
99 **Operator** `x-`
100
101 * `D-` ⇒ `{C,B}`
102 * `B-` ⇒ `{A}`
103 * `A-` ⇒ `{root()}`
104 * `root()-` ⇒ `{}` (empty set)
105 * `none()-` ⇒ `{}` (empty set)
106 * `(D|A)-` ⇒ `{C,B,root()}`
107 * `(C|B)-` ⇒ `{A}`
108
109 **Operator** `x+`
110
111 * `D+` ⇒ `{}` (empty set)
112 * `B+` ⇒ `{D}`
113 * `A+` ⇒ `{B,C}`
114 * `root()+` ⇒ `{A}`
115 * `none()+` ⇒ `{}` (empty set)
116 * `(C|B)+` ⇒ `{D}`
117 * `(B|root())+` ⇒ `{D,A}`
118
119 **Operator** `x::`
120
121 * `D::` ⇒ `{D}`
122 * `B::` ⇒ `{D,B}`
123 * `A::` ⇒ `{D,C,B,A}`
124 * `root()::` ⇒ `{D,C,B,A,root()}`
125 * `none()::` ⇒ `{}` (empty set)
126 * `(C|B)::` ⇒ `{D,C,B}`
127
128 **Operator** `x..`
129
130 * `D..` ⇒ `{}` (empty set)
131 * `B..` ⇒ `{D,C}` (note that, unlike `B::`, this includes `C`)
132 * `A..` ⇒ `{D,C,B}`
133 * `root()..` ⇒ `{D,C,B,A}`
134 * `none()..` ⇒ `{D,C,B,A,root()}`
135 * `(C|B)..` ⇒ `{D}`
136
137 **Operator** `::x`
138
139 * `::D` ⇒ `{D,C,B,A,root()}`
140 * `::B` ⇒ `{B,A,root()}`
141 * `::A` ⇒ `{A,root()}`
142 * `::root()` ⇒ `{root()}`
143 * `::none()` ⇒ `{}` (empty set)
144 * `::(C|B)` ⇒ `{C,B,A,root()}`
145
146 **Operator** `..x`
147
148 * `..D` ⇒ `{D,C,B,A}`
149 * `..B` ⇒ `{B,A}`
150 * `..A` ⇒ `{A}`
151 * `..root()` ⇒ `{}` (empty set)
152 * `..none()` ⇒ `{}` (empty set)
153 * `..(C|B)` ⇒ `{C,B,A}`
154
155 **Operator** `x::y`
156
157 * `D::D` ⇒ `{D}`
158 * `B::D` ⇒ `{D,B}` (note that, unlike `B..D`, this includes `B` and excludes `C`)
159 * `A::D` ⇒ `{D,C,B,A}`
160 * `root()::D` ⇒ `{D,C,B,A,root()}`
161 * `none()::D` ⇒ `{}` (empty set)
162 * `D::B` ⇒ `{}` (empty set)
163 * `(C|B)::(C|B)` ⇒ `{C,B}`
164
165 **Operator** `x..y`
166
167 * `D..D` ⇒ `{}` (empty set)
168 * `B..D` ⇒ `{D,C}` (note that, unlike `B::D`, this includes `C` and excludes `B`)
169 * `A..D` ⇒ `{D,C,B}`
170 * `root()..D` ⇒ `{D,C,B,A}`
171 * `none()..D` ⇒ `{D,C,B,A,root()}`
172 * `D..B` ⇒ `{}` (empty set)
173 * `(C|B)..(C|B)` ⇒ `{}` (empty set)
174
175## Functions
176
177You can also specify revisions by using functions. Some functions take other
178revsets (expressions) as arguments.
179
180* `parents(x)`: Same as `x-`.
181
182* `children(x)`: Same as `x+`.
183
184* `ancestors(x[, depth])`: `ancestors(x)` is the same as `::x`.
185 `ancestors(x, depth)` returns the ancestors of `x` limited to the given
186 `depth`.
187
188* `descendants(x[, depth])`: `descendants(x)` is the same as `x::`.
189 `descendants(x, depth)` returns the descendants of `x` limited to the given
190 `depth`.
191
192* `reachable(srcs, domain)`: All commits reachable from `srcs` within
193 `domain`, traversing all parent and child edges.
194
195* `connected(x)`: Same as `x::x`. Useful when `x` includes several commits.
196
197* `all()`: All visible commits in the repo.
198
199* `none()`: No commits. This function is rarely useful; it is provided for
200 completeness.
201
202* `bookmarks([pattern])`: All local bookmark targets. If `pattern` is specified,
203 this selects the bookmarks whose name match the given [string
204 pattern](#string-patterns). For example, `bookmarks(push)` would match the
205 bookmarks `push-123` and `repushed` but not the bookmark `main`. If a bookmark is
206 in a conflicted state, all its possible targets are included.
207
208* `remote_bookmarks([bookmark_pattern[, [remote=]remote_pattern]])`: All remote
209 bookmarks targets across all remotes. If just the `bookmark_pattern` is
210 specified, the bookmarks whose names match the given [string
211 pattern](#string-patterns) across all remotes are selected. If both
212 `bookmark_pattern` and `remote_pattern` are specified, the selection is
213 further restricted to just the remotes whose names match `remote_pattern`.
214
215 For example, `remote_bookmarks(push, ri)` would match the bookmarks
216 `push-123@origin` and `repushed@private` but not `push-123@upstream` or
217 `main@origin` or `main@upstream`. If a bookmark is in a conflicted state, all
218 its possible targets are included.
219
220 While Git-tracking bookmarks can be selected by `<name>@git`, these bookmarks
221 aren't included in `remote_bookmarks()`.
222
223* `tracked_remote_bookmarks([bookmark_pattern[, [remote=]remote_pattern]])`: All
224 targets of tracked remote bookmarks. Supports the same optional arguments as
225 `remote_bookmarks()`.
226
227* `untracked_remote_bookmarks([bookmark_pattern[, [remote=]remote_pattern]])`:
228 All targets of untracked remote bookmarks. Supports the same optional arguments
229 as `remote_bookmarks()`.
230
231* `tags([pattern])`: All tag targets. If `pattern` is specified,
232 this selects the tags whose name match the given [string
233 pattern](#string-patterns). For example, `tags(v1)` would match the
234 tags `v123` and `rev1` but not the tag `v2`. If a tag is
235 in a conflicted state, all its possible targets are included.
236
237* `git_refs()`: All Git ref targets as of the last import. If a Git ref
238 is in a conflicted state, all its possible targets are included.
239
240* `git_head()`: The Git `HEAD` target as of the last import.
241
242* `visible_heads()`: All visible heads (same as `heads(all())`).
243
244* `root()`: The virtual commit that is the oldest ancestor of all other commits.
245
246* `heads(x)`: Commits in `x` that are not ancestors of other commits in `x`.
247 Note that this is different from
248 [Mercurial's](https://repo.mercurial-scm.org/hg/help/revsets) `heads(x)`
249 function, which is equivalent to `x ~ x-`.
250
251* `roots(x)`: Commits in `x` that are not descendants of other commits in `x`.
252 Note that this is different from
253 [Mercurial's](https://repo.mercurial-scm.org/hg/help/revsets) `roots(x)`
254 function, which is equivalent to `x ~ x+`.
255
256* `latest(x[, count])`: Latest `count` commits in `x`, based on committer
257 timestamp. The default `count` is 1.
258
259* `fork_point(x)`: The fork point of all commits in `x`. The fork point is the
260 common ancestor(s) of all commits in `x` which do not have any descendants
261 that are also common ancestors of all commits in `x`. It is equivalent to
262 the revset `heads(::x_1 & ::x_2 & ... & ::x_N)`, where `x_{1..N}` are commits
263 in `x`. If `x` resolves to a single commit, `fork_point(x)` resolves to `x`.
264
265* `merges()`: Merge commits.
266
267* `description(pattern)`: Commits that have a description matching the given
268 [string pattern](#string-patterns).
269
270 A non-empty description is usually terminated with newline character. For
271 example, `description(exact:"")` matches commits without description, and
272 `description(exact:"foo\n")` matches commits with description `"foo\n"`.
273
274* `subject(pattern)`: Commits that have a subject matching the given [string
275 pattern](#string-patterns). A subject is the first line of the description
276 (without newline character.)
277
278* `author(pattern)`: Commits with the author's name or email matching the given
279 [string pattern](#string-patterns). Equivalent to `author_name(pattern) |
280 author_email(pattern)`.
281
282* `author_name(pattern)`: Commits with the author's name matching the given
283 [string pattern](#string-patterns).
284
285* `author_email(pattern)`: Commits with the author's email matching the given
286 [string pattern](#string-patterns).
287
288* `author_date(pattern)`: Commits with author dates matching the specified [date
289 pattern](#date-patterns).
290
291* `mine()`: Commits where the author's email matches the email of the current
292 user. Equivalent to `author_email(exact-i:<user-email>)`
293
294* `committer(pattern)`: Commits with the committer's name or email matching the
295 given [string pattern](#string-patterns). Equivalent to
296 `committer_name(pattern) | committer_email(pattern)`.
297
298* `committer_name(pattern)`: Commits with the committer's name matching the
299 given [string pattern](#string-patterns).
300
301* `committer_email(pattern)`: Commits with the committer's email matching the
302 given [string pattern](#string-patterns).
303
304* `committer_date(pattern)`: Commits with committer dates matching the specified
305 [date pattern](#date-patterns).
306
307* `empty()`: Commits modifying no files. This also includes `merges()` without
308 user modifications and `root()`.
309
310* `files(expression)`: Commits modifying paths matching the given [fileset
311 expression](filesets.md).
312
313 Paths are relative to the directory `jj` was invoked from. A directory name
314 will match all files in that directory and its subdirectories.
315
316 For example, `files(foo)` will match files `foo`, `foo/bar`, `foo/bar/baz`.
317 It will *not* match `foobar` or `bar/foo`.
318
319 Some file patterns might need quoting because the `expression` must also be
320 parsable as a revset. For example, `.` has to be quoted in `files(".")`.
321
322* `diff_contains(text[, files])`: Commits containing diffs matching the given
323 `text` pattern line by line.
324
325 The search paths can be narrowed by the `files` expression. All modified files
326 are scanned by default, but it is likely to change in future version to
327 respect the command line path arguments.
328
329 For example, `diff_contains("TODO", "src")` will search revisions where "TODO"
330 is added to or removed from files under "src".
331
332* `conflicts()`: Commits with conflicts.
333
334* `present(x)`: Same as `x`, but evaluated to `none()` if any of the commits
335 in `x` doesn't exist (e.g. is an unknown bookmark name.)
336
337* `coalesce(revsets...)`: Commits in the first revset in the list of `revsets`
338 which does not evaluate to `none()`. If all revsets evaluate to `none()`, then
339 the result of `coalesce` will also be `none()`.
340
341* `working_copies()`: The working copy commits across all the workspaces.
342
343* `at_operation(op, x)`: Evaluates `x` at the specified [operation][]. For
344 example, `at_operation(@-, visible_heads())` will return all heads which were
345 visible at the previous operation.
346
347[operation]: glossary.md#operation
348
349??? examples
350
351 Given this history:
352 ```
353 o E
354 |
355 | o D
356 |/|
357 | o C
358 | |
359 o | B
360 |/
361 o A
362 |
363 o root()
364 ```
365
366 **function** `reachable()`
367
368 * `reachable(E, A..)` ⇒ `{E,D,C,B}`
369 * `reachable(D, A..)` ⇒ `{E,D,C,B}`
370 * `reachable(C, A..)` ⇒ `{E,D,C,B}`
371 * `reachable(B, A..)` ⇒ `{E,D,C,B}`
372 * `reachable(A, A..)` ⇒ `{}` (empty set)
373
374 **function** `connected()`
375
376 * `connected(E|A)` ⇒ `{E,B,A}`
377 * `connected(D|A)` ⇒ `{D,C,B,A}`
378 * `connected(A)` ⇒ `{A}`
379
380 **function** `heads()`
381
382 * `heads(E|D)` ⇒ `{E,D}`
383 * `heads(E|C)` ⇒ `{E,C}`
384 * `heads(E|B)` ⇒ `{E}`
385 * `heads(E|A)` ⇒ `{E}`
386 * `heads(A)` ⇒ `{A}`
387
388 **function** `roots()`
389
390 * `roots(E|D)` ⇒ `{E,D}`
391 * `roots(E|C)` ⇒ `{E,C}`
392 * `roots(E|B)` ⇒ `{B}`
393 * `roots(E|A)` ⇒ `{A}`
394 * `roots(A)` ⇒ `{A}`
395
396 **function** `fork_point()`
397
398 * `fork_point(E|D)` ⇒ `{B}`
399 * `fork_point(E|C)` ⇒ `{A}`
400 * `fork_point(E|B)` ⇒ `{B}`
401 * `fork_point(E|A)` ⇒ `{A}`
402 * `fork_point(D|C)` ⇒ `{C}`
403 * `fork_point(D|B)` ⇒ `{A}`
404 * `fork_point(B|C)` ⇒ `{A}`
405 * `fork_point(A)` ⇒ `{A}`
406 * `fork_point(none())` ⇒ `{}`
407
408## String patterns
409
410Functions that perform string matching support the following pattern syntax (the
411quotes are optional):
412
413* `"string"` or `substring:"string"`: Matches strings that contain `string`.
414* `exact:"string"`: Matches strings exactly equal to `string`.
415* `glob:"pattern"`: Matches strings with Unix-style shell [wildcard
416 `pattern`](https://docs.rs/glob/latest/glob/struct.Pattern.html).
417* `regex:"pattern"`: Matches substrings with [regular
418 expression `pattern`](https://docs.rs/regex/latest/regex/#syntax).
419
420You can append `-i` after the kind to match case‐insensitively (e.g.
421`glob-i:"fix*jpeg*"`).
422
423## Date patterns
424
425Functions that perform date matching support the following pattern syntax:
426
427* `after:"string"`: Matches dates exactly at or after the given date.
428* `before:"string"`: Matches dates before, but not including, the given date.
429
430Date strings can be specified in several forms, including:
431
432* 2024-02-01
433* 2024-02-01T12:00:00
434* 2024-02-01T12:00:00-08:00
435* 2024-02-01 12:00:00
436* 2 days ago
437* 5 minutes ago
438* yesterday
439* yesterday 5pm
440* yesterday 10:30
441* yesterday 15:30
442
443## Aliases
444
445New symbols and functions can be defined in the config file, by using any
446combination of the predefined symbols/functions and other aliases.
447
448Alias functions can be overloaded by the number of parameters. However, builtin
449function will be shadowed by name, and can't co-exist with aliases.
450
451For example:
452
453```toml
454[revset-aliases]
455'HEAD' = '@-'
456'user()' = 'user("me@example.org")'
457'user(x)' = 'author(x) | committer(x)'
458```
459
460### Built-in Aliases
461
462The following aliases are built-in and used for certain operations. These functions
463are defined as aliases in order to allow you to overwrite them as needed.
464See [revsets.toml](https://github.com/jj-vcs/jj/blob/main/cli/src/config/revsets.toml)
465for a comprehensive list.
466
467* `trunk()`: Resolves to the head commit for the trunk bookmark of the remote
468 named `origin` or `upstream`. The bookmarks `main`, `master`, and `trunk` are
469 tried. If more than one potential trunk commit exists, the newest one is
470 chosen. If none of the bookmarks exist, the revset evaluates to `root()`.
471
472 When working with an existing Git repository (via `jj git clone` or
473 `jj git init`), `trunk()` will be overridden at the repository level
474 to the default bookmark of the remote `origin`.
475
476 You can [override](./config.md) this as appropriate. If you do, make sure it
477 always resolves to exactly one commit. For example:
478
479 ```toml
480 [revset-aliases]
481 'trunk()' = 'your-bookmark@your-remote'
482 ```
483
484* `builtin_immutable_heads()`: Resolves to
485 `present(trunk()) | tags() | untracked_remote_bookmarks()`. It is used as the
486 default definition for `immutable_heads()` below. It is not recommended to
487 redefine this alias. Prefer to redefine `immutable_heads()` instead.
488
489* `immutable_heads()`: Resolves to
490 `present(trunk()) | tags() | untracked_remote_bookmarks()` by default. It is
491 actually defined as `builtin_immutable_heads()`, and can be overridden as
492 required. See [here](config.md#set-of-immutable-commits) for details.
493
494* `immutable()`: The set of commits that `jj` treats as immutable. This is
495 equivalent to `::(immutable_heads() | root())`. It is not recommended to redefine
496 this alias. Note that modifying this will *not* change whether a commit is immutable.
497 To do that, edit `immutable_heads()`.
498
499* `mutable()`: The set of commits that `jj` treats as mutable. This is
500 equivalent to `~immutable()`. It is not recommended to redefined this alias.
501 Note that modifying this will *not* change whether a commit is immutable.
502 To do that, edit `immutable_heads()`.
503
504
505## The `all:` modifier
506
507Certain commands (such as `jj rebase`) can take multiple revset arguments, and
508each of these may resolve to one-or-many revisions. By default, `jj` will not
509allow revsets that resolve to more than one revision — a so-called "large
510revset" — and will ask you to confirm that you want to proceed by
511prefixing it with the `all:` modifier.
512
513If you set the `ui.always-allow-large-revsets` option to `true`, `jj` will
514behave as though the `all:` modifier was used every time it would matter.
515
516An `all:` modifier before a revset expression does not otherwise change its
517meaning. Strictly speaking, it is not part of the revset language. The notation
518is similar to the modifiers like `glob:` allowed before [string
519patterms](#string-patterns).
520
521For example, `jj rebase -r w -d xyz+` will rebase `w` on top of the child of
522`xyz` as long as `xyz` has exactly one child.
523
524If `xyz` has more than one child, the `all:` modifier is *not* specified, and
525`ui.always-allow-large-revsets` is `false` (the default), `jj rebase -r w -d
526xyz+` will return an error.
527
528If `ui.always-allow-large-revsets` was `true`, the above command would act as if
529`all:` was set (see the next paragraph).
530
531With the `all:` modifier, `jj rebase -r w -d all:xyz+` will make `w` into a merge
532commit if `xyz` has more than one child. The `all:` modifier confirms that the
533user expected `xyz` to have more than one child.
534
535A more useful example: if `w` is a merge commit, `jj rebase -s w -d all:w- -d
536xyz` will add `xyz` to the list of `w`'s parents.
537
538## Examples
539
540Show the parent(s) of the working-copy commit (like `git log -1 HEAD`):
541
542```shell
543jj log -r @-
544```
545
546Show all ancestors of the working copy (like plain `git log`)
547
548```shell
549jj log -r ::@
550```
551
552Show commits not on any remote bookmark:
553
554```shell
555jj log -r 'remote_bookmarks()..'
556```
557
558Show commits not on `origin` (if you have other remotes like `fork`):
559
560```shell
561jj log -r 'remote_bookmarks(remote=origin)..'
562```
563
564Show the initial commits in the repo (the ones Git calls "root commits"):
565
566```shell
567jj log -r 'root()+'
568```
569
570Show some important commits (like `git --simplify-by-decoration`):
571
572```shell
573jj log -r 'tags() | bookmarks()'
574```
575
576Show local commits leading up to the working copy, as well as descendants of
577those commits:
578
579
580```shell
581jj log -r '(remote_bookmarks()..@)::'
582```
583
584Show commits authored by "martinvonz" and containing the word "reset" in the
585description:
586
587```shell
588jj log -r 'author(martinvonz) & description(reset)'
589```