# Jujutsu

> This document instructs Claude Code to use `jj` (Jujutsu) instead of `git` for version control operations. jj is a Git-compatible VCS that provides a simpler mental model and powerful history editing.

- Skill: `factorial-io/jujutsu` (Agent Skill)
- Install (CLI): `npx skillmds@latest add factorial-io/jujutsu`
- Raw SKILL.md: https://api.skillmd.com/api/skills/factorial-io/jujutsu/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: factorial-io (https://skillmd.com/u/factorial-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/factorial-io/jujutsu

---


# Jujutsu (jj) Usage Guide for Claude Code

Use `jj` (Jujutsu) instead of `git` for version control. jj is Git-compatible
with a simpler model and powerful history editing.

## Delegate noisy work to a subagent

For multi-step VCS work — history cleanup, rebase, conflict resolution,
splitting commits — spawn a subagent (Sonnet) to do it and return a one-line
summary. This keeps verbose `jj log`/`diff`/`op log` output and this reference
out of the parent's context, saving tokens. Tell the subagent exactly what
changed and what the end state should be.

Do NOT delegate trivial one-shots (`jj describe -m … && jj git push`) — spawn
overhead exceeds the saving. Run those inline.

## Mental model

- **No staging area** — every change is part of the working copy commit.
- **Working copy is a commit** — `@` is your current working copy commit.
- **Bookmarks, not branches** — commits are anonymous; name a bookmark only to push.
- **Conflicts are values** — they don't block operations; resolve them later.
- **Automatic rebasing** — descendants rebase automatically when you edit history.

| Git | jj |
| --- | --- |
| `branch` | `bookmark` |
| `HEAD` | `@` (working copy) |
| `checkout` | `edit` / `new` |
| `stash` | not needed — just `jj new` |
| `commit --amend` | just edit `@`, changes auto-apply |

## Command reference

| Action | Command |
| --- | --- |
| Status | `jj st` |
| Log | `jj log` (`jj log -r 'all()'` for everything) |
| Diff working copy / a commit | `jj diff --git` / `jj diff --git -r <rev>` |
| Show a commit | `jj show <rev>` |
| Describe `@` | `jj desc -m "feat: …"` |
| New commit on `@` / on a rev | `jj new [-m "…"]` / `jj new <rev>` |
| Edit an existing commit | `jj edit <rev>` (descendants auto-rebase) |
| Squash into parent | `jj squash -m "…"` (`-r <rev>` for a specific one) |
| Squash paths across commits | `jj squash --from <src> --into <dst> <paths>` |
| Restore paths from a rev | `jj restore --from <rev> <paths>` |
| Split by file | `jj split <path>...` (`--parallel` for siblings) |
| Absorb into ancestors | `jj absorb` (see caveat below) |
| Rebase | `jj rebase -d <dest>` (`-r` one, `-s` +descendants, `-b` branch) |
| Bookmarks | `jj bookmark create\|set\|delete <n>` · `jj bookmark track <n>@origin` |
| Fetch / push | `jj git fetch` / `jj git push -b <n>` |
| Conflicts / undo | `jj log -r 'conflicts()'` / `jj op undo` |

## Workflow

**Atomic commits by construction.** The cheapest path is to create commits as
you go, not to split afterward. Start work with a description in place and run
`jj new` between logical steps:

```
jj new main -m "refactor: extract auth helper"   # start from main
# ...edit...
jj new -m "feat: use helper in login flow"
# ...edit...
```

Adjust later with `jj describe -m "…"`; merge two commits with `jj squash -m "…"`.

**Before push:**

```
jj log -r 'conflicts()'          # 1. no conflicts
jj st                            # 2. clean status
jj git fetch && jj rebase -d main # 3. onto latest main
jj log -r '::@ ~ ::main'         # 4. review your changes
jj bookmark create <n> && jj git push -b <n>  # 5. push
```

**Update a PR after review:** `jj edit <commit>`, make changes (descendants
auto-rebase), `jj new <tip>` to return to the tip, `jj git push -b <n>`.

Bookmark naming: `feat/user-dashboard`, `fix/issue-123-auth-bug`,
`claude/<feature>-<session-id>`.

## Judgment notes

- **`jj absorb`** only moves hunks into ancestor commits that already touch the
  same lines. It won't create commits; split mixed unrelated changes in `@` first.
- **Recovery**: `jj op log` shows every operation; `jj op undo` reverts the last,
  `jj op restore <id>` jumps to a known-good state. Nothing is ever truly lost.
- **Colocate with git**: `jj git init --colocate` shares the `.git` dir, so `jj
  git fetch/push` keep both in sync and git commands still work for edge cases.

## Avoiding interactive editors

LLMs can't drive TUIs, so always use non-interactive forms:

- **Inline messages** — pass `-m "…"` to `describe`, `squash`, `commit`, `new`.
  `jj split` takes `-m "first" -m "second"` for both halves.
- **Squash / restore specific paths** without an editor — the `--from/--into`
  and `--from <rev> <paths>` forms above.
- **Split by file** — `jj split <path>...` opens no editor when boundaries align
  with files. Prefer it over the hunk-level TUI.

**Hunk-level split within one file** (no file boundary to use):

1. *Split before you mess up* — `jj new -m "next thing"` between steps; you never
   need `split`.
2. *Compose* — `jj new`; edit `@` to part A's state; `jj squash --from <original>
   --into @ <paths>`; the original now holds only part B.
3. *Patch round-trip* (last resort, scriptable):
   ```
   jj diff -r <rev> --git > /tmp/full.patch   # hand-split into partial/rest
   jj restore -r <rev> && jj new -r <rev>-
   git apply /tmp/partial.patch && jj commit -m "part A"
   git apply /tmp/rest.patch    && jj commit -m "part B"
   ```

