| 1 | # Revset semantics — notes for the §8 subset |
| 2 | |
| 3 | Spec §8 requires a deliberately small revset subset (`author()`, `description()`, |
| 4 | `conflicted()`, `bookmarks()`, `open()`, plus `&`, `|`, `~`) parsed into SQL, and warns: |
| 5 | |
| 6 | > Do not attempt to implement jj's full revset language against a Git-backed index; it will not |
| 7 | > be faithful, and a subtly wrong revset is worse than an honestly limited one. |
| 8 | |
| 9 | These are behaviours verified against jj 0.43.0 that a naive SQL translation gets wrong. |
| 10 | |
| 11 | ## `description()` is an EXACT match, not a substring match |
| 12 | |
| 13 | This is the trap. Verified directly: |
| 14 | |
| 15 | ``` |
| 16 | $ jj log -r 'description("merge base")' # commit described exactly "merge base" |
| 17 | (empty result) |
| 18 | |
| 19 | $ jj log -r 'description(substring:"base")' |
| 20 | mmnulkozlxru merge base |
| 21 | |
| 22 | $ jj log -r 'subject("merge base")' |
| 23 | mmnulkozlxru merge base |
| 24 | ``` |
| 25 | |
| 26 | The obvious implementation — `WHERE description ILIKE '%' || $1 || '%'` — is **wrong**, and |
| 27 | wrong in the most damaging direction: it silently returns *more* than the CLI would, so a |
| 28 | reviewer filtering a change list sees results their terminal does not. That is precisely the |
| 29 | "subtly wrong revset" the spec says is worse than no revset. |
| 30 | |
| 31 | Correct mapping for the v1 subset: |
| 32 | |
| 33 | | revset | SQL | |
| 34 | |---|---| |
| 35 | | `description("x")` | `description = 'x'` — exact, including jj's trailing newline handling | |
| 36 | | `description(substring:"x")` | `description ILIKE '%x%'` | |
| 37 | | `subject("x")` | exact match against the **first line** of the description | |
| 38 | |
| 39 | If only one form is implemented in v1, implement `description(substring:)`, because it is the |
| 40 | one users actually want — but then **do not accept the bare `description("x")` form at all**. |
| 41 | Reject it with the "unsupported expression" message rather than aliasing it to substring. |
| 42 | Accepting it with different semantics than the CLI is the failure mode to avoid. |
| 43 | |
| 44 | Note also that jj descriptions carry a trailing newline. Normalise on ingest (store without |
| 45 | it) so exact matching does not depend on invisible whitespace. |
| 46 | |
| 47 | ## Prefix matching |
| 48 | |
| 49 | Ambiguity handling matters. jj resolves the shortest unique prefix and errors on ambiguity; |
| 50 | spec §7 requires Dogfood render a disambiguation page rather than guess. The index has |
| 51 | `changes_prefix_idx (repo_id, change_id text_pattern_ops)` for this — use |
| 52 | `change_id LIKE $1 || '%'` and count results before choosing a page. |
| 53 | |
| 54 | ## Functions deliberately NOT in the v1 subset |
| 55 | |
| 56 | `heads()`, `roots()`, `ancestors()`, `descendants()`, `::`, `..`, `latest()`, `merges()`, |
| 57 | `file()`, `diff_contains()` — all require graph traversal or content search that the SQL index |
| 58 | cannot answer faithfully. The parser must reject these by name with a clear message naming the |
| 59 | unsupported function, not fail to parse with a generic syntax error. |
| 60 | |
| 61 | ## `mine()` / `author()` |
| 62 | |
| 63 | `author()` in jj matches against the author *email and name* as a substring. Dogfood stores |
| 64 | `revisions.author_email` and `author_name`; match against both, and be explicit in the UI that |
| 65 | this is a substring match so it is not confused with `description()`'s exact semantics. |
65 lines · Markdown