19 KiB
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.
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.
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.
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.
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).
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.
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):
jj log -r @-+ # Children of all parents (siblings)
x+ (Children)
Immediate children of x.
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.
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.
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.
jj log -r main::@ # Commits from main to @
jj log -r @-::@ # Just @ (parent to working copy)
::x with depth
Ancestors with limited depth.
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.
jj log -r ..@ # Ancestors of @ without root
jj log -r ..main # History up to main
x.. (Non-ancestors)
x and its descendants, excluding ancestors.
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).
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).
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.
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.
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.
jj log -r '~@' # Everything except working copy
jj log -r '~main::' # Everything not descended from main
Operator Precedence
From highest to lowest:
-(parents),+(children)::,..(DAG ranges)~(negation/complement)&(intersection)~(set difference)|(union)
Use parentheses to override:
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.
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.
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.
jj log -r 'connected(@)' # Connected component containing @
reachable(x, domain)
Commits in domain that are reachable from x.
jj log -r 'reachable(@, all())' # All reachable from @
Set Functions
all()
All commits in the repository.
jj log -r 'all()' # Entire history
jj log -r 'all() ~ ::main' # Everything not in main's history
none()
Empty set (no commits).
jj log -r 'none()' # No output
jj log -r 'none() | @' # Just @ (contrived example)
heads(x)
Commits in x that have no children in x.
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.
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.
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.
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.
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.
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.
jj log -r 'git_refs()' # All Git refs
git_head()
Git's HEAD reference.
jj log -r 'git_head()' # Show Git HEAD
tracked_remote_bookmarks([bookmark_pattern[, [remote=]remote_pattern]])
Bookmarks with remote tracking configured.
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.
jj log -r 'untracked_remote_bookmarks()' # Untracked remotes
Author and Committer Functions
author(pattern)
Commits where author matches pattern.
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.
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.
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.
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.
jj log -r 'subject(feat:)' # Feature commits
jj log -r 'subject(glob:*WIP*)' # WIP commits
File Functions
files(pattern)
Commits modifying files matching pattern.
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.
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).
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.
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).
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.
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.
jj log -r 'visible_heads()' # All visible heads
immutable_heads()
Heads of immutable commits.
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 anywhereexact:- Exact match onlyglob:- Unix-style glob patternsregex:- Regular expression matching
Add -i suffix for case-insensitive matching:
substring-i:,exact-i:,glob-i:,regex-i:
Examples
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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
# 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:
[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:
jj log -r work
jj log -r attention
jj log -r recent
4. Use present() for Robustness
# 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
# 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
# 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
# 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
# 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
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
jj edit <revset> # Edit commit
jj squash -r <revset> # Squash commit
jj abandon <revset> # Abandon commits
jj describe -r <revset> # Set description
Rebasing
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
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-ifor 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.