Make Your Coding Agent Use Jujutsu Instead of Git
Last updated: August 2026
This page provides project-level configuration files for getting Claude Code and Codex CLI to use Jujutsu instead of Git.
Place the files in your project root, and the agent will have access to Jujutsu’s
mental model and core workflows, helping it use jj for day-to-day version
control operations.
The same configuration files are also available in the book’s companion repository.
Why Skills Alone Aren’t Enough
Most of the code in AI coding agents’ training data was managed with Git. As a result, Git workflows are deeply ingrained in their default behavior. Getting an agent to use Jujutsu for version control instead is not as simple as telling it to run different commands.
Several Jujutsu skills are already available. Skills, however, are designed to load knowledge and procedures for a particular task only when they are needed.
“Always use Jujutsu instead of Git during development” is not a task-specific instruction. It is a behavioral constraint that should apply throughout the entire session. Adding a skill alone may therefore not be enough to stop an agent from falling back to its familiar Git workflow halfway through a task.
The agent also needs to understand the differences between the two mental models. Jujutsu has no staging area, while concepts such as changes and bookmarks have no direct equivalent in Git. These differences need to be communicated without consuming unnecessary context in every session.
The configurations provided on this page combine three elements for each agent:
- Always-on behavioral instructions
- Jujutsu’s mental model and common workflows
- Permissions for running version control commands
Claude Code Config
The Claude Code configuration is built around rules that apply throughout every session.
CLAUDE.md contains only the highest-priority instruction: use Jujutsu instead of
Git. More detailed explanations of Jujutsu’s mental model and task-specific
procedures are kept separately in .claude/rules/jujutsu-rules.md.
The package also includes a settings.json file that blocks the agent from
running Git commands while allowing routine Jujutsu commands to run without asking
for permission each time.
.
├─ .claude/
│ ├─ rules/
│ │ └─ jujutsu-rules.md
│ └─ settings.json
└─ CLAUDE.mdCLAUDE.md## Version Control — Required Procedure
This project uses **Jujutsu (jj)** for version control. For the detailed rules, see `.claude/rules/jujutsu-rules.md`.
**Before you begin editing code, always perform the following steps:**
1. Check the current change with `jj log --ignore-working-copy -r @`.
2. If the description is empty and the diff is empty (an `empty` change) → set a description with `jj describe -m "<description>"` and begin work.
3. Otherwise (work already in progress, or already finished) → create a new change with `jj new -m "<description>"`.
4. Write the description in Conventional Commits format.
**Prohibited:** Direct use of `git` commands (the `jj git` subcommands and the `gh` CLI are allowed)..claude/rules/jujutsu-rules.md# Jujutsu (jj) Rules for AI Agents
This project uses **Jujutsu (jj)** for version control. AI agents must strictly follow the rules below, and **using `git` commands is generally prohibited** (except for the `jj git` subcommands and the `gh` CLI).
---
## Important Notes for AI Agents
### Terminology Mapping
The meanings of terms differ between Jujutsu and Git. AI agents must rigorously observe the distinctions below.
| Git term | Jujutsu term |
| ------------------------ | --------------------------------------------- |
| commit (as a noun) | change |
| branch | bookmark |
| staging | (no such concept) |
| unstaged / uncommitted | (no such concept) |
| HEAD | `@` (the working copy) |
| stash | (no such concept; use `jj new` instead) |
| `git add` | (unnecessary; automatic snapshot) |
| `git commit --amend` | (unnecessary; changes to `@` are applied automatically) |
### Fundamental Differences from Git
1. **There is no such thing as an "unsaved change"**: the moment you save a file, it is automatically included in the current change. There is no need to ask, "Do you want to include this change?"
2. **change and revision**:
- **change**: A unit of work. It has a unique, immutable **change ID**, while its contents are mutable.
- **revision**: A snapshot of a change. A new revision is created every time you edit, but the change ID never changes.
3. **Automatic rebase**: When you change a change's parent, its descendant changes are rebased automatically. There is no need to manage a rebase chain by hand.
4. **First-class conflicts**: Even when a conflict occurs, the operation is not interrupted; it is recorded as a change that contains the conflict. You can resolve it later.
5. **Operation log**: Every operation is recorded, and you can return to any point with `jj undo` / `jj op restore`. You may operate without fear of mistakes.
### Rules for diff Output
**When running `jj diff`, `jj show`, or `jj log -p`, always add the `--git` flag.**
Specifying this option makes the diff output use the Git format automatically.
```bash
# Correct
jj diff --git
jj diff --git -r @-
jj diff --git --from main --to @
# Prohibited
jj diff # without --git is prohibited
jj diff -r @- # without --git is prohibited
```
With a Git-format diff, file additions, deletions, renames, and permission changes are all represented accurately, which greatly improves the accuracy with which an AI agent can analyze the diff.
### Suppressing Snapshots in Read-Only Operations
Every time a command runs, jj automatically creates a snapshot of the working copy. If the AI checks state carelessly, there is a risk that operations in another process cause a conflict in the operation log. Therefore, for purely read-only operations performed **while no files have been modified**, add `--ignore-working-copy`.
```bash
# ✅ Read-only: when investigating without modifying any files
jj log --ignore-working-copy
jj log --ignore-working-copy -r 'main..@'
jj diff --git --ignore-working-copy -r @-
jj bookmark list --ignore-working-copy
# ❌ When you must NOT add it: checking state right after modifying files
# (because the latest snapshot needs to be recorded)
jj status # do not add --ignore-working-copy right after a change
jj diff --git # do not add --ignore-working-copy right after a change
# ℹ️ When you only want to take a snapshot
jj util snapshot
```
**Rule of thumb:** If you have just created, edited, or deleted a file, do not add `--ignore-working-copy`. In all other cases—when you are "merely checking a known state"—add it.
### Choosing the Right Log Output
For ordinary state checks, use `jj log`'s graph display as-is. When you need to extract specific information programmatically, make use of `--no-graph` and `--template` (`-T`).
```bash
# Ordinary check (with graph)
jj log --ignore-working-copy
# Extracting specific information (machine-readable format)
jj log --ignore-working-copy --no-graph -T 'change_id.short() ++ " " ++ description.first_line() ++ "\n"'
jj log --ignore-working-copy --no-graph -T 'commit_id.short() ++ " " ++ bookmarks ++ "\n"' -r 'bookmarks()'
```
---
## Basic Workflow
### 1. Checking State and Diffs
```bash
jj status # Check working-copy state (run as-is right after a change)
jj log --ignore-working-copy # Show history as a graph
jj diff --git # Diff of the current working copy (right after a change)
jj diff --git --ignore-working-copy -r @- # Diff of the previous change (read-only)
jj evolog --ignore-working-copy # Evolution history of the current change
jj op log --ignore-working-copy # Show the operation log
```
### 2. Starting Work
Adjust your actions according to the state of the current change (`@`). The decision procedure is as follows:
1. Check the current change with `jj log --ignore-working-copy -r @`.
2. **If the current change is empty** (it has NO description AND NO diff) → **reuse it**. Set its description with `jj describe -m "<description>"` and do your work in this change. **Do NOT create a new change in this case.** Note that a change created by `jj new` is itself empty, so it also falls under this rule.
3. Otherwise (the current change already has a description or a diff) → create a new change with `jj new -m "<description>"`.
4. Write the description in Conventional Commits format.
### 3. Checking for Conflicts After a Modifying Operation
**After any modifying operation such as `jj rebase`, `jj new`, or `jj squash`, always check for conflicts with `jj status`.** Because Jujutsu does not interrupt an operation when a conflict occurs, there is a risk of unknowingly continuing your work.
```bash
# Always run this after a modifying operation
jj status
# If the output contains lines like the following, a conflict exists:
# The change has 2 conflicts:
# src/main.rs 2-sided conflict
```
If you detect a conflict, resolve it before continuing (see the "Resolving Conflicts" section for the procedure).
### 4. Bookmark Operations
Unlike Git branches, bookmarks must be moved manually.
```bash
jj bookmark create <name> -r @ # Create a new bookmark (-r specifies the target revision)
jj bookmark move <name> -t @ # Move an existing bookmark to the current change
jj bookmark list --ignore-working-copy # List bookmarks
jj bookmark delete <name> # Delete a bookmark
```
### 5. Splitting and Restoring Changes
```bash
# Split a change into several (used to fix a change whose scope is too large)
jj split -r <revision>
# Restore a specific file from another revision
jj restore --from <revision> <path>
# Return a change to a previous state (use a past version found via evolog)
jj evolog --ignore-working-copy -r <change-id> # Check past versions
jj restore --from <change-id>/1 --to <change-id> # Restore to the previous state
```
> **Note:** In the `<change-id>/n` notation, `xyz/0` refers to the latest version and `xyz/1` to the previous one. Use it after checking the evolution history with `jj evolog`.
### 6. Amending and Undoing History
- **`jj undo`**: If you make a mistake, use it without hesitation to return to the previous state.
- **`jj op restore <operation-id>`**: Return to a specific operation. You can find the operation ID with `jj op log`.
- **`jj abandon @`**: Discard the current change itself.
### 7. Resolving Conflicts
In Jujutsu, even when a conflict occurs the operation is not interrupted; the change is recorded with conflict markers inserted. AI agents should resolve it using the following procedure.
1. Identify the conflicting files with `jj status`.
2. Open the relevant file and **directly edit the sections containing conflict markers, rewriting them into the correct state**.
Jujutsu's conflict markers use a different format from Git's:
```
<<<<<<<
%%%%%%%
-removed line
+added line
+++++++
content from the other side
>>>>>>>
```
- The `%%%%%%%` block: diff format. It expresses the change from the base with `-`/`+`.
- The `+++++++` block: snapshot format. It shows the other side's content as-is.
3. Save the file. Because `jj` automatically detects the resolution, no operation equivalent to `git add` is needed.
4. Confirm with `jj status` that the conflict is gone.
### 8. Syncing with the Remote
```bash
jj git fetch # Fetch the latest state from the remote
jj git push -b <bookmark-name> # Push the bookmark to the remote
```
---
## Revision Specification Syntax (revset)
| Syntax | Meaning |
| --------------------- | ---------------------------------------------------- |
| `@` | The current change |
| `@-` | The previous change |
| `@--` | The change two before |
| `<bookmark>` | Specify by bookmark name |
| `<bookmark>@origin` | A remote bookmark |
| `main..@` | The set of all changes from `main` to the current change |
| `empty()` | Changes with empty contents |
| `<change-id>/n` | The version n generations back of a change (0 is the latest) |
---
## Handy revset Patterns
| Pattern | Purpose |
| ------------------------------------------- | -------------------------------------- |
| `trunk()..@` | The entire stack from main to the current change |
| `mine() & mutable() & ~empty()` | List of your in-progress changes |
| `conflict()` | Changes that have conflicts |
| `bookmarks() & ~remote_bookmarks()` | Bookmarks not yet pushed |
```bash
# Examples
jj log --ignore-working-copy -r 'trunk()..@'
jj log --ignore-working-copy -r 'conflict()'
```
---
## PR Creation Workflow
### Basic Rules
1. **The target is always `main`** — Unless instructed otherwise, the target branch for a PR is `main`. No confirmation needed.
2. **No squashing** — Do not combine multiple changes into one. To preserve the traceability of the change history, keep each change as-is.
3. **Things that need no confirmation** — You do not need to ask the user about the following every time:
- "Should I include this change?" → It is always included.
- "Should I merge into main?" → main, unless instructed otherwise.
- "Should I squash?" → No.
4. **The PR title follows Conventional Commits** — The PR title must follow the Conventional Commits format (e.g. `feat: ...`, `fix: ...`). Always pass it explicitly with `--title` rather than relying on the title auto-derived by `gh`.
### PR Creation Steps
```bash
# 1. Fetch the latest state from the remote
jj git fetch
# 2. Check the current state
jj log --ignore-working-copy
# 3. Confirm that the bookmark is set (including the remote)
jj bookmark list --all --ignore-working-copy
# 4. If the bookmark is untracked, start tracking it
jj bookmark track <bookmark-name>@origin
# 5. Push to the remote (only if not yet pushed or if there are updates)
# Judging from jj log or bookmark list --all, if the local and remote bookmarks
# point to the same revision, no re-push is needed
jj git push -b <bookmark-name>
# 6. Create the PR with the GitHub CLI.
# The PR title MUST follow Conventional Commits (e.g. "feat: ...", "fix: ...").
# Do not rely on the auto-derived title; pass it explicitly with --title,
# derived from the description of the lead change of the bookmark.
gh pr create --base main --head <bookmark-name> --title "<type>: <summary>" --body "<body>"
```
---
## Troubleshooting
### "The working copy is stale" Error
This occurs when a human and an AI are working in parallel in the same repository, or when an external tool has modified files. If you see this error, run the following to resync.
```bash
jj workspace update-stale
```
Afterward, confirm with `jj status` that the working copy is in a normal state.
### "Commit XXXX is immutable" Error
If this error appears when you run `describe` or `squash`, the target of the operation is among the protected revisions (`main@origin` and its ancestors). Check that the revision specified with the `-r` option is correct, and redo the operation against a mutable change.
---
## Points to Note
1. **Automatic saving**: jj automatically tracks changes in the working directory. No explicit `add` is needed.
2. **Immutable history**: By default, `trunk()` and its ancestors are immutable. Local mutable changes can be edited freely.
3. **Handling conflicts**: jj can record changes that contain conflicts. You can resolve them later, but **always check with `jj status` after a modifying operation**.
4. **Git compatibility**: It can coexist with a `.git` directory. Use `jj git push/fetch` to integrate with Git remotes.
5. **Transmitting the change ID**: The change ID is also transmitted to the remote as a Git commit header (`change-id`).
6. **glob patterns**: String patterns in revsets and the like are interpreted as globs by default. For partial matches, use the `substring:` prefix.
7. **Restrictions on git commands**: `git` commands are generally prohibited because they risk corrupting state. The `gh` CLI uses `git` internally, but it is allowed. Replace read-only `git` operations (such as `git log`) with `jj log` as well..claude/settings.json{
"permissions": {
"allow": [
"Bash(jj status:*)",
"Bash(jj log:*)",
"Bash(jj diff:*)",
"Bash(jj show:*)",
"Bash(jj file list:*)",
"Bash(jj root:*)",
"Bash(jj describe:*)",
"Bash(jj new:*)",
"Bash(jj edit:*)",
"Bash(jj restore:*)",
"Bash(jj squash:*)",
"Bash(jj rebase:*)",
"Bash(jj abandon:*)",
"Bash(jj evolog:*)",
"Bash(jj op log:*)",
"Bash(jj operation log:*)",
"Bash(jj undo:*)",
"Bash(jj bookmark list:*)",
"Bash(jj bookmark create:*)",
"Bash(jj bookmark move:*)",
"Bash(jj bookmark advance:*)",
"Bash(jj bookmark set:*)",
"Bash(jj bookmark rename:*)",
"Bash(jj git fetch:*)",
"Bash(jj config get:*)",
"Bash(jj config list:*)"
],
"deny": [
"Bash(git:*)",
"Bash(jj split:*)",
"Bash(jj resolve:*)",
"Bash(jj diffedit:*)",
"Bash(jj arrange:*)"
],
"ask": [
"Bash(jj bookmark delete:*)",
"Bash(jj bookmark forget:*)",
"Bash(jj bookmark track:*)",
"Bash(jj bookmark untrack:*)",
"Bash(jj git push:*)"
]
},
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "jj fix"
}
]
}
]
}
}Codex CLI Config
Codex CLI does not have a direct equivalent to Claude Code’s behavioral rules.
The AGENTS.md file therefore contains the instructions Codex must always follow
and directs it to consult a skill for detailed guidance on using Jujutsu.
Codex also has a feature called rules, but these rules serve a different purpose:
they control whether commands may be executed outside the sandbox. The package
includes a rules file adapted from the permissions defined in Claude Code’s
settings.json.
.
├─ .agents/
│ └─ skills/
│ └─ jujutsu/
│ └─ SKILL.md
├─ .codex/
│ └─ rules/
│ └─ jujutsu.rules
└─ AGENTS.mdAGENTS.md## Jujutsu Operating Rules
This repository uses Jujutsu (`jj`) for version control.
Always work using Jujutsu's concepts and commands rather than Git's.
For detailed procedures, see `.agents/skills/jujutsu/SKILL.md`.
### Critical Rules
- Using raw `git` commands is **generally prohibited**
- Exceptions: `jj git ...` and the `gh` CLI
- Do not run `jj split` / `jj resolve` / `jj diffedit` / `jj arrange` (they are interactive commands)
- Even for read-only purposes, do not use `git log` or similar; use `jj log` and the like instead
- Always pass `--git` to `jj diff` / `jj show` / `jj log -p`
- Immediately after any modifying operation such as `jj rebase` / `jj new` / `jj squash`, always run `jj status` to check for conflicts
- Unless instructed otherwise, the base for a PR is `main`
- Do not squash stacked changes
### Terminology
Think in Jujutsu's terms, not Git's.
- commit (the unit of work) → change
- branch → bookmark
- HEAD → `@` (the working copy)
- There is no concept of staging / unstaged / uncommitted
- `git add` is unnecessary
- `git commit --amend` is unnecessary
- When you need the equivalent of stash, use `jj new` instead
### Principles for Starting Work
**Before you begin editing code, always perform the following steps:**
1. Check the current change with `jj log --ignore-working-copy -r @`.
2. **If the current change is empty** (it has NO description AND NO diff) → **reuse it**. Set its description with `jj describe -m "<description>"` and do your work in this change. **Do NOT create a new change in this case.** Note that a change created by `jj new` is itself empty, so it also falls under this rule. Setting a description with `jj describe` does NOT make the change non-empty; keep working in the same change.
3. Otherwise (the current change already has a diff) → create a new change with `jj new -m "<description>"`.
4. Write the description in Conventional Commits format.
### Basic Inspection Commands
Prefer the following commands as needed:
```bash
jj status
jj log --ignore-working-copy
jj diff --git --ignore-working-copy
jj evolog --ignore-working-copy
jj op log --ignore-working-copy
```
**Note:** Except when the agent is inspecting files it has just modified itself, it is recommended to add `--ignore-working-copy` to purely read-only operations.
## Principles for Conflicts
In Jujutsu, an operation is not interrupted even when a conflict occurs.
For that reason, you must not move on without checking `jj status` after a modifying operation.
If there is a conflict, resolve it by editing the files directly, then verify again with `jj status`.
No operation equivalent to `git add` is needed.
## Principles for Remotes and PRs
- For syncing with remotes, use `jj git fetch` / `jj git push -b <bookmark-name>`
- To create a PR, use `gh pr create --base main --head <bookmark-name> --title "<type>: <summary>" --body "<body>"`. The PR title MUST follow Conventional Commits (e.g. `feat: ...`, `fix: ...`); always pass it explicitly with `--title` rather than relying on the title auto-derived by `gh`.
- Unlike a branch, a bookmark does not move automatically; operate on it explicitly as needed
## Additional Notes
When performing any detailed Jujutsu operation in this project, you must consult the `jujutsu` Skill.
In situations that require a Jujutsu-specific judgment, you must not proceed before consulting the `jujutsu` Skill.
Examples of such cases:
- revset
- Resolving conflicts
- Bookmark operations
- rebase / squash / split / restore / abandon / undo / op restore
- Handling errors such as stale / immutable
- fetch / push / creating PRs.agents/skills/jujutsu/SKILL.md---
name: jujutsu
description: This Skill collects detailed procedures for AI agents to use Jujutsu (`jj`) as the version control system safely and consistently in this project. The standing rules live in `AGENTS.md`; this Skill records the concrete operating procedures and decision criteria.
---
# Jujutsu Operations Skill
## When to Use This Skill
- You want to check the current working state or history
- You want to start a new change
- You want to perform a rebase / squash / split / restore
- You want to resolve a conflict
- You want to create, move, or delete a bookmark
- You want to specify a target with a revset
- You want to create a PR
- You want to handle errors such as stale / immutable
## Prerequisites
This project uses Jujutsu as its VCS.
- Using raw `git` commands is generally prohibited
- The exceptions are `jj git ...` and the `gh` CLI
- Do not use read-only commands like `git log` either; replace them with `jj` commands
## Terminology Mapping
Do not bring Git's terms over as-is.
| Git term | In Jujutsu |
| ---------------------- | --------------------------------------- |
| commit (unit of work) | change |
| branch | bookmark |
| HEAD | `@` (the working copy) |
| staging | No such concept |
| unstaged / uncommitted | No such concept |
| stash | Basically unnecessary; use `jj new` if needed |
| `git add` | Unnecessary |
| `git commit --amend` | Unnecessary |
### Key Concepts
1. The moment you save a file, your edits are part of the current change
2. A change is a unit of work, and a revision is a snapshot of it
3. When you change a parent, descendant changes are rebased automatically
4. A conflict is recorded as a first-class state
5. Every operation is kept in the operation log, so you can go back with `jj undo` or `jj op restore` when needed
## Basic Policy for diff and log
### diff
Always add `--git` to `jj diff` / `jj show` / `jj log -p`.
```bash
jj diff --git
jj diff --git -r @-
jj show --git
jj log -p --git
```
Prohibited examples:
```bash
jj diff
jj diff -r @-
jj show
jj log -p
```
### Suppressing Snapshots in Read-Only Operations
Every time a command runs, jj automatically creates a snapshot of the working copy. If the AI checks state carelessly, there is a risk that operations in another process cause a conflict in the operation log. Therefore, for purely read-only operations performed **while no files have been modified**, add `--ignore-working-copy`.
```bash
# ✅ Read-only: when investigating without modifying any files
jj log --ignore-working-copy
jj log --ignore-working-copy -r 'main..@'
jj diff --git --ignore-working-copy -r @-
jj bookmark list --ignore-working-copy
# ❌ When you must NOT add it: checking state right after modifying files
# (because the latest snapshot needs to be reflected)
jj status # do not add --ignore-working-copy right after a change
jj diff --git # do not add --ignore-working-copy right after a change
# ℹ️ When you only want to take a snapshot
jj util snapshot
```
**Rule of thumb:** If you have just created, edited, or deleted a file, do not add `--ignore-working-copy`. In all other cases—when you are "merely checking a known state"—add it.
### log
For ordinary checks, use `jj log` with the graph.
Use `--no-graph` and `-T` only when you want to extract information programmatically.
```bash
jj log --ignore-working-copy
jj log --ignore-working-copy --no-graph -T 'change_id.short() ++ " " ++ description.first_line() ++ "\n"'
jj log --ignore-working-copy --no-graph -T 'commit_id.short() ++ " " ++ bookmarks ++ "\n"' -r 'bookmarks()'
```
## Basic Workflow
### 1. Checking State
```bash
jj status
jj log --ignore-working-copy
jj diff --git
jj diff --git --ignore-working-copy -r @-
jj evolog --ignore-working-copy
jj op log --ignore-working-copy
```
Uses:
- `jj status`: Check the working-copy state
- `jj log --ignore-working-copy`: Grasp the history and stack structure
- `jj diff --git`: Check the current diff (right after a change)
- `jj diff --git --ignore-working-copy -r @-`: Check the diff of the previous change (read-only)
- `jj evolog --ignore-working-copy`: Check the evolution of the current change
- `jj op log --ignore-working-copy`: Check the operation history
### 2. Starting Work
Look at the state of the current `@` and decide whether to use `describe` or `new`.
Procedure:
1. Check the current change with `jj log --ignore-working-copy -r @`.
2. **If the current change is empty** (it has NO description AND NO diff) → **reuse it**. Set its description with `jj describe -m "<description>"` and work in this change. **Do NOT create a new change.** A change created by `jj new` is itself empty and falls under this rule. Note that running `jj describe` to set a description does NOT make the change non-empty — keep working in the same change.
3. **Otherwise** (the current change already has a diff) → create a new change with `jj new -m "<description>"` and work there.
4. Write the description in Conventional Commits format.
Example:
```bash
jj log --ignore-working-copy -r @
jj describe -m "feat: add search form"
```
Or:
```bash
jj new -m "fix: handle empty input"
```
### 3. Checking for Conflicts After a Modifying Operation
Immediately after a modifying operation such as `jj rebase`, `jj new`, or `jj squash`, always run `jj status`.
```bash
jj rebase -s @ -d main
jj status
```
`jj` does not stop an operation even when a conflict occurs.
So if you move on without looking at `jj status`, you risk continuing to work while still carrying a conflict.
Example of a sign of a conflict:
```text
The change has 2 conflicts:
src/main.rs 2-sided conflict
```
### 4. Bookmark Operations
Unlike Git branches, bookmarks do not move automatically. Operate on them explicitly when needed.
```bash
jj bookmark create <name> -r @
jj bookmark move <name> -t @
jj bookmark list --ignore-working-copy
jj bookmark delete <name>
```
Uses:
- Create a new bookmark
- Move an existing bookmark to the current change
- Check the list
- Delete an unneeded bookmark
### 5. Splitting and Restoring Changes
#### Splitting
When a change grows too large, split it.
```bash
jj split -r <revision>
```
#### Restoring
Use `restore` when you want to recover part of the state from another revision.
```bash
jj restore --from <revision> <path>
```
#### Restoring to a Past Version
You can also look at `evolog` and go back to a past state of the same change.
```bash
jj evolog --ignore-working-copy -r <change-id>
jj restore --from <change-id>/1 --to <change-id>
```
Notes:
- `<change-id>/0` is the latest version
- `<change-id>/1` is the previous version
- Check with `jj evolog` before running it
### 6. Amending and Undoing History
```bash
jj undo
jj op restore <operation-id>
jj abandon @
```
Uses:
- `jj undo`: Undo the most recent operation
- `jj op restore <operation-id>`: Return to any operation point
- `jj abandon @`: Discard the current change
You may operate without fear of mistakes, but when your intent is unclear, check with `jj op log` before going back.
## Resolving Conflicts
In Jujutsu, even when a conflict occurs, the change is recorded in that state.
Resolve it with the following procedure.
1. Identify the conflicting files with `jj status`.
2. Open the file and directly edit the sections containing conflict markers.
3. Arrange the content correctly and save.
4. Confirm with `jj status` that the conflict is gone.
Format of the conflict markers:
```text
<<<<<<<
%%%%%%%
-removed line
+added line
+++++++
content from the other side
>>>>>>>
```
Meaning:
- The `%%%%%%%` block: the diff from the base
- The `+++++++` block: the other side's content itself
Notes:
- No `git add` like in Git is needed
- Once you save, `jj` automatically detects the resolution
## Syncing with the Remote
```bash
jj git fetch
jj git push -b <bookmark-name>
```
- `jj git fetch`: Fetch the latest state from the remote
- `jj git push -b <bookmark-name>`: Push the bookmark
## revset Cheat Sheet
### Basic Syntax
| Syntax | Meaning |
| ------------------- | ---------------------------------------- |
| `@` | The current change |
| `@-` | The previous change |
| `@--` | The change two before |
| `<bookmark>` | Bookmark name |
| `<bookmark>@origin` | A remote bookmark |
| `main..@` | The set of changes from `main` to the current one |
| `empty()` | Empty changes |
| `<change-id>/n` | The version n generations back of the same change |
### Handy Patterns
| Pattern | Purpose |
| ----------------------------------- | ---------------------------------- |
| `trunk()..@` | The entire stack from main to the current change |
| `mine() & mutable() & ~empty()` | List of your in-progress changes |
| `conflict()` | Changes that contain conflicts |
| `bookmarks() & ~remote_bookmarks()` | Bookmarks not yet pushed |
Examples:
```bash
jj log --ignore-working-copy -r 'trunk()..@'
jj log --ignore-working-copy -r 'conflict()'
jj log --ignore-working-copy -r 'mine() & mutable() & ~empty()'
```
## PR Creation Workflow
### Basic Rules
- Unless instructed otherwise, the base for a PR is `main`
- The PR title must follow Conventional Commits (e.g. `feat: ...`, `fix: ...`); always pass it explicitly with `--title` rather than relying on the title auto-derived by `gh`
- Do not squash multiple changes
- You do not need to confirm the following every time
* Whether to include the change → it is always in the current change
* Whether to base it on `main` → `main` unless stated otherwise
* Whether to squash → no
### Steps
```bash
jj git fetch
jj log --ignore-working-copy
jj bookmark list --all --ignore-working-copy
jj bookmark track <bookmark-name>@origin
jj git push -b <bookmark-name>
gh pr create --base main --head <bookmark-name> --title "<type>: <summary>" --body "<body>"
```
Notes:
- First check whether the bookmark exists
- If it is untracked, `track` it
- Judging from `jj log` or `bookmark list --all`, if the local and remote bookmarks point to the same revision, it has already been pushed, so no re-push is needed
## Troubleshooting
### "The working copy is stale"
This happens when a human and an AI touch the same repository in parallel, or when an external tool rewrites files.
Fix:
```bash
jj workspace update-stale
jj status
```
### "Commit XXXX is immutable"
The target of `describe` or `squash` is among immutable revisions, such as `main@origin` or its ancestors.
Fix:
1. Check that the target specified with `-r` is correct.
2. Redo the operation against a mutable change.
3. If needed, check your current position with `jj log`.
## Final Notes
1. `jj` has no concept of staging.
2. Changes in the working directory are tracked automatically.
3. Running `jj status` after a modifying operation is mandatory.
4. It can coexist with `.git`, but perform operations through `jj`.
5. Do not use `git` commands as a general rule, because they risk corrupting state.
6. When in doubt, look at `jj log` / `jj status` / `jj op log` before acting..codex/rules/jujutsu.rules# .codex/rules/jujutsu.rules
# --- allow ---
prefix_rule(pattern = ["jj", "status"], decision = "allow")
prefix_rule(pattern = ["jj", "log"], decision = "allow")
prefix_rule(pattern = ["jj", "diff"], decision = "allow")
prefix_rule(pattern = ["jj", "show"], decision = "allow")
prefix_rule(pattern = ["jj", "file", "list"], decision = "allow")
prefix_rule(pattern = ["jj", "root"], decision = "allow")
prefix_rule(pattern = ["jj", "describe"], decision = "allow")
prefix_rule(pattern = ["jj", "new"], decision = "allow")
prefix_rule(pattern = ["jj", "edit"], decision = "allow")
prefix_rule(pattern = ["jj", "restore"], decision = "allow")
prefix_rule(pattern = ["jj", "squash"], decision = "allow")
prefix_rule(pattern = ["jj", "rebase"], decision = "allow")
prefix_rule(pattern = ["jj", "abandon"], decision = "allow")
prefix_rule(pattern = ["jj", "evolog"], decision = "allow")
prefix_rule(pattern = ["jj", "op", "log"], decision = "allow")
prefix_rule(pattern = ["jj", "operation", "log"], decision = "allow")
prefix_rule(pattern = ["jj", "undo"], decision = "allow")
prefix_rule(pattern = ["jj", "bookmark", "list"], decision = "allow")
prefix_rule(pattern = ["jj", "bookmark", "create"], decision = "allow")
prefix_rule(pattern = ["jj", "bookmark", "move"], decision = "allow")
prefix_rule(pattern = ["jj", "bookmark", "advance"], decision = "allow")
prefix_rule(pattern = ["jj", "bookmark", "set"], decision = "allow")
prefix_rule(pattern = ["jj", "bookmark", "rename"], decision = "allow")
prefix_rule(pattern = ["jj", "config", "get"], decision = "allow")
prefix_rule(pattern = ["jj", "config", "list"], decision = "allow")
prefix_rule(
pattern = ["jj", "git", "fetch"],
decision = "allow",
justification = "Jujutsu's fetch is allowed"
)
# --- deny / forbidden ---
prefix_rule(
pattern = ["git"],
decision = "forbidden",
justification = "Do not use raw git; use jj commands instead"
)
prefix_rule(
pattern = ["jj", "split"],
decision = "forbidden",
justification = "jj split should be done manually"
)
prefix_rule(
pattern = ["jj", "resolve"],
decision = "forbidden",
justification = "Conflicts should be resolved manually"
)
prefix_rule(
pattern = ["jj", "diffedit"],
decision = "forbidden",
justification = "jj diffedit should be done manually"
)
prefix_rule(
pattern = ["jj", "arrange"],
decision = "forbidden",
justification = "jj arrange should be done manually"
)
# --- ask / prompt ---
prefix_rule(
pattern = ["jj", "bookmark", "delete"],
decision = "prompt",
justification = "Deleting a bookmark requires confirmation"
)
prefix_rule(
pattern = ["jj", "bookmark", "forget"],
decision = "prompt",
justification = "bookmark forget requires confirmation"
)
prefix_rule(
pattern = ["jj", "bookmark", "track"],
decision = "prompt",
justification = "Starting to track a remote bookmark requires confirmation"
)
prefix_rule(
pattern = ["jj", "bookmark", "untrack"],
decision = "prompt",
justification = "Untracking a remote bookmark requires confirmation"
)
prefix_rule(
pattern = ["jj", "git", "push"],
decision = "prompt",
justification = "Pushing to the remote requires confirmation",
match = ["jj git push -b feature-x"],
)Using These Configs
Setup
- Extract the downloaded ZIP file.
- Copy the contents of the extracted folder—including the dotted directories—into your project root, preserving the directory structure.
- Start a new session in Claude Code or Codex CLI.
If your project already contains files with the same names, copying the configuration as-is will overwrite them. Back up the existing files first, or merge the contents manually.
Requirements
- Jujutsu must be installed and the
jjcommand must be available. - The target project must be initialized as a Jujutsu repository.
- Claude Code or Codex CLI must be installed.
Tested Versions
- Jujutsu: 0.43.0
- Claude Code: 2.1.220
- Codex CLI: 0.146.0
License
These configuration files are provided under the Apache License 2.0. Commercial use, modification, and redistribution are permitted.
See the LICENSE file included in each download for the full terms.
The Book Takes It Further
These configuration files are enough to get Claude Code and Codex to use Jujutsu for everyday version control. Juju-chu! goes further, covering practical workflows such as:
- Running formatters and linters with
jj fixwhenever an agent completes a task - Running tests, builds, and security checks before pushing
- Cleaning up and consolidating large sets of agent-generated changes
- Running multiple agents in parallel and integrating their changes cleanly