| name | my-jj-workflow |
| description | **ALWAYS use when user mentions:** "jj", "jujutsu", OR implies VCS without naming
a tool ("commit", "status", "diff", "push") when user hasn't specified git.
**Assumption:** "commit" without "git" prefix means jj, NOT git.
**DO NOT use for:** releases/semver (@skills/my-semantic-release) or when user
explicitly prefers git.
**For all jj commands, see @skills/jujutsu**
|
My JJ Workflow
Agent-optimized guide for jujutsu (jj). For full command reference, see @skills/jujutsu.
ā” Agent Quick Start
Before any jj operation, verify:
- ā
This is a jj repo (
ls .jj or jj st succeeds)
- ā
CRITICAL: Always use
-m flag for commits (never rely on editor in non-interactive mode)
- ā
Never use
jj split (use patterns below)
- ā
After
jj commit, run jj new to continue
- ā
Use
jj edit, not git checkout
ā ļø NON-INTERACTIVE SAFETY: In scripts/agents, jj commit without -m will abort or hang. Always use:
jj commit -m "message"
jj describe -m "message"
jj new -m "message"
If stuck: EDITOR=cat timeout 10 jj <command> (recovery only)
Prerequisites
- jj ā Jujutsu VCS (install:
cargo install jj or package manager)
- git ā For push/pull to GitHub/GitLab (jj uses git as backend)
- Shell ā Any POSIX-compatible shell (bash, zsh, fish)
- Optional: just ā For pre-commit check examples
Decision Guide: User Says ā You Do
| User says | Your exact steps |
|---|
| "commit this" | 1. jj st ā 2. If no description: jj describe -m "..." ā 3. jj commit -m "..." (ā ļø -m required!) ā 4. jj new -m "wip: next" |
| "what changed?" | jj st then jj diff |
| "check status" | jj st |
| "split this" / "break into commits" | 1. jj diff --summary ā 2. ask_user how to split ā 3. Use Split Workflow below |
| "new branch" / "create branch" | Explain: "jj uses changes, not branches. Use: jj new main -m \"feat: ...\"" |
| "checkout X" | jj edit X (X = bookmark name, change ID, or @--) |
| "go back" / "undo" | jj undo (undoes last jj operation) |
| "push to github" | git push or jj git push --bookmark main |
| "pull from github" | jj git fetch then jj rebase -d main@origin |
| "worktree" / "new worktree" | Explain: "jj uses workspaces: jj workspace add ../ws" |
| "squash/fixup" | jj squash -m "final message" (into parent) or jj squash --from @ --into X -m "..." |
| "amend last commit" | jj edit @- ā make edits ā jj squash OR jj describe -m "new message" if just changing message |
| "see history" | jj log --limit 10 |
| "diff against main" | jj diff --from main --to @ |
Standard Workflows (Step-by-Step)
Workflow 1: Normal Commit
jj st
just check 2>/dev/null || npm test 2>/dev/null || echo "No checks configured"
jj describe -m "feat(scope): what this does"
jj commit -m "feat(scope): what this does"
jj new -m "wip: next task"
Workflow 2: Describe-First (Preferred)
Start with intent, evolve message, commit when ready:
jj new main -m "wip: auth flow"
jj describe -m "feat(auth): JWT authentication"
jj describe -m "feat(auth): add JWT authentication with refresh tokens"
jj commit -m "feat(auth): add JWT authentication with refresh tokens"
jj new -m "wip: next task"
Workflow 3: Fixup Previous Commit
jj edit @-
jj describe -m "new message"
jj edit @+
jj edit @-
jj squash -m "updated message"
jj edit @+
Workflow 4: Split Workflow
NEVER use jj split ā it's interactive only. Use this instead:
jj diff --summary
jj new @- -m "docs: update readme"
jj edit @-
jj commit -m "feat: add authentication"
jj edit @+
jj commit -m "docs: document authentication"
change_id=$(jj log -r @ -T 'change_id')
jj new @- -m "feat(auth): add auth endpoints"
jj new @-- -m "feat(login): add login page"
jj abandon $change_id
Workflow 5: Context Switch
jj describe -m "wip: feature X on hold"
jj new main -m "feat: urgent fix"
jj bookmark create feature-x -r @
jj new main -m "feat: urgent fix"
jj edit feature-x
Workflow 6: Workspace with Sparse Checkout
jj workspace add --sparse-patterns empty ../feature-ws
cd ../feature-ws
jj sparse set --add src/auth/ --add README.md
jj workspace forget ../feature-ws
Workflow 7: Pre-Push Verification
ALWAYS run this before pushing to CI/CD or releasing:
jj st
just check 2>/dev/null || npm test 2>/dev/null || cargo test 2>/dev/null
just fix 2>/dev/null || npx @biomejs/biome check --write .
jj commit -m "style: fix formatting"
just release
Why this matters: CI failures after tagging require force-pushing the tag, which is messy and risks race conditions with npm registry.
Error Recovery
Problem: Editor Opened (Forgot -m Flag)
WHAT HAPPENED: You ran `jj commit` without `-m`, editor opened
RECOVERY:
1. Cancel editor immediately:
- Vim: Press Escape, type `:q!`, press Enter
- Nano: Press Ctrl+X
2. Re-run with `-m` flag:
jj commit -m "feat: actual message"
PREVENTION: Always use `-m` flag. Never rely on editor.
Problem: Command Stuck/Hanging
WHAT HAPPENED: Command waiting for input (forgot `-m` or interactive prompt)
RECOVERY:
1. Force non-interactive completion:
EDITOR=cat timeout 10 jj <command>
2. Or kill and retry:
Ctrl+C or kill -9 <pid>
Then retry with `-m` flag
WARNING: EDITOR=cat creates empty messages. Fix immediately with:
jj describe -m "proper message" # if not committed
# or edit @- and squash if already committed
Problem: jj commit Aborts in Non-Interactive Mode
WHAT HAPPENED: Ran `jj commit` without `-m` flag in non-interactive mode
SYMPTOM: Command aborts with "Command aborted" or hangs indefinitely
RECOVERY:
1. Check if commit actually happened:
jj log --limit 3
2. If not committed, retry with `-m` flag:
jj commit -m "feat: actual message"
3. If partially committed (change ID changed), check status:
jj st
PREVENTION: ALWAYS use `-m` flag in scripts/automation:
ā
jj commit -m "feat: x"
ā
jj commit --message "feat: x"
ā jj commit # Never use without -m in non-interactive mode
Problem: "No .jj Directory Found"
CAUSE: Not a jj repo yet
SOLUTION:
1. If git repo exists: Run `jj git init --colocate` to set up jj alongside git
2. If new project: jj git init
Problem: "Nothing Changed" After Commit
THIS IS NORMAL. After `jj commit`:
- Your changes were saved to the commit
- Working copy is now "empty" (no files, no description)
NEXT STEP: Always run `jj new` to continue working:
jj new -m "wip: next task"
Problem: Accidentally Used Git Commands
DANGER: git commit, git rebase, git merge, git cherry-pick, git reset --hard
CORRUPT jj history in colocated repos.
IF YOU JUST DID IT (< 1 minute):
jj undo # May recover
IF PUSHED/PERSISTED:
jj op log # See operation history
jj op restore <op-id> # Restore from before git command
# Or manually recreate changes
SAFE git commands (read-only):
git log, git diff, git show, git blame, git grep
Essential Reference
Revset Symbols (Navigation)
| Symbol | Means | Use in |
|---|
@ | Current working copy | jj diff -r @ |
@- | Parent of current | jj edit @- |
@-- | Grandparent | jj log -r @-- |
@+ | Child of current | jj edit @+ |
main | Bookmark (like branch) | jj new main |
abc123 | Change ID | jj edit abc123 |
ā ļø Git syntax that breaks in jj:
- ā
@~1 ā ā
@-
- ā
@^ ā ā
@-
- ā
@~-1 ā ā
@+
Common Commands
| Task | Command |
|---|
| Status | jj st |
| Diff | jj diff |
| Log | jj log --limit 20 |
| New change | jj new main -m "feat: x" |
| Describe | jj describe -m "feat: x" |
| Commit | jj commit -m "feat: x" |
| Squash | jj squash -m "final" |
| Edit change | jj edit @- or jj edit <id> |
| Undo | jj undo |
Timeout Wrapper (For Automation)
jj_with_retry() {
local cmd="$1"
local max_retries=3
local timeout_sec=10
local delay=2
for ((attempt=1; attempt<=max_retries; attempt++)); do
echo "Attempt $attempt/$max_retries: $cmd"
if timeout $timeout_sec bash -c "$cmd"; then
return 0
fi
echo " ā Retrying in ${delay}s..."
sleep $delay
delay=$((delay * 2))
done
return 1
}
Sparse Patterns
Git Interop
| Situation | Tool |
|---|
| Daily work | jj |
| Push to GitHub | git push or jj git push |
| Clone | git clone ā then jj git init --colocate |
| Submodules | git (jj limited) |
| Complex split | git (jj split is interactive) |
CRITICAL: Never use git commit, git rebase, git merge in colocated jj repos.
š Full command reference: @skills/jujutsu