From jj
Guides non-interactive Jujutsu (jj) workflows for version control: creating/describing changes, viewing status/logs, squashing, restoring files, and managing repositories/commits.
How this skill is triggered — by the user, by Claude, or both
Slash command
/jj:jj-workflowThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Jujutsu is a modern version control system that provides a simpler mental model than Git while remaining Git-compatible. This skill covers the core concepts and workflow commands.
Jujutsu is a modern version control system that provides a simpler mental model than Git while remaining Git-compatible. This skill covers the core concepts and workflow commands.
Always use -m to prevent jj from opening an editor:
# WRONG — opens editor, blocks AI
jj new
jj describe
jj squash
# CORRECT — non-interactive
jj new -m "message"
jj describe -m "message"
jj squash -m "message"
jj split is inherently interactive — no non-interactive mode exists. Use jj restore --from @- <path> to remove a file from the current change instead.
kkmpptxz). Changes can be modified.@) is always a change that can be modified freely.@ and represents your current state.@- refers to the parent of the working copy.| Situation | Do This |
|---|---|
| Starting new work | jj new -m "what I'm trying" |
| Forgot to start with jj new | jj describe -r @ -m "type: what I'm doing" (do this immediately) |
| Change has no description | Run the Description Check Protocol (see below) |
jj git push rejected with "no description set" | jj describe -r <change> -m "type: message" |
| Work is done, move on | jj new -m "next task" |
| Annotate what you did | jj describe -m "feat: auth" |
| Broke something | jj op log → jj op restore <id> |
| Undo one file | jj restore --from @- <path> |
| Exclude file from current change | jj restore --from @- <path> |
| Stop tracking an ignored file | Add ignore rule first, then jj file untrack <path> |
| Stop tracking ignored directory contents | Add ignore rule first, then jj file untrack 'glob:dir/**' |
| Combine messy commits | jj squash -m "combined message" |
| Try something risky | jj new -m "experiment", then jj abandon @ if it fails |
CRITICAL: Jujutsu commands require GPG signing and SSH/GitHub authentication. Always request elevated permissions when running jj or gh commands:
required_permissions: ["all"]
Never run jj commands in the default sandbox—they will fail due to authentication requirements.
jj status # Show working copy status
jj log # View commit history
jj diff # Show uncommitted changes
jj show @ # Show current change details
jj new # Create a new empty change on top of @
jj describe -r @ -m "feat: message" # Set commit message for current change (always use -r to target explicitly)
jj new -m "feat: message" # Create new change with message
jj tug # Fetch updates and rebase current change onto latest remote
jj git fetch # Fetch from remote without rebasing
jj git push # Push current changes to remote (requires description on the change)
jj git push --bookmark <name> # Push AND set up remote tracking (like git push -u origin <branch>)
jj git push will reject changes without descriptions ("no description set"). Always run the Description Check Protocol before pushing.
jj git push --bookmark <name> is the unified push+track command — it creates the bookmark on the remote and sets up tracking in one step.
jj squash # Squash current change into parent
jj squash --into @- # Explicitly squash into parent
jj restore --from @- <path> # Remove a file from current change (non-interactive alternative to split)
jj file untrack <path> # Stop tracking a path that is already ignored
jj edit <change-id> # Edit an existing change
Use jj file untrack when a file should stay on disk locally but stop being tracked by jj. This is an ignore-driven workflow: add the path to .gitignore first, then untrack it.
jj file untrack only works for paths that are already ignored, usually via .gitignore or .git/info/exclude in colocated workspaces.
# 1. Add an ignore rule first
echo "local-config.json" >> .gitignore
# 2. Stop tracking the ignored file
jj file untrack local-config.json
# Untrack ignored directory contents with a fileset glob
jj file untrack 'glob:.agents/**'
Use jj restore --from @- <path> when you only want to remove a path from the current change. Use jj file untrack <path> when you want jj to stop tracking an ignored path going forward.
jj bookmark create <name> -r @ # Create a bookmark at @
jj bookmark set <name> -r @ # Move bookmark to @
jj bookmark list # List all bookmarks
Workspaces are jj's equivalent of git worktrees — multiple working directories backed by the same repository. Each workspace has its own @ (working copy), enabling true parallel development without branch locking.
jj workspace add <path> # Create new workspace at path (e.g., ../myproject-fix)
jj workspace list # List all workspaces with their @ revisions
jj workspace forget <name> # Stop tracking a workspace (run from another workspace)
jj workspace root # Print the root path of current workspace
jj workspace update-stale # Fix a stale working copy after concurrent changes
Key differences from git worktrees:
@ change automatically on creation@Use Conventional Commits format:
feat: — New featurefix: — Bug fixrefactor: — Code change that neither fixes a bug nor adds a featureperf: — Performance improvementdocs: — Documentation changeschore: — Maintenance taskstest: — Adding or updating testsExamples:
jj describe -r @ -m "feat: add user authentication"
jj describe -r @ -m "fix: resolve null pointer in parser"
jj describe -r @ -m "refactor: extract validation logic"
Always run this protocol before pushing or creating a PR. jj git push rejects changes without descriptions. This is a hard requirement, not a quality preference.
# Check the first line (used as PR title)
jj log -r <change> -T description --no-graph | head -1
# If first line is blank, check for an existing body
jj log -r <change> -T description --no-graph | tail -n +2
Gate logic:
jj describe --stdin (see Step 5). Never use -m in this case — it would replace the body.Default to @ (working copy), but verify with jj status first:
@ has modified files, use @.@ has no modified files (fresh "next task" placeholder), check @- before using it:# Check if @- has actual work
jj diff -r @-
# Check @- bookmarks for trunk markers
jj log -r @- -T 'bookmarks' --no-graph
Safety gate — stop and ask the user if ANY of these are true:
@- has an empty diff (nothing to describe)@- carries a trunk bookmark (main, master, or any bookmark with @origin suffix like main@origin)@- has no bookmark AND no diff (ambiguous — could be a bare trunk ancestor)Only proceed with @- when it has a non-empty diff AND no trunk/remote bookmarks. If @- carries main because work was rebased onto it during conflict resolution, it will have a non-empty diff — but still ask the user to confirm before mutating a revision that holds a trunk bookmark.
jj diff -r <change>
Focus on the high-level nature of changes: file paths, new vs modified files, and the overall purpose. If the diff is empty, warn that there are no changes to describe — do not generate a description.
Map the diff content to a type:
| Type | Signals in the diff |
|---|---|
feat: | New files, new functions/exports, new API routes, new components, new public methods |
fix: | Bug fixes, error handling additions, null/type guards, boundary condition checks |
refactor: | Code moves/renames, extractions, restructuring with no new behavior |
perf: | Caching, async/parallel, lazy loading, memoization, reduced allocations |
docs: | Only documentation files changed (README, markdown, comments, JSDoc) |
chore: | Dependencies, config files, CI/CD, build tooling, formatting, lint rules |
test: | Only test files changed (*_test.*, *.spec.*, __tests__/, test fixtures) |
Priority when multiple types apply: if only docs changed → docs:; if only tests changed → test:; otherwise use the first matching type from: feat > fix > perf > refactor > chore.
Case A: No existing description (most common)
jj describe -r <change> -m "type: concise imperative description"
Case B: Blank first line but existing body (from Step 1 gate)
Use --stdin to prepend the title while preserving the body:
printf 'type: concise title\n\n' | cat - <(jj log -r <change> -T description --no-graph | tail -n +2) | jj describe -r <change> --stdin
Never use -m for this case — it would replace the entire description and lose the body.
Rules for the description:
--stdin) to prepend a title while preserving the existing body. Never use -m.@- with trunk bookmark: If @- carries main, master, or a remote-tracking bookmark (e.g., main@origin), stop and ask the user which change to target. See Step 2 safety gate.# 1. Check current description
jj log -r @ -T description --no-graph
# 2. If empty, check which change has the work
jj status
# 3. Analyze the diff (use @- if @ is a placeholder)
jj diff -r @
# 4. Determine type from the diff, then set the description
jj describe -r @ -m "type: concise description"
jj tug # Sync with remote
jj new -m "feat: new feature" # Start new change
# ... make changes ...
# Before pushing: run the Description Check Protocol if @- has no description
jj describe -r @- -m "feat: new feature" # Ensure description is set
jj new # Finalize and start next
Simply make changes—they're automatically included in @. Use jj describe -r @ -m "type: message" to update the message if needed. If the change has no description yet, run the Description Check Protocol.
jj tug # Fetches and rebases in one command
# Run the Description Check Protocol first — jj git push requires descriptions
jj log -r 'remote_bookmarks()..@' # Changes not yet on remote
The operation log records every operation. Nothing is lost.
jj op log # See all operations
jj undo # Undo last operation
jj op restore <id> # Jump to any past state
# Create a new workspace for parallel work
jj workspace add ../myproject-hotfix
cd ../myproject-hotfix
jj new -m "fix: critical hotfix" # New @ in the new workspace
# ... make changes, push, etc. ...
# Back in original workspace — unaffected
cd ../myproject
jj log # Other workspace's changes appear in shared history
# Clean up when done
jj workspace forget hotfix # Run from any other workspace
rm -rf ../myproject-hotfix # Remove the directory
Use cases:
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub hotthoughts/jj-skills --plugin jj