Add CUPS print server container for Brother HL-4150CDN

This commit is contained in:
juergen committed 2026-10-18 09:16:37 +02:00
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.