jj-skill/revsets.md
2025-11-03 14:13:41 -05:00

818 lines
19 KiB
Markdown

# Jujutsu Revsets - Complete Reference
Revsets are a functional language in jujutsu for selecting and querying commits. This document provides comprehensive coverage of revset syntax, symbols, operators, functions, and practical usage patterns.
## Overview
The revset language (inspired by Mercurial) enables powerful commit selection across jujutsu commands. Expressions in this language are called "revsets" and are fundamental to working effectively with jujutsu.
The language consists of:
- **Symbols** - References to specific commits or sets of commits
- **Operators** - Ways to traverse and combine commit sets
- **Functions** - Queries and filters for finding commits
## Core Symbols
### Working Copy References
#### `@`
The current working copy commit.
```bash
jj log -r @ # Show working copy commit
jj diff -r @ # Diff of working copy
jj new @- # Create new commit on parent of @
```
#### `<workspace>@`
Working copy in a named workspace.
```bash
jj log -r main@ # Working copy in "main" workspace
jj diff -r feature@ # Working copy in "feature" workspace
```
### Remote References
#### `<name>@<remote>`
Remote-tracking bookmark.
```bash
jj log -r main@origin # Remote tracking branch
jj rebase -d main@origin # Rebase onto remote main
jj diff --from main@origin --to @ # Compare to remote
```
### Commit Identifiers
#### Change IDs and Commit IDs
Full hexadecimal IDs or unique prefixes.
```bash
jj log -r k # Unique prefix
jj log -r kmkuslsw # Longer prefix
jj log -r kmkuslswpqwq # Full change ID
jj show a1b2c3d # Commit ID prefix
```
**Resolution order:** Tags → Bookmarks → Git refs → Commit IDs
### Special Symbols
#### `root()`
The root commit (virtual commit at base of history).
```bash
jj log -r root() # Show root commit
jj log -r ::@ # All ancestors including root
```
## Operators
Operators traverse commit history and combine commit sets.
### Ancestry Operators
#### `x-` (Parents)
Immediate parent(s) of x.
```bash
jj log -r @- # Parent of working copy
jj log -r @-- # Grandparent
jj show @- # Show parent commit
jj edit @- # Edit parent commit
```
Multiple parents (merge commits):
```bash
jj log -r @-+ # Children of all parents (siblings)
```
#### `x+` (Children)
Immediate children of x.
```bash
jj log -r @+ # Children of working copy
jj log -r main@origin+ # Children of remote main
```
#### `::x` (Ancestors)
All ancestors of x, including x itself.
```bash
jj log -r ::@ # All ancestors of working copy
jj log -r ::main # All ancestors of main
jj log -r ::@- # Ancestors up to parent
```
#### `x::` (Descendants)
All descendants of x, including x itself.
```bash
jj log -r @:: # Working copy and all descendants
jj log -r main:: # main and its descendants
```
#### `x::y` (DAG Range)
Commits reachable from y through ancestor relationships with x.
```bash
jj log -r main::@ # Commits from main to @
jj log -r @-::@ # Just @ (parent to working copy)
```
#### `::x` with depth
Ancestors with limited depth.
```bash
jj log -r ancestors(@, 5) # Last 5 ancestors
jj log -r ancestors(@, 1) # Just parent (same as @-)
```
### Range Operators
#### `..x` (Ancestors excluding root)
Ancestors of x, excluding the root commit.
```bash
jj log -r ..@ # Ancestors of @ without root
jj log -r ..main # History up to main
```
#### `x..` (Non-ancestors)
x and its descendants, excluding ancestors.
```bash
jj log -r @.. # Working copy and descendants
jj log -r main.. # Everything not ancestral to main
```
#### `x..y` (Range)
Commits in y but not in x (y's descendants minus x's ancestors).
```bash
jj log -r main..@ # Commits added since main
jj log -r @-..@ # Just @ (changes since parent)
jj log -r @..main # Commits in main not in @
```
### Set Operators
#### `x | y` (Union)
Commits in either x or y (or both).
```bash
jj log -r '@- | @' # Parent and working copy
jj log -r 'main | feature' # Both branches
jj log -r 'bookmarks() | tags()' # All bookmarks and tags
```
#### `x & y` (Intersection)
Commits in both x and y.
```bash
jj log -r 'author(alice) & description(bug)' # Alice's bug fixes
jj log -r 'mine() & ::main' # My commits in main's history
```
#### `x ~ y` (Difference)
Commits in x but not in y.
```bash
jj log -r 'all() ~ main' # All commits except main
jj log -r '@:: ~ @' # Descendants excluding working copy
jj log -r 'mine() ~ ::main' # My commits not yet in main
```
#### `~x` (Complement)
All commits except those in x.
```bash
jj log -r '~@' # Everything except working copy
jj log -r '~main::' # Everything not descended from main
```
### Operator Precedence
From highest to lowest:
1. `-` (parents), `+` (children)
2. `::`, `..` (DAG ranges)
3. `~` (negation/complement)
4. `&` (intersection)
5. `~` (set difference)
6. `|` (union)
Use parentheses to override:
```bash
jj log -r '(main | feature) & author(alice)'
jj log -r 'main::(@ | feature)'
```
## Functions
Functions query and filter commits based on various criteria.
### Traversal Functions
#### `ancestors(x[, depth])`
All ancestors of x, optionally limited by depth.
```bash
jj log -r 'ancestors(@)' # All ancestors
jj log -r 'ancestors(@, 5)' # 5 generations back
jj log -r 'ancestors(main, 10)' # 10 commits before main
```
#### `descendants(x)`
All descendants of x, including x.
```bash
jj log -r 'descendants(main)' # main and everything after
jj log -r 'descendants(@-)' # Parent and its descendants
```
#### `connected(x)`
Commits connected to x through parents and children.
```bash
jj log -r 'connected(@)' # Connected component containing @
```
#### `reachable(x, domain)`
Commits in domain that are reachable from x.
```bash
jj log -r 'reachable(@, all())' # All reachable from @
```
### Set Functions
#### `all()`
All commits in the repository.
```bash
jj log -r 'all()' # Entire history
jj log -r 'all() ~ ::main' # Everything not in main's history
```
#### `none()`
Empty set (no commits).
```bash
jj log -r 'none()' # No output
jj log -r 'none() | @' # Just @ (contrived example)
```
#### `heads(x)`
Commits in x that have no children in x.
```bash
jj log -r 'heads(all())' # All branch tips
jj log -r 'heads(main::)' # Tips descended from main
```
#### `roots(x)`
Commits in x that have no parents in x.
```bash
jj log -r 'roots(main..@)' # First commits after branching from main
jj log -r 'roots(bookmarks())' # Root commits of bookmarked branches
```
#### `latest(x[, count])`
Latest (newest) commits from x, optionally limited.
```bash
jj log -r 'latest(all())' # Most recent commit
jj log -r 'latest(all(), 10)' # 10 most recent commits
jj log -r 'latest(author(alice), 5)' # Alice's 5 latest commits
```
### Bookmark and Tag Functions
#### `bookmarks([pattern])`
Commits pointed to by bookmarks, optionally filtered by pattern.
```bash
jj log -r 'bookmarks()' # All bookmarked commits
jj log -r 'bookmarks(main)' # Main bookmark
jj log -r 'bookmarks(glob:feature-*)' # Feature branches
```
Pattern types: `substring:`, `exact:`, `glob:`, `regex:` (append `-i` for case-insensitive)
#### `remote_bookmarks([pattern[, [remote=]pattern]])`
Remote-tracking bookmarks, optionally filtered.
```bash
jj log -r 'remote_bookmarks()' # All remote bookmarks
jj log -r 'remote_bookmarks(main)' # main on all remotes
jj log -r 'remote_bookmarks(main, origin)' # main@origin
jj log -r 'remote_bookmarks(glob:feature-*, origin)' # Feature branches on origin
```
#### `tags([pattern])`
Commits with tags, optionally filtered.
```bash
jj log -r 'tags()' # All tagged commits
jj log -r 'tags(v1.0.0)' # Specific tag
jj log -r 'tags(glob:v1.*)' # All v1.x tags
```
#### `git_refs()`
All Git references.
```bash
jj log -r 'git_refs()' # All Git refs
```
#### `git_head()`
Git's HEAD reference.
```bash
jj log -r 'git_head()' # Show Git HEAD
```
#### `tracked_remote_bookmarks([bookmark_pattern[, [remote=]remote_pattern]])`
Bookmarks with remote tracking configured.
```bash
jj log -r 'tracked_remote_bookmarks()' # All tracked bookmarks
jj log -r 'tracked_remote_bookmarks(main)' # Tracked main branches
```
#### `untracked_remote_bookmarks([bookmark_pattern[, [remote=]remote_pattern]])`
Remote bookmarks without local tracking.
```bash
jj log -r 'untracked_remote_bookmarks()' # Untracked remotes
```
### Author and Committer Functions
#### `author(pattern)`
Commits where author matches pattern.
```bash
jj log -r 'author(alice)' # Alice's commits
jj log -r 'author("Alice Smith")' # Exact name
jj log -r 'author(glob:*@example.com)' # By email domain
jj log -r 'author(regex:^A)' # Names starting with A
```
Pattern matching supports: `substring:`, `exact:`, `glob:`, `regex:` (with `-i` suffix for case-insensitive)
#### `committer(pattern)`
Commits where committer matches pattern.
```bash
jj log -r 'committer(bot)' # Commits by bot
jj log -r 'committer(glob:*@github.com)' # GitHub committers
```
#### `mine()`
Commits authored or committed by the current user.
```bash
jj log -r 'mine()' # All my commits
jj log -r 'mine() & ::main' # My commits in main
jj log -r 'mine() ~ ::main' # My commits not in main
```
### Description Functions
#### `description(pattern)`
Commits with description matching pattern.
```bash
jj log -r 'description(bug)' # Commits mentioning "bug"
jj log -r 'description(exact:Fix bug #123)' # Exact match
jj log -r 'description(glob:*BREAKING*)' # Contains "BREAKING"
jj log -r 'description(regex:^fix:)' # Conventional commit fixes
```
#### `subject(pattern)`
Commits with first line of description matching pattern.
```bash
jj log -r 'subject(feat:)' # Feature commits
jj log -r 'subject(glob:*WIP*)' # WIP commits
```
### File Functions
#### `files(pattern)`
Commits modifying files matching pattern.
```bash
jj log -r 'files(glob:*.rs)' # Commits changing Rust files
jj log -r 'files(src/main.rs)' # Commits changing specific file
jj log -r 'files(glob:**/test_*)' # Commits changing test files
```
Uses fileset syntax (see filesets.md).
#### `diff_contains(pattern[, [files=]fileset])`
Commits with diff containing pattern, optionally in specific files.
```bash
jj log -r 'diff_contains(TODO)' # Commits adding/removing TODO
jj log -r 'diff_contains(bug, files=glob:*.rs)' # "bug" in Rust files
jj log -r 'diff_contains(regex:\bfix\b)' # Word "fix" in diff
```
### State Functions
#### `empty()`
Commits with no changes (empty diffs).
```bash
jj log -r 'empty()' # All empty commits
jj log -r 'mine() & empty()' # My empty commits
jj abandon $(jj log -r 'empty()' -T commit_id --no-graph) # Remove empty commits
```
#### `conflict()`
Commits with unresolved conflicts.
```bash
jj log -r 'conflict()' # Find conflicted commits
jj resolve $(jj log -r 'conflict()' -T commit_id --no-graph | head -1) # Resolve first
```
#### `immutable()`
Commits considered immutable (configured in settings).
```bash
jj log -r 'immutable()' # Show immutable commits
jj log -r 'all() ~ immutable()' # Only mutable commits
```
#### `present(x)`
Returns x if it exists, otherwise empty set.
```bash
jj log -r 'present(some-branch)' # Branch if it exists
jj rebase -d 'present(main) | @-' # Use main if exists, else parent
```
Useful for scripting to avoid errors when references don't exist.
### Visibility Functions
#### `visible_heads()`
Commits that are visible heads.
```bash
jj log -r 'visible_heads()' # All visible heads
```
#### `immutable_heads()`
Heads of immutable commits.
```bash
jj log -r 'immutable_heads()' # Immutable heads
```
## Pattern Matching
String patterns in functions support multiple matching modes.
### Pattern Prefixes
- `substring:` - Default, matches if pattern appears anywhere
- `exact:` - Exact match only
- `glob:` - Unix-style glob patterns
- `regex:` - Regular expression matching
Add `-i` suffix for case-insensitive matching:
- `substring-i:`, `exact-i:`, `glob-i:`, `regex-i:`
### Examples
```bash
# Substring (default)
jj log -r 'author(alice)'
jj log -r 'author(substring:alice)' # Equivalent
# Exact match
jj log -r 'author(exact:Alice Smith)'
jj log -r 'description(exact:Fix bug #123)'
# Glob patterns
jj log -r 'author(glob:*@example.com)'
jj log -r 'bookmarks(glob:feature-*)'
jj log -r 'files(glob:**/*.rs)'
# Regular expressions
jj log -r 'description(regex:^feat:)'
jj log -r 'author(regex:\d+\+.*@users.noreply.github.com)'
# Case-insensitive
jj log -r 'author(substring-i:ALICE)'
jj log -r 'description(glob-i:*breaking*)'
```
## Practical Examples
### Finding Commits
```bash
# Recent work
jj log -r 'latest(mine(), 10)' # My 10 most recent commits
# Commits by criteria
jj log -r 'author(alice) & description(bug)' # Alice's bug fixes
jj log -r 'mine() & conflict()' # My conflicted commits
jj log -r 'files(glob:*.rs) & description(perf)' # Rust performance commits
# Branch comparisons
jj log -r 'main..@' # My commits not in main
jj log -r 'main..feature' # Commits in feature not in main
jj log -r 'main@origin..' # All commits not pushed
```
### Working with Branches
```bash
# View all branches
jj log -r 'bookmarks()'
# Active development branches
jj log -r 'heads(all()) ~ remote_bookmarks()'
# Branches needing updates
jj log -r 'bookmarks() ~ ::remote_bookmarks(main, origin)'
# Merged branches
jj log -r 'bookmarks() & ::main'
```
### History Analysis
```bash
# Commits touching specific files
jj log -r 'files(src/auth.rs)'
# Recent changes to tests
jj log -r 'latest(files(glob:**/*_test.rs), 20)'
# Commits between tags
jj log -r 'tags(v1.0.0)..tags(v2.0.0)'
# Commits not yet tagged
jj log -r 'heads(all()) ~ ::tags()'
```
### Commit Cleanup
```bash
# Find empty commits
jj log -r 'empty()'
# Find WIP commits
jj log -r 'description(glob:*WIP*)'
# Commits needing attention
jj log -r 'conflict() | empty() | description(glob:*TODO*)'
```
### Working with Remotes
```bash
# Commits not pushed
jj log -r 'mine() ~ ::remote_bookmarks()'
# Remote updates
jj log -r 'remote_bookmarks() ~ ::bookmarks()'
# Diverged branches
jj log -r '(main ~ ::main@origin) | (main@origin ~ ::main)'
```
## Complex Queries
### Multi-Criteria Searches
```bash
# My recent bug fixes in Rust code
jj log -r '
latest(
mine()
& description(glob:*fix*|*bug*)
& files(glob:**/*.rs),
20
)
'
# Commits by team members on feature branches
jj log -r '
(author(alice) | author(bob) | author(carol))
& bookmarks(glob:feature-*)::
~ ::main
'
# Risky commits (large changes, no tests)
jj log -r '
files(glob:src/**/*.rs)
~ files(glob:**/*_test.rs)
& description(regex:(?i)\b(refactor|rewrite)\b)
'
```
### Conditional Selection
```bash
# Use main@origin if it exists, otherwise main
jj rebase -d 'present(main@origin) | main'
# Latest stable release or head
jj log -r 'present(tags(glob:v*.*.0)) | heads(all())'
```
### Graph Queries
```bash
# All commits between branches
jj log -r 'main::feature ~ (::main | feature::)'
# Divergent changes
jj log -r '(main ~ ::feature) | (feature ~ ::main)'
# Common ancestors
jj log -r '::main & ::feature'
```
## Best Practices
### 1. Start Simple, Then Refine
```bash
# Start broad
jj log -r 'mine()'
# Add date constraints
jj log -r 'latest(mine(), 20)'
# Add content filter
jj log -r 'latest(mine() & description(feature), 20)'
```
### 2. Use Readable Multi-Line Queries
```bash
# Hard to read
jj log -r 'latest(mine() & (description(bug) | description(fix)) & files(glob:src/**/*.rs) ~ ::main, 10)'
# Readable
jj log -r '
latest(
mine()
& (description(bug) | description(fix))
& files(glob:src/**/*.rs)
~ ::main,
10
)
'
```
### 3. Create Aliases for Common Queries
Edit config with `jj config edit --user`:
```toml
[revset-aliases]
# My commits not in main
work = "mine() ~ ::main"
# Commits needing attention
attention = "conflict() | empty() | description(glob:*WIP*) | description(glob:*TODO*)"
# Recent changes
recent = "latest(all(), 20)"
# Unpushed work
unpushed = "mine() ~ ::remote_bookmarks()"
```
Use in commands:
```bash
jj log -r work
jj log -r attention
jj log -r recent
```
### 4. Use `present()` for Robustness
```bash
# Fails if branch doesn't exist
jj rebase -d some-branch
# Safe version
jj rebase -d 'present(some-branch) | @-'
```
### 5. Combine with Templates for Insight
```bash
# Show commit IDs of specific commits
jj log -r 'conflict()' -T commit_id --no-graph
# Count commits
jj log -r 'mine() ~ ::main' --no-graph | wc -l
# Custom format for scripting
jj log -r 'latest(mine(), 5)' -T 'commit_id ++ " " ++ description.first_line() ++ "\n"'
```
## Troubleshooting
### No Commits Matched
```bash
# Check individual parts
jj log -r 'author(alice)' # Does author filter work?
jj log -r 'description(bug)' # Does description filter work?
jj log -r 'author(alice) & description(bug)' # Combine
# Verify symbols exist
jj bookmark list # Check bookmark names
jj log -r 'bookmarks()' # See all bookmarks
```
### Unexpected Results
```bash
# Use `all()` to understand set size
jj log -r 'all() ~ ::main' # Everything not in main
# Break down complex queries
jj log -r 'mine()' # Step 1
jj log -r 'mine() ~ ::main' # Step 2
jj log -r 'latest(mine() ~ ::main, 10)' # Step 3
```
### Performance Issues
```bash
# Large repositories may be slow with broad queries
jj log -r 'all()' # Can be slow
# Optimize by limiting scope
jj log -r 'latest(all(), 100)' # Faster
jj log -r '::@' # Just ancestors
jj log -r 'ancestors(@, 50)' # Limited depth
```
## Integration with Commands
Revsets work with most jujutsu commands:
### Viewing History
```bash
jj log -r '<revset>' # Show log
jj show -r '<revset>' # Show details
jj diff -r '<revset>' # Show changes
jj evolog -r '<revset>' # Show evolution log
```
### Modifying Commits
```bash
jj edit <revset> # Edit commit
jj squash -r <revset> # Squash commit
jj abandon <revset> # Abandon commits
jj describe -r <revset> # Set description
```
### Rebasing
```bash
jj rebase -d <revset> # Rebase destination
jj rebase -r <revset> -d <dest> # Rebase specific commit
jj rebase -s <revset> -d <dest> # Rebase source and descendants
```
### Branching
```bash
jj bookmark set <name> -r <revset> # Set bookmark
jj new <revset> # New commit on base
```
## Summary
Revsets are the foundation of powerful commit selection in jujutsu:
- **Symbols**: `@`, `root()`, commit IDs, bookmarks, tags
- **Operators**: `-/+` (parents/children), `::` (ancestors/descendants), `|/&/~` (set operations)
- **Functions**: `mine()`, `author()`, `description()`, `files()`, `conflict()`, `empty()`, etc.
- **Patterns**: `substring:`, `exact:`, `glob:`, `regex:` (with `-i` for case-insensitive)
- **Best practices**: Start simple, use aliases, combine with templates
- **Integration**: Works across all jujutsu commands
For file-based filtering, see filesets.md. For output customization, see templating.md.
Master revsets to unlock jujutsu's full potential for commit management and history navigation.