Add CUPS print server container for Brother HL-4150CDN
This commit is contained in:
commit
6f3162849b
52 files changed
+5168
No files matched your search
@@ -0,0 +1,81 @@
|
||||
# Co-located jj + git Repositories
|
||||
|
||||
A **co-located** repo has both `.jj/` and `.git/` at the same root. jj manages local operations; git stays in sync underneath so remote-side tooling (CI, GitHub, hooks, IDE plugins, `gh` CLI) continues to work normally.
|
||||
|
||||
This is the **strongly recommended** setup for working with an existing git project.
|
||||
|
||||
## Why co-located
|
||||
|
||||
- **Local: jj's superior model.** Mutable commits, no staging area, the op log as a safety net.
|
||||
- **Remote: still git.** GitHub sees ordinary commits and refs. Collaborators don't need to know you use jj.
|
||||
- **Tooling parity.** IDE git integrations, pre-commit hooks, `gh`/`git` CLI for read-only inspection all continue to work.
|
||||
|
||||
## Creating a co-located repo
|
||||
|
||||
```bash
|
||||
# From scratch
|
||||
jj git init --colocate
|
||||
|
||||
# From a remote
|
||||
jj git clone <url> --colocate
|
||||
|
||||
# Adopt an existing git repo (run from inside it)
|
||||
cd existing-git-repo
|
||||
jj git init --colocate
|
||||
```
|
||||
|
||||
After this, both `.jj/` and `.git/` exist and stay synchronized automatically. **Never run `jj git init` without `--colocate`** on a project you already have git history for — non-colocated init creates a separate git store inside `.jj/repo/store/git` which is harder to work with.
|
||||
|
||||
## The mutation rule
|
||||
|
||||
> **Never use `git` for mutations in a co-located jj repo.** Read-only `git` is fine.
|
||||
|
||||
| git command | Allowed in jj repo? |
|
||||
|---|---|
|
||||
| `git log`, `git show`, `git diff`, `git blame`, `git grep` | ✅ Allowed (jj equivalents preferred) |
|
||||
| `git status` | ✅ Allowed (shows git's view; may differ from `jj st`) |
|
||||
| `git commit`, `git add`, `git stash`, `git reset` | ❌ Forbidden — corrupts jj state |
|
||||
| `git checkout <branch>`, `git switch` | ❌ Forbidden — use `jj edit` instead |
|
||||
| `git rebase`, `git merge`, `git cherry-pick` | ❌ Forbidden — use `jj rebase`, `jj new <a> <b>`, `jj duplicate` |
|
||||
| `git fetch` | ⚠️ Use `jj git fetch` — it keeps jj in sync |
|
||||
| `git pull` | ❌ Forbidden — `git pull` does fetch+merge; use `jj git fetch` + `jj rebase` |
|
||||
| `git push` | ❌ Forbidden — use `jj git push -b <bookmark>` |
|
||||
|
||||
The reason: jj snapshots the working copy on every jj command. If git mutates state behind jj's back, jj's next snapshot will be confused about what changed when, and the op log can get out of sync.
|
||||
|
||||
## Switching to git mode (rare; usually a smell)
|
||||
|
||||
If you genuinely need to use a git workflow for one operation (e.g., an external tool insists), do it carefully:
|
||||
|
||||
```bash
|
||||
# 1. Ensure jj working copy is committed and clean
|
||||
jj st
|
||||
# (resolve anything pending — describe, refine, etc.)
|
||||
|
||||
# 2. Now run the git operation
|
||||
git <something>
|
||||
|
||||
# 3. Return to jj — jj will re-sync on the next command
|
||||
jj st # Triggers re-sync
|
||||
jj edit <change-id> # Resume work
|
||||
```
|
||||
|
||||
**Almost every git operation has a jj equivalent that's safer.** Reach for git mode only as a last resort.
|
||||
|
||||
## Common gotchas
|
||||
|
||||
- **Git complains about uncommitted changes**: jj's working copy is always a commit; git sees that as "uncommitted." Use `jj st` (not `git status`) to assess state.
|
||||
- **HEAD detachment**: jj manages `HEAD` per change. `git checkout` will detach HEAD in confusing ways — use `jj edit` instead.
|
||||
- **`.gitignore` is respected by jj** automatically. `.jjignore` is only needed if you want jj-only ignore patterns the outer git doesn't see.
|
||||
- **Branches vs bookmarks**: git branches become jj bookmarks on fetch. Bookmarks don't auto-advance like git branches — you must `jj bookmark move` before pushing.
|
||||
|
||||
## Pushing from a co-located repo
|
||||
|
||||
Use `jj git push`, not `git push`:
|
||||
|
||||
```bash
|
||||
jj bookmark move main --to @ # Move bookmark to your latest change
|
||||
jj git push -b main # Push to remote
|
||||
```
|
||||
|
||||
See `references/PUSH.md` for the full push workflow.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Creating good commit messages
|
||||
|
||||
1. Use the following format:
|
||||
|
||||
```
|
||||
<type>: <subject>
|
||||
|
||||
<body>
|
||||
```
|
||||
The valid <type> values are: fix, feat, chore, docs
|
||||
Type and subject are required
|
||||
Body is optional
|
||||
|
||||
2. If there is a body, separate it from the subject with a blank line
|
||||
3. Limit the subject line to 50 characters. This is not a hard limit but a rule of thumb. The subject line should explain the "what" of the logical change
|
||||
4. Capitalize the subject line
|
||||
5. Do not end the subject line with a period
|
||||
6. Use the imperative mood in the subject line
|
||||
Imperative mood just means “spoken or written as if giving a command or instruction”. A few examples:
|
||||
|
||||
* Clean your room
|
||||
* Close the door
|
||||
* Take out the trash
|
||||
7. Wrap the body at 72 characters
|
||||
8. Use the body to explain what and why vs. how
|
||||
@@ -0,0 +1,176 @@
|
||||
# Resolving Conflicts
|
||||
|
||||
jj treats conflicts as **first-class objects**: a commit can contain a conflict, and that conflict **persists through rebases** until someone resolves it. This is the headline mental-model difference from git, where a working tree must be conflict-free before committing.
|
||||
|
||||
For an agent, this changes the rhythm:
|
||||
|
||||
- A `jj rebase` or `jj squash` that produces a conflict **does not fail**. It succeeds; the resulting commit is just marked conflicted.
|
||||
- You can keep working — even keep rebasing the conflicted commit further — and resolve it later.
|
||||
- `jj` won't let you push a conflicted commit to a remote, so conflicts must be resolved before `jj git push`.
|
||||
|
||||
## Detecting conflicts
|
||||
|
||||
```bash
|
||||
jj st # Shows "There are unresolved conflicts" if any
|
||||
jj resolve --list # Lists all conflicted paths in @
|
||||
jj resolve --list -r <change-id> # Lists conflicts in a specific change
|
||||
jj log # Conflicted changes are shown with a "conflict" marker
|
||||
```
|
||||
|
||||
## Conflict marker formats
|
||||
|
||||
jj supports three marker styles (set via `ui.conflict-marker-style`):
|
||||
|
||||
- **`diff`** (modern default) — shows base→side as a diff
|
||||
- **`snapshot`** — full content of each side, like git's `<<<<<<<` style
|
||||
- **`git`** — legacy git-compatible 3-way
|
||||
|
||||
Check your style:
|
||||
|
||||
```bash
|
||||
jj config get ui.conflict-marker-style
|
||||
```
|
||||
|
||||
### Example: `diff` style
|
||||
|
||||
```
|
||||
<<<<<<< Conflict 1 of 1
|
||||
%%%%%%% Changes from base to side #1
|
||||
-base content here
|
||||
+side 1 content here
|
||||
+++++++ Contents of side #2
|
||||
side 2 content here
|
||||
>>>>>>> Conflict 1 of 1 ends
|
||||
```
|
||||
|
||||
- Side #1 = "ours" (the destination — typically your current branch/change)
|
||||
- Side #2 = "theirs" (the source — typically what's being rebased/merged in)
|
||||
- The `%%%%%%%` block shows what side #1 changed *from the common base*; side #2's content is shown directly.
|
||||
|
||||
### Example: `snapshot` style
|
||||
|
||||
```
|
||||
<<<<<<< Conflict 1 of 1
|
||||
+++++++ Contents of side #1
|
||||
side 1 content here
|
||||
%%%%%%% Contents of base
|
||||
base content here
|
||||
+++++++ Contents of side #2
|
||||
side 2 content here
|
||||
>>>>>>> Conflict 1 of 1 ends
|
||||
```
|
||||
|
||||
Both styles delimit conflict regions identically. To resolve: replace the entire `<<<<<<< ... >>>>>>>` block with the desired final content.
|
||||
|
||||
## Five resolution paths (agent-friendly)
|
||||
|
||||
`jj resolve` with no `--tool` launches an interactive merge tool and **will hang in non-interactive agent environments**. Use one of these instead:
|
||||
|
||||
### 1. Edit conflict markers directly (most common)
|
||||
|
||||
Read the file, replace each `<<<<<<<` block with the resolved content, save. Then snapshot:
|
||||
|
||||
```bash
|
||||
jj st # Triggers snapshot; should report no remaining conflicts
|
||||
```
|
||||
|
||||
This is the canonical agent path. Works for any merge complexity.
|
||||
|
||||
### 2. Take one side wholesale: `:ours` / `:theirs`
|
||||
|
||||
For conflicts where one side simply wins (e.g., a rebase brings in unrelated formatting changes you don't want):
|
||||
|
||||
```bash
|
||||
jj resolve --tool :ours # Keep side #1; drop side #2's conflicting changes
|
||||
jj resolve --tool :theirs # Keep side #2; drop side #1's conflicting changes
|
||||
|
||||
# Scope to specific paths
|
||||
jj resolve --tool :ours path/to/file.rs
|
||||
```
|
||||
|
||||
These built-in tools are **non-interactive** and safe to use in agent contexts. They apply to all conflicts in `@` (or the paths given).
|
||||
|
||||
### 3. Restore from a specific version: `jj restore --from`
|
||||
|
||||
When neither side is right but a *different* revision has the content you want:
|
||||
|
||||
```bash
|
||||
jj restore --from <change-id> path/to/file.txt
|
||||
```
|
||||
|
||||
This pulls the file contents from `<change-id>` into the working copy, replacing conflict markers entirely.
|
||||
|
||||
### 4. Abandon a hopelessly conflicted change
|
||||
|
||||
If the conflicted change wasn't worth keeping:
|
||||
|
||||
```bash
|
||||
jj abandon <change-id>
|
||||
```
|
||||
|
||||
Descendants are rebased onto the parent. Their own conflicts (if any) remain to be resolved separately.
|
||||
|
||||
### 5. Resolve at the ancestor, let jj re-propagate
|
||||
|
||||
If a rebase produced the same conflict in many descendants, **don't resolve each descendant individually**. Resolve once at the change where the conflict first appeared, and jj will re-propagate the resolution forward:
|
||||
|
||||
```bash
|
||||
# Find where the conflict first appeared
|
||||
jj log -r 'conflicts()'
|
||||
|
||||
# Edit that change directly
|
||||
jj edit <ancestor-change-id>
|
||||
# ... fix conflict markers ...
|
||||
jj st # Snapshot the resolution
|
||||
jj edit @- # Move back to where you were
|
||||
```
|
||||
|
||||
Descendants pick up the resolution automatically.
|
||||
|
||||
## Verifying a resolution
|
||||
|
||||
After editing, **don't trust `jj st` alone**:
|
||||
|
||||
```bash
|
||||
# 1. Status should report no conflicts
|
||||
jj st
|
||||
|
||||
# 2. Grep for residual markers (catches typos that left markers in place)
|
||||
git grep -E '^(<{7}|>{7}|\\%{7}|\\+{7})' || echo 'no residual conflict markers'
|
||||
|
||||
# 3. Run the project's tests/linter — markers may be syntactically valid in some
|
||||
# languages (e.g. inside strings) and parse-tests catch what eyeballing misses.
|
||||
```
|
||||
|
||||
In a colocated repo, `git grep` is allowed (read-only). In a non-colocated repo, use `grep -rE` or `rg`.
|
||||
|
||||
## Pushing conflicted commits is blocked
|
||||
|
||||
```bash
|
||||
jj git push -b main
|
||||
# Refused: branch main contains conflicts
|
||||
```
|
||||
|
||||
You'll need to resolve before pushing. There's no `--force-push-conflicts` option — this is by design.
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
- ❌ **`jj resolve` (no `--tool`)** — interactive; will hang the agent. Always pass `--tool :ours` / `--tool :theirs` or edit markers manually.
|
||||
- ❌ **Resolving descendants individually after a rebase** — wasteful and error-prone; resolve at the ancestor (path 5).
|
||||
- ❌ **Relying on `jj st` to verify resolution** — also grep for residual markers and run tests.
|
||||
- ❌ **Trying to `git checkout` to "drop" a conflict** in a colocated repo — corrupts jj state. Use `jj abandon` or resolve.
|
||||
- ❌ **`jj squash` to "hide" a conflict into another commit** — the conflict propagates; it doesn't disappear.
|
||||
|
||||
## Quick reference
|
||||
|
||||
| Action | Command |
|
||||
|---|---|
|
||||
| List conflicts in `@` | `jj resolve --list` |
|
||||
| List conflicts everywhere | `jj log -r 'conflicts()'` |
|
||||
| Resolve manually | Edit file, then `jj st` |
|
||||
| Take "ours" (side #1) | `jj resolve --tool :ours [paths...]` |
|
||||
| Take "theirs" (side #2) | `jj resolve --tool :theirs [paths...]` |
|
||||
| Restore from another rev | `jj restore --from <change-id> <path>` |
|
||||
| Drop the change | `jj abandon <change-id>` |
|
||||
| Find conflict's ancestor | `jj log -r 'conflicts() ~ ::conflicts()-'` |
|
||||
| Check marker style | `jj config get ui.conflict-marker-style` |
|
||||
@@ -0,0 +1,47 @@
|
||||
# Gotchas
|
||||
|
||||
Short list of jj behaviors that regularly surprise agents.
|
||||
|
||||
**Tested with jj 0.41.0**
|
||||
|
||||
## 1. Working Copy Is a Commit
|
||||
|
||||
`@` is always a commit, not a staging area. `jj new` starts a different change; it does not finish the current one.
|
||||
|
||||
## 2. Most jj Commands Snapshot the Working Copy
|
||||
|
||||
Running `jj st`, `jj log`, and many other commands can record filesystem changes into the working-copy commit. If you intentionally need a stale view, use `--ignore-working-copy`.
|
||||
|
||||
## 3. Change IDs and Commit IDs Are Different
|
||||
|
||||
Change IDs are stable across rewrites. Commit IDs change whenever content or metadata changes. Prefer change IDs when handing work to another agent.
|
||||
|
||||
## 4. `jj undo` Is the First Recovery Tool
|
||||
|
||||
Do not manually reverse a bad `rebase`, `squash`, or `abandon` before trying `jj undo`. Use `references/RECOVERY.md` for deeper recovery.
|
||||
|
||||
## 5. Pager Output Is Not a Good Agent Default
|
||||
|
||||
Use the compact commands in `references/TEMPLATES.md`. Those commands intentionally use `jj --no-pager ...`.
|
||||
|
||||
## 6. Co-located Repos Still Forbid Git Mutations
|
||||
|
||||
`git log`, `git show`, `git diff`, `git blame`, `git grep`, and `git status` are fine. `git commit`, `git add`, `git rebase`, `git push`, and similar mutations can corrupt jj state.
|
||||
|
||||
## 7. `jj workspace forget` Does Not Delete Files
|
||||
|
||||
It only unregisters the workspace. Delete the directory separately if cleanup is intended.
|
||||
|
||||
## 8. Stale Workspaces Are Normal in Parallel Work
|
||||
|
||||
If another workspace rewrites your base, use `jj workspace update-stale`. Verify the result with `jj st` instead of improvising a manual repair.
|
||||
|
||||
## 9. Bare `jj resolve` Is Interactive
|
||||
|
||||
For agent-safe conflict handling, use `jj resolve --tool :ours`, `jj resolve --tool :theirs`, or edit conflict markers directly.
|
||||
|
||||
## 10. Default Output Is Often Too Large
|
||||
|
||||
Prefer compact templates, targeted revsets, and narrow filesets before reaching for full `jj log -p` or `jj show`.
|
||||
|
||||
When behavior feels surprising, stop and check `references/NEW_CHANGE.md`, `references/TEMPLATES.md`, `references/WORKSPACES.md`, or `references/RECOVERY.md` before inventing a git-style workaround.
|
||||
@@ -0,0 +1,49 @@
|
||||
# Starting a New Change
|
||||
|
||||
The jj workflow is: **describe first, then code, then refine**. The working copy is always a commit (`@`), so you're already in one when you start. The question is whether to reuse it or start fresh.
|
||||
|
||||
## The opening sequence
|
||||
|
||||
```bash
|
||||
# 1. Check what @ currently looks like
|
||||
jj st
|
||||
```
|
||||
|
||||
**If `@` is empty** (no changes, no description set, or description is fine to discard):
|
||||
- Reuse it. Just describe and start.
|
||||
|
||||
**If `@` has changes you want to keep** (it's someone else's WIP, or work you haven't finished):
|
||||
- Start fresh with `jj new` — this creates a new empty commit on top of `@`.
|
||||
|
||||
```bash
|
||||
# 2. Describe before coding (only with -m; never editor-based)
|
||||
jj desc -m "feat: Add user authentication to login endpoint"
|
||||
|
||||
# 3. Make changes — they snapshot to @ automatically on the next jj command
|
||||
# (no `jj add`, no `jj commit` needed)
|
||||
|
||||
# 4. Verify
|
||||
jj st
|
||||
```
|
||||
|
||||
## Why describe first
|
||||
|
||||
Setting the message up front:
|
||||
- Forces you to articulate the change as one logical unit before writing code (atomic commits)
|
||||
- Means there's never a phase where `@` says "(no description set)"
|
||||
- Makes the change navigable in `jj log` while it's still being built
|
||||
|
||||
See `references/COMMIT_MSG.md` for the message format conventions.
|
||||
|
||||
## After the change is done
|
||||
|
||||
**Do NOT run `jj new` to "close" the change.** In jj, the working copy is always a commit — there's no separate commit step. Leave `@` as-is; the *next* task's step 1 (`jj st` → reuse or `jj new`) handles transition.
|
||||
|
||||
If refinement is needed (split, squash, absorb, abandon), see `references/REFINE_COMMIT.md`.
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
- ❌ `jj commit` — doesn't exist; the working copy is already a commit
|
||||
- ❌ `jj desc` without `-m` — opens an editor; will hang in non-interactive agent environments
|
||||
- ❌ Running `jj new` at the end of a task — leaves an empty `@` for the next agent to clean up
|
||||
- ❌ Starting code before describing — you'll forget to describe, or describe vaguely after the fact
|
||||
@@ -0,0 +1,65 @@
|
||||
# Nonlinear Work
|
||||
|
||||
This skill supports sibling commits and unrelated work. Do not force everything onto the current stack if the work is logically independent.
|
||||
|
||||
**Tested with jj 0.41.0**
|
||||
|
||||
## When to Start a Sibling from `trunk()`
|
||||
|
||||
Use a fresh sibling when:
|
||||
|
||||
- `@` already represents one logical change and the new work is unrelated
|
||||
- the user wants multiple independent lines of work
|
||||
- you need to compare or merge two alternative implementations
|
||||
- a quick fix appears while a feature change is already in progress
|
||||
|
||||
## Preferred Flow
|
||||
|
||||
This skill keeps the local "describe first" rule, even for sibling work:
|
||||
|
||||
```bash
|
||||
jj st
|
||||
jj new trunk()
|
||||
jj desc -m "fix: handle stale workspace recovery"
|
||||
# make changes
|
||||
jj st
|
||||
```
|
||||
|
||||
Use `trunk()` instead of guessing `main` or `master`. If the new work should build on the current change, use plain `jj new` instead of `jj new trunk()`.
|
||||
|
||||
## Child vs Sibling
|
||||
|
||||
Use a child (`jj new`) when:
|
||||
|
||||
- the new work depends on the current change
|
||||
- the user wants a stacked sequence
|
||||
- the current commit is intentionally being split into follow-up commits
|
||||
|
||||
Use a sibling from `trunk()` when:
|
||||
|
||||
- the work should review, land, or be abandoned independently
|
||||
- you are branching off to do an unrelated fix, doc change, or experiment
|
||||
- parallel agents need clearly separated lines of history
|
||||
|
||||
## Merge-Shaped Work
|
||||
|
||||
If two independently meaningful lines of work should remain visible as a merge, create an explicit merge commit:
|
||||
|
||||
```bash
|
||||
jj new <left-change> <right-change> -m "merge: combine related lines of work"
|
||||
```
|
||||
|
||||
Ask the user before choosing between:
|
||||
|
||||
- rebasing one line onto the other
|
||||
- keeping a visible merge
|
||||
- pushing each line separately
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
- Do not keep piling unrelated work into the current `@` just because it is convenient.
|
||||
- Do not adopt a `describe -> code -> jj new -> repeat` loop. In this skill, `jj new` starts the next change; it does not close the current one.
|
||||
- Do not default to `jj new trunk() --no-edit -m ...` here. Keep the local sequence: choose the base first, then run `jj desc -m ...`.
|
||||
- Do not hardcode `main` or `master` when `trunk()` expresses the intent.
|
||||
|
||||
See `references/NEW_CHANGE.md` for the default change-starting workflow and `references/WORKSPACES.md` for multi-workspace parallelism.
|
||||
@@ -0,0 +1,38 @@
|
||||
### Pushing Changes
|
||||
|
||||
When the user asks you to push 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:
|
||||
|
||||
```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:
|
||||
|
||||
```bash
|
||||
# Create a bookmark at the current commit
|
||||
jj bookmark create my-feature
|
||||
|
||||
# Then push it
|
||||
jj git push -b my-feature
|
||||
```
|
||||
|
||||
For named remotes, multi-remote setups (e.g. fork + upstream), tracking remote bookmarks, and one-time configuration for the two canonical multi-remote workflows, see `references/REMOTES.md`.
|
||||
@@ -0,0 +1,91 @@
|
||||
# Query Languages
|
||||
|
||||
Use jj's query languages when you need to select revisions, paths, or render compact output without falling back to the default verbose UI.
|
||||
|
||||
**Tested with jj 0.41.0**
|
||||
|
||||
## Revsets
|
||||
|
||||
Use revsets to select commits and workspaces.
|
||||
|
||||
Common selectors:
|
||||
|
||||
- `@`: current working-copy change
|
||||
- `@-`: parent of the current working-copy change
|
||||
- `trunk()`: configured mainline; use this instead of guessing `main` vs `master`
|
||||
- `::@`: all ancestors of `@`
|
||||
- `trunk()..@`: work reachable from `@` that is not on `trunk()`
|
||||
- `working_copies()`: all workspace working-copy commits
|
||||
- `<workspace>@`: another workspace's working-copy commit
|
||||
- `<change-id>`: a stable change ID such as `wqzktpqm`
|
||||
|
||||
Agent patterns:
|
||||
|
||||
```bash
|
||||
# Current work relative to trunk
|
||||
jj --no-pager log -r 'trunk()..@'
|
||||
|
||||
# All active workspace tips
|
||||
jj --no-pager log -r 'working_copies()'
|
||||
|
||||
# One specific workspace tip
|
||||
jj --no-pager log -r '<workspace>@'
|
||||
```
|
||||
|
||||
Quote revsets that contain punctuation or function calls.
|
||||
|
||||
## Filesets
|
||||
|
||||
Use filesets when a command should operate on only part of the tree.
|
||||
|
||||
Common patterns:
|
||||
|
||||
- `path` or `"path"`: cwd-relative path prefix
|
||||
- `file:"path"`: exact cwd-relative file path
|
||||
- `glob:"pattern"`: cwd-relative glob
|
||||
- `root:"path"`: workspace-relative path prefix
|
||||
- `~x`: everything except `x`
|
||||
- `x & y`: intersection
|
||||
- `x | y`: union
|
||||
- `x ~ y`: subtract `y` from `x`
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
# Only markdown files in the current directory
|
||||
jj diff 'glob:"*.md"'
|
||||
|
||||
# Everything in src except snapshots
|
||||
jj diff 'src ~ glob:"src/**/*.snap"'
|
||||
|
||||
# Exact file path
|
||||
jj diff 'file:"README.md"'
|
||||
```
|
||||
|
||||
## Templates
|
||||
|
||||
Use templates when default output is too large or too decorative for agent work.
|
||||
|
||||
Common building blocks:
|
||||
|
||||
- `++`: concatenate output pieces
|
||||
- `if(cond, a, b)`: conditional formatting
|
||||
- `description.first_line()`: one-line summary
|
||||
- `change_id.shortest(8)`: stable short ID
|
||||
- `bookmarks`: bookmark names for a commit
|
||||
- `current_working_copy`, `empty`, `conflict`: useful booleans in `jj log`
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
jj --no-pager log --no-graph -n 5 -T 'change_id.shortest(8) ++ " " ++ if(description, description.first_line(), "(no description)") ++ "\n"'
|
||||
```
|
||||
|
||||
Start from `references/TEMPLATES.md` instead of inventing new templates unless you need something substantially different.
|
||||
|
||||
## Rules
|
||||
|
||||
- Prefer `trunk()` over hardcoding `main` or `master`.
|
||||
- Quote revsets and filesets in shell commands when punctuation could be interpreted by the shell.
|
||||
- Prefer compact `jj --no-pager` inspection commands before widening the query.
|
||||
- If a query fails to parse, check `jj <command> --help`, `jj help -k revsets`, `jj help -k filesets`, or `jj help -k templates`.
|
||||
@@ -0,0 +1,118 @@
|
||||
# Recovery and the Operation Log
|
||||
|
||||
The **operation log** records every mutation to the repo — every `jj` command that changed state. Nothing is ever truly lost. This is jj's safety net: an agent that makes a mistake (bad squash, accidental abandon, wrong rebase) can always go back.
|
||||
|
||||
## The three recovery primitives
|
||||
|
||||
### 1. `jj undo` — reverse the last operation
|
||||
|
||||
```bash
|
||||
jj undo
|
||||
```
|
||||
|
||||
The first thing to reach for. Reverses the most recent operation atomically — works for `abandon`, `squash`, `rebase`, `restore`, `describe`, and most other mutations.
|
||||
|
||||
**Prefer `jj undo` over manually reversing changes** — it's always correct and safe.
|
||||
|
||||
### 2. `jj op log` — see the full operation history
|
||||
|
||||
```bash
|
||||
jj op log # All operations, newest first
|
||||
jj op log -n 20 # Limit to 20
|
||||
jj op log -p # Include diffs of what each op changed
|
||||
```
|
||||
|
||||
If you are inspecting first and do not yet need full diffs, start with the compact `jj op log` command from `references/TEMPLATES.md`. Add `-p` only after you have narrowed the relevant op IDs.
|
||||
|
||||
Each operation has an `op-id`. Use these to navigate further back than a single `jj undo`.
|
||||
|
||||
### 3. `jj op restore` — jump to any past state
|
||||
|
||||
```bash
|
||||
jj op restore <op-id>
|
||||
```
|
||||
|
||||
Restores the entire repo to the state it was in at that op. **This is the nuke-from-orbit recovery** — it can undo many operations at once. Safe because the restore itself becomes a new op in the log (you can `jj undo` it if you change your mind).
|
||||
|
||||
## Per-change history: `jj evolog`
|
||||
|
||||
The op log shows everything; `jj evolog` shows how a *single change* evolved (was rewritten, squashed into, rebased, etc.):
|
||||
|
||||
```bash
|
||||
jj evolog # Evolution of the current @
|
||||
jj evolog -r <change-id> # Evolution of a specific change
|
||||
jj evolog -p # Include patches between versions
|
||||
```
|
||||
|
||||
If you are inspecting first and do not yet need patches, start with the compact `jj evolog` command from `references/TEMPLATES.md`. Add `-p` only after you have narrowed the relevant version.
|
||||
|
||||
Useful when you want to recover an earlier *version* of a specific commit (e.g., "what did this commit look like before I squashed into it?").
|
||||
|
||||
## Common recovery scenarios
|
||||
|
||||
### Accidentally abandoned the wrong change
|
||||
|
||||
```bash
|
||||
jj undo # If it was just now
|
||||
# or
|
||||
jj op log # Find the abandon op
|
||||
jj op restore <op-id> # Restore to before it
|
||||
```
|
||||
|
||||
### A `jj squash` merged work into the wrong parent
|
||||
|
||||
```bash
|
||||
jj undo # Single-step undo
|
||||
```
|
||||
|
||||
If you've done more work since:
|
||||
|
||||
```bash
|
||||
jj op log # Locate the squash op
|
||||
jj op restore <op-id> # Jump back; ops after that point are dropped
|
||||
```
|
||||
|
||||
### A rebase went sideways
|
||||
|
||||
```bash
|
||||
jj undo # Reverse the rebase entirely
|
||||
```
|
||||
|
||||
### Lost work after a complex sequence
|
||||
|
||||
```bash
|
||||
jj op log -p # Diffs show exactly what each op did
|
||||
# Find the op id where the work still existed
|
||||
jj op restore <op-id>
|
||||
```
|
||||
|
||||
### Need a previous version of a commit's content
|
||||
|
||||
```bash
|
||||
jj evolog -r <change-id> -p # Find the version you want
|
||||
jj restore --from <commit-id-from-evolog> <path> # Restore working-copy content from it
|
||||
```
|
||||
|
||||
### Restored the wrong op-id
|
||||
|
||||
```bash
|
||||
jj undo # The restore is itself a recorded op
|
||||
```
|
||||
|
||||
## Notes for agents
|
||||
|
||||
- **Always reach for `jj undo` first** when something looks wrong. Don't try to manually unwind with new commits or `jj abandon` — `undo` is the canonical safe path.
|
||||
- **The op log isn't pruned aggressively** — even a week of work is fully retrievable.
|
||||
- **`jj op restore` is recorded** — it's not destructive in the way `git reset --hard` is. You can always undo a restore.
|
||||
- **There is no stash/pop in jj** because every state is automatically a commit in the op log. Treat `jj undo` / `jj op restore` as the equivalent.
|
||||
|
||||
## Quick reference
|
||||
|
||||
| Action | Command |
|
||||
|---|---|
|
||||
| Undo last operation | `jj undo` |
|
||||
| See all operations | `jj op log` |
|
||||
| See ops with diffs | `jj op log -p` |
|
||||
| Jump to past state | `jj op restore <op-id>` |
|
||||
| Change evolution | `jj evolog -r <change-id>` |
|
||||
| Restore file from past version | `jj restore --from <commit-id> <path>` |
|
||||
@@ -0,0 +1,77 @@
|
||||
### Squashing Changes
|
||||
|
||||
`jj squash` moves changes from one revision to another. The default is `@` → its parent; pass `--into` to target any other change.
|
||||
|
||||
```bash
|
||||
# Default: squash @ into its parent
|
||||
jj squash
|
||||
|
||||
# Squash @ into a specific change (not the parent)
|
||||
jj squash --into <change-id>
|
||||
jj squash -t <change-id> # short form
|
||||
|
||||
# Squash a non-@ change into its parent
|
||||
jj squash -r <change-id>
|
||||
|
||||
# Full explicit form: any source, any destination
|
||||
jj squash --from <source-id> --into <destination-id>
|
||||
|
||||
# Squash only specific paths (the rest stay in the source)
|
||||
jj squash path/to/file.txt
|
||||
jj squash --into <change-id> src/auth/
|
||||
```
|
||||
|
||||
**Combined description prompt**: if both source and destination have non-empty descriptions, `jj squash` opens an editor to pick a combined description — and hangs in agent environments. Always pass `-m` inline when squashing two described commits together:
|
||||
|
||||
```bash
|
||||
jj squash --into <change-id> -m "feat: Combine auth changes"
|
||||
```
|
||||
|
||||
**Empty source**: when squashing leaves the source revision empty (no content vs. its parent), jj abandons it by default. Pass `--keep-emptied` to keep an empty placeholder instead.
|
||||
|
||||
**Interactive forms to avoid**: `jj squash -i` / `jj squash --interactive` open a TUI and will hang in agent environments. Use `--from` / `--into` / paths instead.
|
||||
|
||||
### Splitting Commits
|
||||
|
||||
**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.
|
||||
|
||||
### Absorbing Changes
|
||||
|
||||
Automatically distribute changes to the commits that last modified those lines:
|
||||
|
||||
```bash
|
||||
# Absorb working copy changes into appropriate ancestor commits
|
||||
jj absorb
|
||||
```
|
||||
|
||||
### Abandoning Commits
|
||||
|
||||
Remove a commit entirely (descendants are rebased to its parent):
|
||||
|
||||
```bash
|
||||
jj abandon <change-id>
|
||||
```
|
||||
|
||||
### Undoing Operations
|
||||
|
||||
Reverse the last jj operation:
|
||||
|
||||
```bash
|
||||
jj undo
|
||||
```
|
||||
|
||||
This reverts the repository to its state before the previous command. Useful for recovering from mistakes like accidental `abandon`, `squash`, or `rebase`.
|
||||
|
||||
### Restoring Files
|
||||
|
||||
Discard changes to specific files or restore files from another revision:
|
||||
|
||||
```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
|
||||
@@ -0,0 +1,164 @@
|
||||
# Named Remotes and Multi-Remote Workflows
|
||||
|
||||
For pushing to a single remote (the common case), see `references/PUSH.md`. This file covers everything else: managing named remotes, selecting which remote to push to, tracking remote bookmarks, and the two canonical multi-remote setups.
|
||||
|
||||
## Named remotes
|
||||
|
||||
When a repo has more than one remote (e.g. `origin` = your fork, `upstream` = the canonical source), you select the target with `--remote <name>`.
|
||||
|
||||
```bash
|
||||
jj git remote list # See all remotes and their URLs
|
||||
jj git remote add upstream <url> # Add a second remote
|
||||
jj git remote rename origin fork # Rename a remote
|
||||
jj git remote set-url origin <new-url> # Change a remote's URL
|
||||
jj git remote remove old-name # Remove a remote (and forget its bookmarks)
|
||||
```
|
||||
|
||||
**Default remote selection**: when `--remote` is omitted, `jj git push` uses:
|
||||
|
||||
1. The `git.push` config setting (if set), or
|
||||
2. The remote named `origin` (if multiple remotes exist), or
|
||||
3. The single remote (if only one exists)
|
||||
|
||||
There is **no built-in option to push to multiple remotes in one command** — call `jj git push` once per remote.
|
||||
|
||||
### Pushing the same bookmark to two remotes
|
||||
|
||||
```bash
|
||||
jj bookmark move my-feature --to @ # Move the bookmark to current change
|
||||
jj git push -b my-feature --remote fork # Push to your fork
|
||||
jj git push -b my-feature --remote upstream # Push to canonical (e.g. for a PR target branch)
|
||||
```
|
||||
|
||||
### Setting a default remote
|
||||
|
||||
To avoid typing `--remote` repeatedly:
|
||||
|
||||
```bash
|
||||
# Per-repo (writes to .jj/repo/config.toml)
|
||||
jj config set --repo git.push fork
|
||||
|
||||
# Globally (writes to ~/.config/jj/config.toml)
|
||||
jj config set --user git.push origin
|
||||
```
|
||||
|
||||
### Tracking a remote bookmark
|
||||
|
||||
By default, when you `jj git fetch`, remote bookmarks appear as `<name>@<remote>` (e.g. `main@upstream`). To get a local bookmark that follows it:
|
||||
|
||||
```bash
|
||||
jj bookmark track main@upstream # Track upstream/main as a local 'main'
|
||||
jj bookmark track 'glob:release/*@upstream' # Track all upstream release branches
|
||||
```
|
||||
|
||||
Once tracked, `jj git push --tracked --remote upstream` will push all tracked bookmarks back to that remote.
|
||||
|
||||
### Fetching from a specific remote
|
||||
|
||||
```bash
|
||||
jj git fetch --remote upstream # Fetch only from upstream
|
||||
jj git fetch --all-remotes # Fetch from every configured remote
|
||||
```
|
||||
|
||||
### When a push is rejected
|
||||
|
||||
If the remote has moved since your last fetch, `jj git push` refuses (similar to `git push --force-with-lease`). Re-sync first:
|
||||
|
||||
```bash
|
||||
jj git fetch --remote <name> # Pull the remote's current state
|
||||
# (resolve any bookmark conflicts or rebase if needed)
|
||||
jj git push -b <bookmark> --remote <name> # Try again
|
||||
```
|
||||
|
||||
### Quick reference
|
||||
|
||||
| Action | Command |
|
||||
|---|---|
|
||||
| List remotes | `jj git remote list` |
|
||||
| Add a remote | `jj git remote add <name> <url>` |
|
||||
| Push to specific remote | `jj git push -b <bookmark> --remote <name>` |
|
||||
| Push all tracked bookmarks to a remote | `jj git push --tracked --remote <name>` |
|
||||
| Set default push remote (repo) | `jj config set --repo git.push <name>` |
|
||||
| Track a remote bookmark | `jj bookmark track <name>@<remote>` |
|
||||
| Fetch one remote | `jj git fetch --remote <name>` |
|
||||
| Fetch all remotes | `jj git fetch --all-remotes` |
|
||||
|
||||
## Common multi-remote workflows
|
||||
|
||||
Two canonical setups documented in [the jj docs](https://docs.jj-vcs.dev/latest/guides/multiple-remotes/). Pick the one that matches your relationship to the remotes; the config is one-time per repo.
|
||||
|
||||
**Nomenclature** (jj convention):
|
||||
- `origin` = a remote you have write access to (typically where you push)
|
||||
- `upstream` = the more canonical / well-known repo (you may not have push access)
|
||||
- Trunk is assumed to be `main`, so remote bookmarks are `main@origin` and `main@upstream`
|
||||
|
||||
### Scenario 1: GitHub-style fork contributing upstream
|
||||
|
||||
`upstream` is the canonical project; `origin` is your fork; you open PRs from `origin` against `upstream`.
|
||||
|
||||
Typical actions:
|
||||
- Fetch from `upstream` to get the latest changes
|
||||
- Push `main` to `origin` to keep your fork's `main` in sync
|
||||
- Push `my-feature` to `origin`, then open a PR targeting `upstream`
|
||||
|
||||
One-time setup:
|
||||
|
||||
```bash
|
||||
# Fetch from both remotes when you run `jj git fetch`
|
||||
jj config set --repo git.fetch '["upstream", "origin"]'
|
||||
|
||||
# Default push target is your fork
|
||||
jj config set --repo git.push origin
|
||||
|
||||
# Track both remote bookmarks named `main`
|
||||
jj bookmark track main
|
||||
|
||||
# Make upstream's main the trunk (treated as immutable; rebases stop here)
|
||||
jj config set --repo 'revset-aliases."trunk()"' main@upstream
|
||||
```
|
||||
|
||||
Why these specifically:
|
||||
- Tracking `main@upstream` keeps your local `main` updated whenever you fetch from upstream
|
||||
- Tracking `main@origin` means `jj git push` will also update your fork's `main`
|
||||
- `trunk() = main@upstream` prevents jj from rewriting commits that are already in the canonical repo
|
||||
|
||||
### Scenario 2: Independent repository, periodically integrating from upstream
|
||||
|
||||
`origin` is a long-lived repository with its own divergent history; `upstream` is the source you occasionally pull changes from but don't contribute back to.
|
||||
|
||||
Typical actions:
|
||||
- Fetch from `origin` to get the latest of your own work
|
||||
- Push bookmarks to `origin`; merge PRs into `main@origin`
|
||||
- Periodically fetch from `main@upstream` and merge/rebase/duplicate its changes into `main@origin`
|
||||
|
||||
One-time setup:
|
||||
|
||||
```bash
|
||||
# Fetch only from origin (typical case)
|
||||
jj config set --repo git.fetch '["origin"]'
|
||||
# Or fetch from both if you want upstream changes visible on every fetch:
|
||||
# jj config set --repo git.fetch '["upstream", "origin"]'
|
||||
|
||||
# Default push target is origin
|
||||
jj config set --repo git.push origin
|
||||
|
||||
# Track only origin's main — do NOT track upstream's main
|
||||
jj bookmark track main --remote=origin
|
||||
jj bookmark untrack main --remote=upstream
|
||||
|
||||
# Your origin defines the trunk (your main is the canonical line)
|
||||
jj config set --repo 'revset-aliases."trunk()"' main@origin
|
||||
```
|
||||
|
||||
Why these specifically:
|
||||
- Tracking only `main@origin` keeps `main@upstream` as a clearly separate reference you can `rebase`/`merge` from intentionally — it won't silently overwrite your local `main`
|
||||
- `trunk() = main@origin` treats your divergent line (not upstream's) as the immutable history boundary
|
||||
|
||||
### Choosing between the scenarios
|
||||
|
||||
| Question | Scenario 1 | Scenario 2 |
|
||||
|---|---|---|
|
||||
| Do you push features back to upstream as PRs? | Yes | No |
|
||||
| Should fetching from upstream update your local `main`? | Yes | No (you integrate manually) |
|
||||
| Is `upstream/main` immutable from your repo's perspective? | Yes | No (you may rewrite when integrating) |
|
||||
| Is `origin/main` allowed to diverge from `upstream/main`? | Minimally (just for syncing) | Yes (it's the point) |
|
||||
@@ -0,0 +1,84 @@
|
||||
# Token-Reducing Templates
|
||||
|
||||
Use inline `-T` templates for compact, agent-friendly jj output. Prefer inline `-T` use over editing repo or global jj config so the skill stays explicit and portable.
|
||||
|
||||
Assumptions:
|
||||
|
||||
- Assume `jj 0.41.0` or later.
|
||||
- Each template was tested with `jj 0.41.0`.
|
||||
- If a template fails to parse, treat that as jj version drift and verify with `jj <command> --help` plus `jj help -k templates`.
|
||||
- Use the commands below as written. They intentionally put `--no-pager` at the top-level `jj` invocation so agent output does not depend on pager configuration.
|
||||
|
||||
## `jj log`
|
||||
|
||||
Tested with: `jj 0.41.0`
|
||||
|
||||
```bash
|
||||
jj --no-pager log --no-graph -n 20 -T 'change_id.shortest(8) ++ if(bookmarks, " " ++ bookmarks, "") ++ if(current_working_copy, " @", "") ++ if(empty, " (empty)", "") ++ if(conflict, " (conflict)", "") ++ " " ++ if(description, description.first_line(), "(no description)") ++ "\n"'
|
||||
```
|
||||
|
||||
This keeps the change ID, bookmark or `@`, empty/conflict markers, and the first description line. Use `shortest(8)` instead of bare `shortest()` so IDs do not collapse to one character on `jj 0.41.0`.
|
||||
|
||||
## `jj show`
|
||||
|
||||
Tested with: `jj 0.41.0`
|
||||
|
||||
```bash
|
||||
jj --no-pager show <change-id> --stat -T 'change_id.shortest(8) ++ if(bookmarks, " " ++ bookmarks, "") ++ if(current_working_copy, " @", "") ++ if(empty, " (empty)", "") ++ if(conflict, " (conflict)", "") ++ " " ++ if(description, description.first_line(), "(no description)") ++ "\n"'
|
||||
```
|
||||
|
||||
Prefer `--stat` for a compact file summary. Swap `--stat` for `--no-patch` if you want the commit summary without any file-level diff summary.
|
||||
|
||||
## `jj diff`
|
||||
|
||||
Tested with: `jj 0.41.0`
|
||||
|
||||
```bash
|
||||
jj --no-pager diff -T 'self.status_char() ++ " " ++ self.display_diff_path() ++ "\n"'
|
||||
```
|
||||
|
||||
Add `-r <rev>` if you want a diff for a specific revision instead of the working copy.
|
||||
|
||||
## `jj bookmark list`
|
||||
|
||||
Tested with: `jj 0.41.0`
|
||||
|
||||
```bash
|
||||
jj --no-pager bookmark list -T 'if(self.normal_target(), self.name() ++ ": " ++ self.normal_target().change_id().shortest(8) ++ " " ++ if(self.normal_target().description(), self.normal_target().description().first_line(), "(no description)") ++ "\n", self.name() ++ ": (conflicted)\n")'
|
||||
```
|
||||
|
||||
Use this for ordinary bookmark inspection. If you are explicitly debugging conflicted bookmarks and need every old/new target, fall back to jj's default output.
|
||||
|
||||
## `jj workspace list`
|
||||
|
||||
Tested with: `jj 0.41.0`
|
||||
|
||||
```bash
|
||||
jj --no-pager workspace list -T 'self.name() ++ ": " ++ self.target().change_id().shortest(8) ++ " " ++ if(self.target().description(), self.target().description().first_line(), "(no description)") ++ if(self.target().empty(), " (empty)", "") ++ "\n"'
|
||||
```
|
||||
|
||||
This shows workspace name, working-copy change ID, description, and whether that working copy is empty.
|
||||
|
||||
## `jj op log`
|
||||
|
||||
Tested with: `jj 0.41.0`
|
||||
|
||||
```bash
|
||||
jj --no-pager op log --no-graph -n 20 -T 'id.short(8) ++ " " ++ time.start().ago() ++ " " ++ description.first_line() ++ "\n"'
|
||||
```
|
||||
|
||||
Use this first when you need recovery context but do not yet need full diffs. Add `-p` only after you have narrowed down the interesting op IDs.
|
||||
|
||||
## `jj evolog`
|
||||
|
||||
Tested with: `jj 0.41.0`
|
||||
|
||||
```bash
|
||||
jj --no-pager evolog -r <change-id> --no-graph -n 20 -T 'commit.change_id().shortest(8) ++ " " ++ if(commit.description(), commit.description().first_line(), "(no description)") ++ " | " ++ operation.id().short(8) ++ " " ++ operation.description().first_line() ++ "\n"'
|
||||
```
|
||||
|
||||
This keeps the evolving change summary on the left and the rewriting operation on the right.
|
||||
|
||||
## Not Templated
|
||||
|
||||
`jj st` and `jj status` do not expose a template flag in `jj 0.41.0`, so leave status output untemplated. The same applies to commands like `jj undo`, `jj op restore`, and `jj resolve` where the value is in the side effect, not in custom rendering.
|
||||
@@ -0,0 +1,209 @@
|
||||
# Parallel Workspaces
|
||||
|
||||
Use `jj workspace` when multiple agents need independent working copies of the same repo. Each workspace gets its own `@` while sharing the same repo store and operation log.
|
||||
|
||||
**Tested with jj 0.41.0**
|
||||
|
||||
This skill intentionally prefers project-local workspace directories when they are safely ignored. Directory priority for this skill:
|
||||
|
||||
1. `.workspaces/`
|
||||
2. `workspaces/`
|
||||
3. Repo-local instructions such as `AGENTS.md` or `CLAUDE.md`
|
||||
4. Ask the user
|
||||
|
||||
Do not replace this with a blanket "sibling directories only" rule.
|
||||
|
||||
## Before Creating a Workspace
|
||||
|
||||
First, make sure project-local workspaces will not leak into Git history:
|
||||
|
||||
```bash
|
||||
grep -E '^\.?workspaces/?$' .gitignore 2>/dev/null
|
||||
```
|
||||
|
||||
If the entry is missing, add it before `jj workspace add` and record that change using the normal workflow from `references/NEW_CHANGE.md`:
|
||||
|
||||
```bash
|
||||
echo ".workspaces/" >> .gitignore
|
||||
```
|
||||
|
||||
Never skip this check. `jj workspace add` creates directories that contain `.jj/` metadata, and the outer Git repo must ignore them.
|
||||
|
||||
Use descriptive names so `jj workspace list` and `jj log` stay readable:
|
||||
|
||||
```bash
|
||||
REPO_NAME=$(basename "$(jj workspace root)")
|
||||
WORKSPACE_PATH=".workspaces/${REPO_NAME}-<purpose>"
|
||||
```
|
||||
|
||||
Examples:
|
||||
|
||||
- `.workspaces/myrepo-test-runner`
|
||||
- `.workspaces/myrepo-reviewer-agent`
|
||||
- `.workspaces/myrepo-refactor-experiment`
|
||||
|
||||
Avoid generic names such as `tmp`, `wip`, or `new`.
|
||||
|
||||
## Creating and Bootstrapping
|
||||
|
||||
Create the workspace and move into it:
|
||||
|
||||
```bash
|
||||
jj workspace add "$WORKSPACE_PATH"
|
||||
cd "$WORKSPACE_PATH"
|
||||
```
|
||||
|
||||
After creation:
|
||||
|
||||
- the workspace has its own working-copy commit
|
||||
- it appears in log output as `<workspace-name>@`
|
||||
- it shares history and the operation log with the main workspace
|
||||
- snapshots in this workspace affect only this workspace's `@`
|
||||
|
||||
If the repo needs environment setup, bootstrap it in the new workspace before handing it to another agent:
|
||||
|
||||
| Marker file | Setup command |
|
||||
| --- | --- |
|
||||
| `package.json` | `npm install` |
|
||||
| `Cargo.toml` | `cargo build` |
|
||||
| `pyproject.toml` | `uv sync` |
|
||||
| `Gemfile` | `bundle install` |
|
||||
| `flake.nix` | `nix develop` |
|
||||
| `go.mod` | `go mod download` |
|
||||
| `mix.exs` | `mix deps.get` |
|
||||
|
||||
## Agent Handoff
|
||||
|
||||
When one agent creates a workspace for another, always report:
|
||||
|
||||
- the absolute workspace path
|
||||
- the change ID the agent should edit
|
||||
- the exact `cd` command
|
||||
- the exact `jj edit <change-id>` command
|
||||
- the scope boundaries
|
||||
|
||||
Use a handoff like this:
|
||||
|
||||
```text
|
||||
Workspace: /absolute/path/to/.workspaces/myrepo-tests
|
||||
Change ID: abcdefgh
|
||||
|
||||
cd /absolute/path/to/.workspaces/myrepo-tests
|
||||
jj edit abcdefgh
|
||||
|
||||
Rules:
|
||||
- Always use -m for messages.
|
||||
- Run jj st after every mutation.
|
||||
- Do not modify files outside the assigned scope.
|
||||
```
|
||||
|
||||
Absolute paths matter. Agents lose track of relative locations more often than they lose track of change IDs.
|
||||
|
||||
## Monitoring Progress
|
||||
|
||||
Start from the compact `jj log` command in `references/TEMPLATES.md`, then swap in the revset you need:
|
||||
|
||||
- all workspace tips: `-r 'working_copies()'`
|
||||
- one named workspace: `-r '<workspace>@'`
|
||||
|
||||
Examples of the revsets themselves:
|
||||
|
||||
```bash
|
||||
working_copies()
|
||||
reviewer@
|
||||
```
|
||||
|
||||
See `references/QUERY_LANGUAGES.md` for the revset syntax behind those examples.
|
||||
|
||||
## Combining Results
|
||||
|
||||
When a workspace finishes its work, bring that change back to the main line and retire the workspace. Do not decide silently; ask the user which strategy they want.
|
||||
|
||||
### Strategy 1: Rebase into the Default Workspace
|
||||
|
||||
Use this when one developer is orchestrating multiple agents and the result should land as ordinary commits in the default workspace.
|
||||
|
||||
```bash
|
||||
jj rebase -s <workspace-change-id> -d @
|
||||
jj workspace forget <workspace-name>
|
||||
rm -rf "$WORKSPACE_PATH"
|
||||
```
|
||||
|
||||
If the repo uses bookmarks for shipping work, move or create the bookmark after the rebase using the normal push workflow from `references/PUSH.md`.
|
||||
|
||||
### Strategy 2: Keep an Explicit Merge
|
||||
|
||||
Use this when both lines of work are independently meaningful and the merge structure should remain visible:
|
||||
|
||||
```bash
|
||||
jj new <change-from-workspace-a> <change-from-workspace-b> -m "merge: combine parallel work"
|
||||
```
|
||||
|
||||
### Strategy 3: Push a Bookmark and Open a PR
|
||||
|
||||
Use this when the work needs human review before landing:
|
||||
|
||||
```bash
|
||||
jj bookmark create my-feature -r <workspace-change-id>
|
||||
jj git push -b my-feature
|
||||
```
|
||||
|
||||
Decision guide:
|
||||
|
||||
| Situation | Default suggestion |
|
||||
| --- | --- |
|
||||
| Solo developer running multiple local agents | Strategy 1 |
|
||||
| Long-lived feature with checkpoints | Strategy 1, then push a bookmark when ready |
|
||||
| Human review required | Strategy 3 |
|
||||
| Both lines of work are independently meaningful | Strategy 2 |
|
||||
|
||||
If the context is ambiguous, suggest Strategy 1 first and ask whether the user wants the result to land in the default workspace or as a PR.
|
||||
|
||||
## Listing and Cleaning Up
|
||||
|
||||
```bash
|
||||
jj workspace list
|
||||
jj workspace forget <workspace-name>
|
||||
rm -rf "$WORKSPACE_PATH"
|
||||
```
|
||||
|
||||
If you need to inspect `jj workspace list` output as an agent, use the compact command from `references/TEMPLATES.md`.
|
||||
|
||||
`jj workspace forget` only unregisters the workspace. It does not delete files from disk.
|
||||
|
||||
## `update-stale`
|
||||
|
||||
If another workspace rewrites the commit your workspace had checked out, jj marks that workspace as stale. Use this first:
|
||||
|
||||
```bash
|
||||
jj workspace update-stale
|
||||
jj st
|
||||
```
|
||||
|
||||
Do not improvise a manual repair before trying `update-stale`. If the old operation is gone, jj can create a recovery commit with the current working-copy contents instead of silently discarding them.
|
||||
|
||||
## Conflict Avoidance
|
||||
|
||||
| Risk | Recommended mitigation |
|
||||
| --- | --- |
|
||||
| Build outputs or generated files | Ignore them or give each workspace separate output paths |
|
||||
| Shared config files | Assign a single owner for that file or change it serially |
|
||||
| Lockfiles / dependency updates | Let one task own dependency changes |
|
||||
| Same source files across workspaces | Redesign the task boundary or serialize the work |
|
||||
|
||||
## Critical Rules
|
||||
|
||||
1. Never skip the `.gitignore` check for project-local workspace directories.
|
||||
2. Always follow the directory priority: `.workspaces/` -> `workspaces/` -> repo instructions -> ask.
|
||||
3. Use descriptive names: `${REPO_NAME}-<purpose>`, not `tmp`.
|
||||
4. Bootstrap the workspace before handing it to another agent.
|
||||
5. Report the absolute path, change ID, and next commands when a workspace is ready.
|
||||
6. Run `jj workspace forget` before deleting the directory.
|
||||
7. Ask before choosing between rebase, merge, or PR integration.
|
||||
8. Use `jj workspace update-stale` as the first response to a stale workspace.
|
||||
|
||||
## Reference
|
||||
|
||||
Convention adapted from [edmundmiller/dotfiles `using-jj-workspaces`](https://lobehub.com/skills/edmundmiller-dotfiles-using-jj-workspaces).
|
||||
|
||||
Additional handoff, monitoring, stale-workspace, and conflict-avoidance patterns were adapted from [joshuadavidthomas/agent-skills `jj`](https://github.com/joshuadavidthomas/agent-skills/tree/main/jj) and its [`workspaces.md`](https://raw.githubusercontent.com/joshuadavidthomas/agent-skills/main/jj/workspaces.md), while intentionally preserving this skill's local `.workspaces/` / `workspaces/` directory priority and local change-creation workflow.
|
||||
Reference in new issue
Block a user