567 lines
12 KiB
Markdown
567 lines
12 KiB
Markdown
# Jujutsu Filesets - Complete Reference
|
|
|
|
Filesets are a functional language in jujutsu for selecting and filtering files. This document provides comprehensive coverage of the fileset syntax, operators, functions, and practical usage patterns.
|
|
|
|
## Overview
|
|
|
|
The fileset language (inspired by Mercurial) enables precise file selection across jujutsu commands. Expressions in this language are called "filesets" and can be used with any command that accepts file arguments.
|
|
|
|
## Core Concept
|
|
|
|
Filesets allow you to specify which files should be affected by operations using:
|
|
- File patterns and paths
|
|
- Logical operators for combining selections
|
|
- Functions for special selections
|
|
|
|
## File Pattern Types
|
|
|
|
### 1. Path Prefixes (Default)
|
|
|
|
Match files recursively under directories or exact file paths.
|
|
|
|
```bash
|
|
# Syntax: "path" or cwd:"path"
|
|
jj diff "src" # All files under src/
|
|
jj diff "src/main.rs" # Specific file
|
|
jj diff cwd:"relative" # Relative to current working directory
|
|
```
|
|
|
|
**Behavior:**
|
|
- If path is a directory: matches all files recursively under it
|
|
- If path is a file: matches that exact file
|
|
- Paths are relative to repository root by default
|
|
- `cwd:` prefix makes paths relative to current directory
|
|
|
|
### 2. Exact File Match
|
|
|
|
Match only specific files, not directories.
|
|
|
|
```bash
|
|
# Syntax: file:"path" or cwd-file:"path"
|
|
jj diff 'file:"src"' # Only if "src" is a file, not directory
|
|
jj diff 'cwd-file:"README.md"' # Exact file relative to cwd
|
|
```
|
|
|
|
**Use case:** When you want to ensure you're matching a file, not a directory.
|
|
|
|
### 3. Glob Patterns
|
|
|
|
Use Unix-style wildcards for pattern matching.
|
|
|
|
```bash
|
|
# Syntax: glob:"pattern"
|
|
jj diff 'glob:"*.rs"' # All Rust files in root
|
|
jj diff 'glob:"**/*.rs"' # All Rust files recursively
|
|
jj diff 'glob:"src/**/*.test.ts"' # All test files in src/
|
|
jj diff 'glob:"*.{rs,toml}"' # All .rs and .toml files
|
|
```
|
|
|
|
**Wildcard patterns:**
|
|
- `*` - Match any characters except `/`
|
|
- `**` - Match any characters including `/` (recursive)
|
|
- `?` - Match single character
|
|
- `[abc]` - Match one character from set
|
|
- `[a-z]` - Match one character from range
|
|
- `{a,b}` - Match either pattern a or b
|
|
|
|
### 4. Case-Insensitive Glob
|
|
|
|
Like glob but ignores case distinctions.
|
|
|
|
```bash
|
|
# Syntax: glob-i:"pattern"
|
|
jj diff 'glob-i:"*.MD"' # Matches .md, .MD, .Md, etc.
|
|
jj diff 'glob-i:"readme.*"' # Matches README.txt, readme.md, etc.
|
|
```
|
|
|
|
### 5. Root-Relative Variants
|
|
|
|
Explicitly specify paths relative to repository root.
|
|
|
|
```bash
|
|
# Syntax: root:, root-file:, root-glob:, root-glob-i:
|
|
jj diff 'root:src' # src/ from repo root
|
|
jj diff 'root-file:Cargo.toml' # Exact file from root
|
|
jj diff 'root-glob:**/test_*.rs' # Pattern from root
|
|
jj diff 'root-glob-i:**/README.*' # Case-insensitive from root
|
|
```
|
|
|
|
**Use case:** Ensure consistent behavior regardless of current working directory.
|
|
|
|
## Operators
|
|
|
|
Operators combine filesets using set logic. Listed by precedence (highest to lowest):
|
|
|
|
### 1. Negation (`~x`)
|
|
|
|
Select everything except x.
|
|
|
|
```bash
|
|
jj diff '~Cargo.lock' # Everything except Cargo.lock
|
|
jj diff '~glob:"*.test.ts"' # All files except tests
|
|
jj diff '~"target"' # Everything except target/
|
|
```
|
|
|
|
**Precedence:** Highest (binds tightly to operand)
|
|
|
|
### 2. Intersection (`x & y`)
|
|
|
|
Select files that match both x and y.
|
|
|
|
```bash
|
|
jj diff 'src & glob:"*.rs"' # Rust files in src/
|
|
jj diff 'glob:"*.rs" & ~glob:"*_test.rs"' # Rust files except tests
|
|
```
|
|
|
|
**Use case:** Narrow down selection by combining criteria.
|
|
|
|
### 3. Difference (`x ~ y`)
|
|
|
|
Select files in x but not in y.
|
|
|
|
```bash
|
|
jj diff 'src ~ glob:"*.test.ts"' # Files in src/ excluding tests
|
|
jj diff 'all() ~ "target"' # Everything except target/
|
|
```
|
|
|
|
**Use case:** Remove specific files from a larger selection.
|
|
|
|
### 4. Union (`x | y`)
|
|
|
|
Select files that match either x or y (or both).
|
|
|
|
```bash
|
|
jj diff 'glob:"*.rs" | glob:"*.toml"' # All Rust and TOML files
|
|
jj diff 'src | tests' # Files in src/ or tests/
|
|
jj diff '"README.md" | "LICENSE"' # Either file
|
|
```
|
|
|
|
**Precedence:** Lowest (evaluates last)
|
|
|
|
### Operator Precedence Summary
|
|
|
|
From highest to lowest:
|
|
1. `~x` (negation)
|
|
2. `x & y` (intersection)
|
|
3. `x ~ y` (difference)
|
|
4. `x | y` (union)
|
|
|
|
Use parentheses to override precedence:
|
|
```bash
|
|
jj diff '(src | tests) & glob:"*.rs"' # Rust files in src/ or tests/
|
|
jj diff 'src | (tests & glob:"*.rs")' # All src/ files OR Rust test files
|
|
```
|
|
|
|
## Functions
|
|
|
|
Built-in functions for special selections.
|
|
|
|
### 1. `all()`
|
|
|
|
Select all files in the working copy.
|
|
|
|
```bash
|
|
jj diff 'all()' # Show all changes
|
|
jj diff 'all() ~ "vendor"' # All files except vendor/
|
|
```
|
|
|
|
**Use case:** Starting point for exclusion-based selections.
|
|
|
|
### 2. `none()`
|
|
|
|
Select no files.
|
|
|
|
```bash
|
|
jj diff 'none()' # No output
|
|
jj diff 'none() | "README.md"' # Just README.md (contrived example)
|
|
```
|
|
|
|
**Use case:** Rare; mainly for testing or as a base for unions.
|
|
|
|
## Practical Examples
|
|
|
|
### Excluding Files
|
|
|
|
```bash
|
|
# Exclude single file
|
|
jj diff '~Cargo.lock'
|
|
|
|
# Exclude directory
|
|
jj diff '~target'
|
|
|
|
# Exclude multiple patterns
|
|
jj diff '~glob:"*.lock" & ~"target"'
|
|
```
|
|
|
|
### Working with Specific File Types
|
|
|
|
```bash
|
|
# All Rust files
|
|
jj diff 'glob:"**/*.rs"'
|
|
|
|
# Rust files except tests
|
|
jj diff 'glob:"**/*.rs" ~ glob:"**/*_test.rs"'
|
|
|
|
# TypeScript and JavaScript
|
|
jj diff 'glob:"**/*.{ts,js}"'
|
|
|
|
# Source files only (exclude tests and configs)
|
|
jj diff 'src & ~glob:"*.test.*" & ~glob:"*.config.*"'
|
|
```
|
|
|
|
### Directory-Based Selections
|
|
|
|
```bash
|
|
# Single directory
|
|
jj diff 'src'
|
|
|
|
# Multiple directories
|
|
jj diff 'src | tests | docs'
|
|
|
|
# Nested directories
|
|
jj diff 'src/components'
|
|
|
|
# Directory except subdirectory
|
|
jj diff 'src ~ src/generated'
|
|
```
|
|
|
|
### Complex Filtering
|
|
|
|
```bash
|
|
# Source Rust files, excluding tests and benchmarks
|
|
jj diff 'glob:"src/**/*.rs" ~ glob:"**/*_test.rs" ~ glob:"**/bench_*.rs"'
|
|
|
|
# Configuration files only
|
|
jj diff 'glob:"*.{toml,yaml,yml,json}"'
|
|
|
|
# Documentation files
|
|
jj diff 'glob:"**/*.md" | glob:"**/*.txt" | "LICENSE"'
|
|
|
|
# Modified files in src/ or tests/, excluding snapshots
|
|
jj diff '(src | tests) ~ glob:"**/*.snap"'
|
|
```
|
|
|
|
## Using Filesets in Commands
|
|
|
|
Filesets can be used with many jujutsu commands:
|
|
|
|
### `jj diff`
|
|
|
|
Show changes for selected files.
|
|
|
|
```bash
|
|
jj diff 'glob:"*.rs"' # Changes to Rust files
|
|
jj diff '~vendor' # Changes except vendor/
|
|
jj diff -r @- 'src' # Changes to src/ in parent commit
|
|
```
|
|
|
|
### `jj split`
|
|
|
|
Split commit including only selected files.
|
|
|
|
```bash
|
|
# Split out documentation changes
|
|
jj split 'glob:"**/*.md"'
|
|
|
|
# Split out everything except tests
|
|
jj split '~glob:"**/*.test.ts"'
|
|
|
|
# Split specific directory
|
|
jj split 'src/auth'
|
|
```
|
|
|
|
### `jj file list`
|
|
|
|
List files matching selection.
|
|
|
|
```bash
|
|
jj file list 'glob:"*.rs"' # List all Rust files
|
|
jj file list 'src ~ glob:"*.test.*"' # Source files excluding tests
|
|
```
|
|
|
|
### `jj restore`
|
|
|
|
Restore selected files from another commit.
|
|
|
|
```bash
|
|
jj restore --from @- 'Cargo.lock' # Restore lockfile from parent
|
|
jj restore 'glob:"*.config.ts"' # Restore config files
|
|
```
|
|
|
|
### `jj squash`
|
|
|
|
Squash only selected files.
|
|
|
|
```bash
|
|
# Squash only documentation changes
|
|
jj squash 'glob:"**/*.md"'
|
|
|
|
# Squash everything except tests
|
|
jj squash '~glob:"**/*.test.*"'
|
|
```
|
|
|
|
## Quoting and Shell Escaping
|
|
|
|
### When to Quote
|
|
|
|
Filesets often contain special characters that shells interpret. Always quote filesets:
|
|
|
|
```bash
|
|
# WRONG - Shell interprets * and other characters
|
|
jj diff glob:*.rs
|
|
|
|
# CORRECT - Quoted to protect from shell
|
|
jj diff 'glob:*.rs'
|
|
```
|
|
|
|
### Quote Style
|
|
|
|
Use single quotes to avoid shell variable expansion:
|
|
|
|
```bash
|
|
# Single quotes (preferred) - No expansion
|
|
jj diff '~glob:"*.lock"'
|
|
|
|
# Double quotes - Variables would expand
|
|
jj diff "~glob:\"*.lock\"" # More complex escaping needed
|
|
```
|
|
|
|
## Advanced Patterns
|
|
|
|
### Case-Insensitive Matching
|
|
|
|
```bash
|
|
# Match regardless of case
|
|
jj diff 'glob-i:"readme.*"' # README.md, readme.txt, Readme.rst
|
|
|
|
# Case-insensitive extension
|
|
jj diff 'glob-i:"*.jpg"' # .jpg, .JPG, .Jpg
|
|
```
|
|
|
|
### Combining Multiple Criteria
|
|
|
|
```bash
|
|
# TypeScript files in src/, excluding tests and generated files
|
|
jj diff '
|
|
src
|
|
& glob:"**/*.ts"
|
|
~ glob:"**/*.test.ts"
|
|
~ glob:"**/*.generated.ts"
|
|
'
|
|
|
|
# Any source file, excluding dependencies
|
|
jj diff '
|
|
(glob:"**/*.rs" | glob:"**/*.ts" | glob:"**/*.py")
|
|
~ "vendor"
|
|
~ "node_modules"
|
|
~ "target"
|
|
'
|
|
```
|
|
|
|
### Working Copy vs Repository Root
|
|
|
|
```bash
|
|
# From current directory
|
|
jj diff 'cwd:"utils"' # utils/ relative to pwd
|
|
|
|
# From repository root
|
|
jj diff 'root:utils' # utils/ relative to repo root
|
|
|
|
# Both styles in combination
|
|
jj diff 'cwd:"src" | root:tests'
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
### Exclude Generated Files
|
|
|
|
```bash
|
|
jj diff '
|
|
all()
|
|
~ "target"
|
|
~ "node_modules"
|
|
~ "dist"
|
|
~ "build"
|
|
~ glob:"*.generated.*"
|
|
'
|
|
```
|
|
|
|
### Source Code Only
|
|
|
|
```bash
|
|
jj diff '
|
|
glob:"**/*.{rs,ts,py,go,java}"
|
|
~ glob:"**/*.test.*"
|
|
~ glob:"**/*.spec.*"
|
|
'
|
|
```
|
|
|
|
### Configuration Files
|
|
|
|
```bash
|
|
jj diff 'glob:"*.{toml,yaml,yml,json,ini,conf}"'
|
|
```
|
|
|
|
### Documentation Files
|
|
|
|
```bash
|
|
jj diff '
|
|
glob:"**/*.md"
|
|
| glob:"**/*.rst"
|
|
| glob:"**/*.txt"
|
|
| "LICENSE"
|
|
| "README"
|
|
'
|
|
```
|
|
|
|
### Test Files Only
|
|
|
|
```bash
|
|
jj diff '
|
|
glob:"**/*.test.{ts,js}"
|
|
| glob:"**/*_test.{rs,go}"
|
|
| glob:"**/test_*.py"
|
|
'
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### 1. Start Simple, Then Refine
|
|
|
|
```bash
|
|
# Start broad
|
|
jj diff 'src'
|
|
|
|
# Add exclusions
|
|
jj diff 'src ~ glob:"*.test.*"'
|
|
|
|
# Further refine
|
|
jj diff 'src ~ glob:"*.test.*" ~ "src/generated"'
|
|
```
|
|
|
|
### 2. Use Functions for Clarity
|
|
|
|
```bash
|
|
# Clear intent with all()
|
|
jj diff 'all() ~ "target"'
|
|
|
|
# Rather than relying on implicit all
|
|
jj diff '~"target"' # Works but less explicit
|
|
```
|
|
|
|
### 3. Prefer Specific Patterns
|
|
|
|
```bash
|
|
# Specific - matches exactly what you want
|
|
jj diff 'glob:"src/**/*.rs"'
|
|
|
|
# Too broad - might match unexpected files
|
|
jj diff 'glob:"*.rs"'
|
|
```
|
|
|
|
### 4. Test Filesets with `jj file list`
|
|
|
|
```bash
|
|
# Test your fileset before using in operations
|
|
jj file list 'glob:"**/*.rs" ~ glob:"**/*_test.rs"'
|
|
|
|
# Then use in actual command
|
|
jj split 'glob:"**/*.rs" ~ glob:"**/*_test.rs"'
|
|
```
|
|
|
|
### 5. Use Root-Relative for Scripts
|
|
|
|
```bash
|
|
# In scripts, use root: for consistency
|
|
jj diff 'root:src/main.rs'
|
|
|
|
# Rather than cwd: which depends on pwd
|
|
jj diff 'cwd:src/main.rs' # Breaks if not in repo root
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### No Files Matched
|
|
|
|
If your fileset matches nothing:
|
|
|
|
```bash
|
|
# Check what files exist
|
|
jj file list
|
|
|
|
# Test individual parts
|
|
jj file list 'glob:"*.rs"'
|
|
jj file list 'src'
|
|
|
|
# Check if negation is too broad
|
|
jj file list '~glob:"*.test.*"' # Might exclude everything
|
|
```
|
|
|
|
### Unexpected Matches
|
|
|
|
```bash
|
|
# Use file list to debug
|
|
jj file list 'your-fileset-here'
|
|
|
|
# Check pattern specificity
|
|
jj file list 'glob:*.rs' # Only root level
|
|
jj file list 'glob:**/*.rs' # All levels (recursive)
|
|
```
|
|
|
|
### Shell Quoting Issues
|
|
|
|
```bash
|
|
# If seeing errors, check quoting
|
|
jj diff glob:*.rs # WRONG - shell expands *
|
|
jj diff 'glob:*.rs' # CORRECT - quoted
|
|
|
|
# For complex patterns, use single quotes
|
|
jj diff '~glob:"*.{rs,toml}"' # CORRECT
|
|
```
|
|
|
|
## Integration with Other Features
|
|
|
|
### With Revsets
|
|
|
|
Combine filesets with revset filtering:
|
|
|
|
```bash
|
|
# Show Rust file changes in specific commit
|
|
jj diff -r <commit> 'glob:"**/*.rs"'
|
|
|
|
# Changes to src/ between two commits
|
|
jj diff --from <commit1> --to <commit2> 'src'
|
|
```
|
|
|
|
### With Interactive Commands
|
|
|
|
```bash
|
|
# Interactively squash only test files
|
|
jj squash -i 'glob:"**/*.test.*"'
|
|
|
|
# Interactively split documentation changes
|
|
jj split 'glob:"**/*.md"'
|
|
```
|
|
|
|
### In Configuration
|
|
|
|
Some commands accept default filesets in config:
|
|
|
|
```toml
|
|
[ui]
|
|
# Example: ignore certain files in diff by default
|
|
# (Check current jj docs for actual config options)
|
|
```
|
|
|
|
## Summary
|
|
|
|
Filesets provide powerful, flexible file selection in jujutsu:
|
|
|
|
- **Pattern types**: paths, files, globs (case-sensitive and insensitive), root-relative
|
|
- **Operators**: `~` (not), `&` (and), `~` (minus), `|` (or)
|
|
- **Functions**: `all()`, `none()`
|
|
- **Always quote** filesets to protect from shell interpretation
|
|
- **Test with** `jj file list` before using in destructive operations
|
|
- **Combine with revsets** for precise change management
|
|
|
|
For more information, see the official jujutsu documentation on filesets.
|