# 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 'glob:"**/*.rs"' # Changes to src/ between two commits jj diff --from --to '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.