Commit 3cd7f8c
Changed files (1)
modules
dev
agents
skills
jujutsu
modules/dev/agents/skills/jujutsu/SKILL.md
@@ -7,442 +7,446 @@ description: >
allowed-tools: Bash(jj *)
---
-# Jujutsu (jj) Version Control System
+# Jujutsu (jj) version control system
-This skill helps you work with Jujutsu, a Git-compatible VCS with mutable
-commits and automatic rebasing.
+Jujutsu is a Git-compatible VCS with mutable commits and automatic rebasing.
-**Tested with jj v0.37.0** - Commands may differ in other versions.
+Tested with jj v0.46.0. Commands may differ in other versions.
-## Important: Automated/Agent Environment
+## Automated and agent environments
When running as an agent:
-1. **Always use `--no-pager`** to prevent commands from opening an interactive
- pager (like `less`), which will hang the agent:
+- Pass `--no-pager` to every command that produces output. A pager such as
+ `less` waits for a keypress and hangs the run. `JJ_PAGER` overrides `ui.pager`
+ when you need a jj-specific setting.
```bash
-# Always use --no-pager on commands that produce output
-jj --no-pager log # NOT: jj log
-jj --no-pager diff # NOT: jj diff
-jj --no-pager show <id> # NOT: jj show <id>
+jj --no-pager log
+jj --no-pager diff
+jj --no-pager show <id>
```
-2. **Always use `-m` flags** to provide messages inline rather than relying on
- editor prompts:
+- Pass messages inline with `-m`. Commands that open an editor fail without a
+ terminal.
```bash
-# Always use -m to avoid editor prompts
-jj desc -m "message" # NOT: jj desc
-jj squash -m "message" # NOT: jj squash (which opens editor)
+jj desc -m "message"
+jj squash -m "message"
```
-Editor-based commands will fail in non-interactive environments.
+- Confirm mutations with `jj st`. After `squash`, `abandon`, `rebase`, or
+ `restore`, check that the operation did what you expected.
+
+- Avoid the interactive commands. They open a TUI or an editor and hang:
+ `jj arrange`, `jj split`, `jj resolve`, `jj diffedit`, `jj file edit`,
+ `jj squash -i`, and `jj absorb -i`.
-3. **Verify operations with `jj st`** after mutations (`squash`, `abandon`,
-`rebase`, `restore`) to confirm the operation succeeded.
+- Use `--no-integrate-operation` to run a command without touching the repo
+ state or the working copy, which helps when a background tool might snapshot
+ at the same time.
-## Core Concepts
+## Core concepts
-### The Working Copy is a Commit
+### The working copy is a commit
-In jj, your working directory is always a commit (referenced as `@`). Changes
-are automatically snapshotted when you run any jj command. There is no staging
-area.
+Your working directory is always a commit, referenced as `@`. Every jj command
+snapshots your changes automatically. There is no staging area, and no need to
+run `jj commit`.
-There is no need to run `jj commit`.
+### Commits are mutable
-### Commits Are Mutable
+Unlike git, jj commits can be rewritten after you create them. You can change
+the description, squash changes, rebase, and absorb without making new commits.
+See "Essential workflow" for the working pattern.
-**CRITICAL**: Unlike git, jj commits can be freely modified after creation. You
-can update descriptions, squash changes, rebase, and absorb — all without
-creating new commits. See "Essential Workflow" below for the recommended working
-pattern.
+### Change IDs vs commit IDs
-### Change IDs vs Commit IDs
+- Change ID: a stable identifier such as `tqpwlqmp` that survives a rewrite.
+ Prefer it when referencing commits.
+- Commit ID: a content hash such as `3ccf7581` that changes with the content.
-- **Change ID**: A stable identifier (like `tqpwlqmp`) that persists when a
- commit is rewritten — prefer these when referencing commits
-- **Commit ID**: A content hash (like `3ccf7581`) that changes when commit
- content changes
+A change ID can name several hidden or divergent commits at once. Add a numeric
+suffix to pick one: `xyz/0` is the newest version and `xyz/1` the one before it.
+So `jj restore --from xyz/1 --to xyz` puts `xyz` back to its previous contents.
### Revsets
-jj uses a revset language to select commits in commands. Common revsets:
+jj selects commits with a revset language. Common revsets:
+
+- `@`, the working copy commit
+- `@-`, the parent of the working copy
+- `::@`, all ancestors of `@`
+- `@::`, all descendants of `@`
+- `trunk()..@`, the commits between trunk and `@`
+- `bookmarks()`, every commit with a bookmark
-- `@` — the working copy commit
-- `@-` — the parent of the working copy
-- `::@` — all ancestors of `@`
-- `@::` — all descendants of `@`
-- `trunk()..@` — commits between trunk and `@` (your branch)
-- `bookmarks()` — all commits with bookmarks
+Useful functions added in recent versions: `divergent()`, `merge_point()`,
+`forks()`, `remote_tags()`, `diff_lines()`, `diff_lines_added()`,
+`diff_lines_removed()`, and `builtin_log()` (the default `jj log` revset).
-Use revsets with `-r` flags: `jj log -r 'trunk()..@'`
+Pass revsets with `-r`: `jj log -r 'trunk()..@'`.
-## Essential Workflow
+String patterns in revsets default to glob matching, so use the `substring:` or
+`exact:` prefix for other kinds. Git-style symbols such as `refs/heads/main` no
+longer resolve to revisions; use the bookmark or tag name.
-### Starting Work: Describe First, Then Code
+## Essential workflow
-**Always create your commit message before writing code:**
+### Starting work: describe first, then code
-Validate that you're on a blank revision with `jj st`. If you are not, you
-should type:
+Write the commit message before the code.
+
+Check that you're on a blank revision with `jj st`. If not, start one:
```bash
jj new
```
+Then describe the work and make the changes:
+
```bash
-# First, describe what you intend to do
jj desc -m "Add user authentication to login endpoint"
-
-# Then make your changes - they automatically become part of this commit
# ... edit files ...
-
-# Check status
jj st
```
-### Creating Atomic Commits
+The edits become part of that commit on their own.
-Each commit should represent ONE logical change. Use this format for commit
-messages:
+### Creating atomic commits
+
+Each commit should be one logical change. Message examples:
```text
-Examples:
-- "Add validation to user input forms"
-- "Fix null pointer in payment processor"
-- "Remove deprecated API endpoints"
-- "Update dependencies to latest versions"
+Add validation to user input forms
+Fix null pointer in payment processor
+Remove deprecated API endpoints
```
-### Viewing History
+### Viewing history
```bash
-# View recent commits
jj --no-pager log
-
-# View with patches
jj --no-pager log -p
-
-# View specific commit
jj --no-pager show <change-id>
-
-# View diff of working copy (use --git for familiar +/- format)
jj --no-pager diff --git
```
-**IMPORTANT: `jj diff` output format**: The default `jj diff` output uses a
-side-by-side line number format (e.g. `26 26:`) that looks very different from
-git's `+`/`-` prefix format. This is **normal and correct** — it is NOT
-corrupted or showing stale content. However, to avoid confusion,
-**always use `jj diff --git`** to get standard unified diff format with `+`/`-`
-lines.
+`jj show` takes several revisions at once, and `--reversed` shows a commit
+against its parent.
+
+About the `jj diff` format: the default output is a side-by-side line-number
+layout (`26 26:`) that looks nothing like git's `+`/`-` prefixes. It is
+correct, not stale or corrupt. Use `jj diff --git` for a standard unified diff.
-### Moving Between Commits
+### Moving between commits
```bash
-# Create a new empty commit on top of current
jj new
-
-# Create new commit with message
jj new -m "Commit message"
-
-# Edit an existing commit (working copy becomes that commit)
jj edit <change-id>
-
-# Edit the previous commit
jj prev -e
-
-# Edit the next commit
jj next -e
```
-## Refining Commits
+## Refining commits
-### Squashing Changes
-
-Move changes from current commit into its parent:
+### Squashing changes
```bash
-# Squash all changes into parent
jj squash
```
-**Note**: `jj squash -i` opens an interactive UI and will hang in agent
-environments. Avoid it.
-
-### Splitting Commits
+`jj squash -i` opens an interactive UI and hangs in an agent environment.
-**Warning**: `jj split` is interactive and will hang in agent environments. To
-divide a commit, use `jj restore` to move changes out, then create separate
-commits manually.
+### Splitting commits
-### Absorbing Changes
+`jj split` is interactive and hangs in an agent environment. To divide a commit,
+use `jj restore` to move changes out and create the separate commits by hand.
-Automatically distribute changes to the commits that last modified those lines:
+### Absorbing changes
```bash
-# Absorb working copy changes into appropriate ancestor commits
jj absorb
```
-### Abandoning Commits
+Distributes working-copy changes to the commits that last touched those lines.
+`jj absorb -i` and `--tool` are interactive; skip them in agent runs.
-Remove a commit entirely (descendants are rebased to its parent):
+### Abandoning commits
```bash
jj abandon <change-id>
```
-### Undoing Operations
+Descendants are rebased onto the abandoned commit's parent.
-Reverse the last jj operation:
+### Undoing operations
```bash
jj undo
+jj redo
```
-This reverts the repository to its state before the previous command. Useful
-for recovering from mistakes like accidental `abandon`, `squash`, or `rebase`.
+These reverse or reapply the last operation, which recovers from a mistaken
+`abandon`, `squash`, or `rebase`. They refuse to touch an operation from another
+workspace unless you pass `--allow-cross-workspace`. To revert one specific
+operation, use `jj op revert`; the old `jj op undo` was removed.
-### Rebasing Commits
-
-Move commits to a different parent:
+### Rebasing commits
```bash
-# Rebase current branch onto a destination
jj rebase -d <destination>
-
-# Rebase a specific revision (without descendants) onto a destination
jj rebase -r <change-id> -d <destination>
-
-# Rebase a revision and all its descendants
jj rebase -s <change-id> -d <destination>
-
-# Rebase onto trunk (common: update your branch to latest main)
jj rebase -d main
```
-### Restoring Files
-
-Discard changes to specific files or restore files from another revision:
+### Restoring files
```bash
-# Discard all uncommitted changes in working copy (restore from parent)
jj restore
-
-# Discard changes to specific files
jj restore path/to/file.txt
-
-# Restore files from a specific revision
jj restore --from <change-id> path/to/file.txt
```
-## Working with Bookmarks (Branches)
+## Working with bookmarks
-Bookmarks are jj's equivalent to git branches:
+Bookmarks are jj's branches.
```bash
-# Create a bookmark at current commit
-jj bookmark create my-feature -r@
-
-# Move bookmark to a different commit
+jj bookmark create my-feature -r @
jj bookmark move my-feature --to <change-id>
-
-# List bookmarks
jj --no-pager bookmark list
-
-# Delete a bookmark
jj bookmark delete my-feature
```
-## Workspaces
+Bookmarks don't advance when you add commits, so move them by hand before
+pushing. `jj bookmark advance [names]` moves bookmarks forward to a target in
+one step, with `--to` defaulting to `@`.
+
+`jj bookmark track` and `untrack` take `--remote`. Without it, every remote
+bookmark with a matching name is tracked or untracked.
+
+## Working with tags
+
+Tags use the same tracking model as bookmarks: fetched tags are tracked by
+default, and tracked tags are pushed by default.
+
+```bash
+jj --no-pager tag list
+jj tag track <name>
+jj tag untrack <name>
+```
+
+`jj tag list` supports `--sort` and can be filtered by revset.
-A **workspace** is a working copy plus its associated repo. One repo can have
-multiple workspaces — each with its own working directory and working-copy
-commit (`@`) — all sharing the same commits, operations, and bookmarks. This is
-jj's equivalent of `git worktree`.
+## Workspaces
-Useful for running a long build or test in one workspace while editing in
-another. Workspaces are a rarely-needed feature; consult the
+A workspace is a working copy plus its repo. One repo can hold several
+workspaces, each with its own working directory and `@`, all sharing the same
+commits, operations, and bookmarks. It is jj's version of `git worktree`, handy
+for running a long build or test while editing elsewhere. Most tasks don't need
+one; see the
[official docs](https://docs.jj-vcs.dev/latest/working-copy/#workspaces) for
-anything beyond the basics below.
+anything beyond the basics.
### Common commands
```bash
-# Create a new workspace (defaults: name = basename of path, parent = current @'s parent)
jj workspace add ../my-tests
-jj workspace add --name tests -r <change-id> ../my-tests # explicit name and base
-
-# Inspect
+jj workspace add --name tests -r <change-id> ../my-tests
jj --no-pager workspace list
jj workspace root [--name <ws>]
-
-# Remove (does NOT delete files on disk — rm the directory separately)
-jj workspace forget [<ws>]
-
-# Rename current workspace
jj workspace rename <new-name>
+jj workspace forget [<ws>]
+jj workspace remove [<ws>]
```
+`forget` leaves the directory on disk; `remove` deletes it too, after
+snapshotting the working copy into a commit. `add` links by relative path, so a
+workspace survives being moved together with its parent or run inside a
+container. `list` shows each workspace root by default and no longer hides
+unreachable paths.
+
In `jj log`, each workspace's `@` appears as `<workspace-name>@`.
+### Colocation
+
+`jj workspace add` can create a Git worktree alongside the workspace with
+`--colocate`/`--no-colocate`. `jj git colocation status`, `enable`, and
+`disable` report or toggle this, and they work on child workspaces.
+
### Key semantics
-- **Isolation by default.** `jj workspace add` gives the new workspace its own
- fresh empty commit; workspaces don't start out sharing `@`, and on-disk files
- are never live-mirrored between them.
-- **Propagation at command boundaries.** Each jj command snapshots the current
- workspace's files and reads the op log, so it sees commits/bookmarks made by
- other workspaces. There is no filesystem watcher.
-- **Stale working copy.** If another workspace rewrites this workspace's `@`
- (e.g. via `jj squash`, `rebase`, `abandon`), jj refuses commands here until
- you run `jj workspace update-stale`. Same recovery path if a command was
- interrupted mid-update.
-- **Shared `@` is sharp-edged.** `jj edit <id>` lets two workspaces point at the
- same change without warning. When one mutates it, the other goes stale; if the
- stale one had un-snapshotted edits, `update-stale` preserves them as a
- **divergent commit** (same change ID, shown as `xyz??` in `jj log`) that you
- must resolve. Avoid sharing `@` unless both workspaces are read-only.
+- Isolation by default. A new workspace gets its own fresh empty commit.
+ Workspaces don't start out sharing `@`, and files are never live-mirrored
+ between them.
+- Propagation at command boundaries. Each command snapshots its own files and
+ reads the op log, so it sees commits and bookmarks from other workspaces.
+ There is no filesystem watcher.
+- Stale working copy. If another workspace rewrites this workspace's `@` (via
+ `squash`, `rebase`, or `abandon`), jj refuses commands here until you run
+ `jj workspace update-stale`. The same recovery applies after an interrupted
+ command.
+- Shared `@` is sharp-edged. `jj edit <id>` lets two workspaces point at the
+ same change without warning. When one mutates it, the other goes stale; if
+ the stale one had unsnapshotted edits, `update-stale` keeps them as a
+ divergent commit (same change ID, shown as `xyz??` in `jj log`). Resolve the
+ divergence with `jj converge`, or select one version with the `xyz/0`,
+ `xyz/1` syntax. Avoid sharing `@` unless both workspaces are read-only.
### Agent guidance
-- Always pass `--no-pager` to `jj workspace list`.
-- Don't `jj edit` a change another workspace already has as its `@` — main cause
- of accidental divergence.
+- Pass `--no-pager` to `jj workspace list`.
+- Don't `jj edit` a change that another workspace already has as `@`. That is
+ the usual cause of accidental divergence.
- Don't `rm -rf` a workspace directory without also running
`jj workspace forget <name>`.
-## Git Integration
+## Git integration
-### Working with Existing Git Repos
+### Working with existing Git repos
```bash
-# Clone a git repository
jj git clone <url>
-
-# Initialize jj in an existing git repo
jj git init --colocate
```
-### Fetching Remote Changes
+`jj git init --colocate` refuses to run inside a Git worktree and explains the
+alternatives.
+
+### Fetching remote changes
```bash
-# Fetch all branches from the default remote
jj git fetch
-
-# Fetch from a specific remote
jj git fetch --remote <remote-name>
-
-# Fetch specific branches
jj git fetch -b <branch-name>
```
-After fetching, rebase your work onto the updated trunk: `jj rebase -d main`
-
-### Switching Between jj and git (Colocated Repos Only)
+`jj git fetch` reports abandoned commits and forwards git's stderr, so
+authentication prompts still reach you. It rebases local descendants onto
+rewritten parents when the remote preserves change IDs. Tags arrive as
+`<name>@<remote>` and are tracked by local tags of the same name. After
+fetching, rebase your work: `jj rebase -d main`.
-**This section only applies to colocated repos** (where both `.jj/` and `.git/`
-exist). In non-colocated repos, do not use git commands — they will corrupt jj
-state.
+### Switching between jj and git (colocated repos only)
-In a colocated repository, you can use both jj and git commands with care:
+This applies only to colocated repos, where `.jj/` and `.git/` both exist. In a
+non-colocated repo, do not run git commands; they corrupt jj state.
-**Switching to git mode** (e.g., for merge workflows):
+Switch to git for a merge workflow:
```bash
-# First, ensure your jj working copy is clean
jj st
-
-# Then checkout a branch with git
git checkout <branch-name>
```
-**Switching back to jj mode**:
+Switch back:
```bash
-# Use jj edit to resume working with jj
jj edit <change-id>
```
-**Important notes:**
-
-- Git may complain about uncommitted changes if jj's working copy differs from
- the git HEAD
-- ALWAYS ensure your work is committed in jj before switching to git
-- After git operations, jj will detect and incorporate the changes on next
- command
+Git may complain about uncommitted changes when jj's working copy differs from
+the git HEAD, so commit your jj work first. jj picks up git's changes on the
+next command.
-### Pushing Changes
-
-When the user asks you to push changes:
+### Pushing changes
```bash
-# Push a specific bookmark to the remote
jj git push -b <bookmark-name>
-
-# Example: push the main bookmark
jj git push -b main
```
-**Before pushing, ensure:**
-
-1. Your bookmark points to the correct commit (bookmarks don't auto-advance like
- git branches)
-2. The commits are refined and atomic
-3. The user has explicitly requested the push
-
-**IMPORTANT**: Unlike git branches, jj bookmarks do not automatically move when
-you create new commits. You must manually update them before pushing:
+Before pushing, confirm the bookmark points at the right commit (bookmarks
+don't auto-advance), the commits are refined and atomic, and the user asked for
+the push.
```bash
-# Move an existing bookmark to the current commit
jj bookmark move my-feature --to @
-
-# Then push it
jj git push -b my-feature
```
-If no bookmark exists for your changes, create one first:
+If no bookmark exists, create one first:
```bash
-# Create a bookmark at the current commit
jj bookmark create my-feature
-
-# Then push it
jj git push -b my-feature
```
-## Handling Conflicts
+`jj git push` can push to several remotes at once with repeated `--remote`,
+allow commits that contain conflicts with `--allow-conflicts`, forward push
+options with `-o`, and auto-track a bookmark given to `--bookmark`. The default
+push targets come from `revsets.git-push`.
+
+## Handling conflicts
-jj allows committing conflicts — you can resolve them later:
+jj lets you commit conflicts and resolve them later.
```bash
-# View conflicts
jj st
```
-**Agent conflict resolution**: Do not use `jj resolve` (interactive). Instead,
-edit the conflicted files directly to remove conflict markers, then run `jj st`
-to verify resolution.
+Resolve conflicts by editing the files to remove the markers, then run `jj st`.
+Don't use `jj resolve`; it is interactive.
+
+## Running commands over revisions
+
+`jj run` runs a command over a set of revisions, each in an isolated working
+copy, then amends each revision with the result:
+
+```bash
+jj run -- cargo fix
+jj run -j 4 -- cargo test
+jj run --ignore-changes -- cargo clippy
+```
+
+It processes revisions oldest to newest by default. `--passthrough` connects the
+subprocess to the terminal instead of capturing output, `--ignore-changes` runs
+read-only without rewriting commits, and `--ignore-errors` continues after a
+failing command.
+
+## Resolving divergence
+
+`jj converge` combines the divergent revisions of a change into one
+replacement, rebasing descendants and moving bookmarks onto it. It applies
+heuristics and prompts when they don't agree. Pass `--no-interactive` to print a
+warning and exit instead of prompting, which is what an agent should do.
+
+## Inspecting files and other utilities
+
+```bash
+jj file search -p <pattern> # grep-like; prints each match with its path
+jj file search -p <pattern> -n # add line numbers; --name-only lists files
+jj file delete -r <rev> <path> # delete files in any revision
+jj file show -r <rev> <path> # print a file from a revision
+jj file annotate <path> # blame
+jj util snapshot # snapshot the working copy
+jj util diff <a> <b> # compare two files on disk
+jj util backend name # show the commit backend
+jj util gc # prune unreachable data in the store
+jj config gc # drop config for deleted repos
+jj bisect run --range <revs> <cmd> # find the first bad revision
+```
+
+`jj file search` matches by regex unless the pattern sets another kind, and it
+requires `-p`. `jj file edit` edits a file in a revision but opens your editor,
+so it is not for agent runs. `jj bisect run` checks its endpoints first; pass
+`--trust-endpoints` to skip the checks.
-## Preserving Commit Quality
+## Preserving commit quality
-**IMPORTANT**: Because commits are mutable, always refine them before
-considering work done:
+Because commits are mutable, refine them before you call the work done.
-1. **Review your commit**: `jj --no-pager show @` or `jj --no-pager diff --git`
-2. **Is it atomic?** One logical change per commit
-3. **Is the message clear?** Use imperative verb phrase in sentence case format
- with no full stop: e.g. "Add login endpoint", "Fix null pointer in payment
- processor", "Remove deprecated API endpoints"
-4. **Are there unrelated changes?** Use `jj restore` to move changes out, then
- create separate commits
-5. **Should changes be elsewhere?** Use `jj squash` or `jj absorb`
+1. Review: `jj --no-pager show @` or `jj --no-pager diff --git`.
+2. Keep one logical change per commit.
+3. Write the message as an imperative phrase in sentence case with no full
+ stop: "Add login endpoint", "Fix null pointer in payment processor".
+4. Move unrelated changes out with `jj restore`, then make separate commits.
+5. Put changes in the right commit with `jj squash` or `jj absorb`.
-## Quick Reference
+## Quick reference
| Action | Command |
|--------|---------|
@@ -450,7 +454,7 @@ considering work done:
| View status | `jj st` |
| View log | `jj --no-pager log` |
| View diff | `jj --no-pager diff --git` |
-| New commit | `jj new -m "message"` (use `jj st` first; skip if `@` is empty) |
+| New commit | `jj new -m "message"` |
| Edit commit | `jj edit <id>` |
| Squash to parent | `jj squash` |
| Auto-distribute | `jj absorb` |
@@ -458,18 +462,24 @@ considering work done:
| Abandon commit | `jj abandon <id>` |
| Undo last operation | `jj undo` |
| Restore files | `jj restore [paths]` |
+| Search files | `jj file search -p <pattern>` |
+| Run over revisions | `jj run -- <cmd>` |
+| Resolve divergence | `jj converge --no-interactive` |
| Create bookmark | `jj bookmark create <name>` |
+| Advance bookmark | `jj bookmark advance <name>` |
+| Track tag | `jj tag track <name>` |
| Fetch remote | `jj git fetch` |
| Push bookmark | `jj git push -b <name>` |
| Add workspace | `jj workspace add <path>` |
| List workspaces | `jj --no-pager workspace list` |
+| Remove workspace | `jj workspace remove [name]` |
| Forget workspace | `jj workspace forget [name]` |
| Fix stale working copy | `jj workspace update-stale` |
-## Best Practices Summary
+## Best practices summary
-1. **Describe first**: Set the commit message before coding
-2. **One change per commit**: Keep commits atomic and focused
-3. **Use change IDs**: They're stable across rewrites
-4. **Refine commits**: Leverage mutability for clean history
-5. **Embrace the workflow**: No staging area, no stashing - just commit
+1. Describe first: set the message before coding.
+2. One change per commit.
+3. Use change IDs; they survive rewrites.
+4. Refine commits with `squash` and `absorb`.
+5. No staging area, no stashing.