This commit is contained in:
EFLKumo committed 2026-10-11 16:21:39 +08:00
commit ba8f925eed
121 files changed
+19786

No files matched your search

@@ -0,0 +1,125 @@
# Collaboration
Commands for working with Git remotes, bookmarks, and pushing/pulling changes.
## jj git clone
Clone a remote Git repository into a jj-backed repo.
```
jj git clone <URL> # clone and checkout default branch
jj git clone <URL> --colocate # clone as a colocated repo (works with git commands too)
```
## jj git fetch
Fetch changes from a remote. Unlike `git fetch`, this fetches all bookmarks by default and can automatically track new bookmarks based on config.
```
jj git fetch # fetch all remotes
jj git fetch --remote origin # fetch only origin
jj git fetch --remote origin main # fetch specific bookmark(s)
```
After fetch, new remote bookmarks appear as `BOOKMARK@remote`. They aren't automatically tracked — you must explicitly `jj bookmark track` them.
## jj git push
Push changes to a remote. By default, pushes all bookmarks that have a remote counterpart.
```
jj git push # push all tracked bookmarks
jj git push --remote origin # push to specific remote
jj git push --bookmark main # push only specific bookmark(s)
jj git push --deleted BOOKMARK # propagate a bookmark deletion
jj git push --change CHANGE_ID # push a specific change by change ID
```
- `--bookmark` / `-b`: specify which bookmarks to push
- `--change`: push a single change to the remote as a draft PR (creates a bookmark on the remote)
- `--remote`: specify the remote
- `--deleted`: confirm you want to push a bookmark deletion to the remote
**Push checklist:**
1. The revision has a description: `jj describe -m "msg"`
2. The bookmark points at the right revision: `jj bookmark move --to=REV BOOKMARK` or use `jj bookmark set BOOKMARK` (creates or moves)
3. The revision does not have conflicts: `jj status` will show them
4. You're not upstream of others' work (if collaborating)
5. Push: `jj git push`
## jj bookmark
Bookmark management. Short alias: `jj b`.
```
jj bookmark list # list all bookmarks and their targets (aliases: l)
jj bookmark create NAME # create a new bookmark at @ (aliases: c)
jj bookmark move --to REV NAME # move bookmark NAME to point at REV (aliases: m)
jj bookmark set NAME # create or update bookmark NAME at @ (aliases: s)
jj bookmark track NAME --remote R # track a remote bookmark locally (aliases: t)
jj bookmark untrack NAME # stop tracking a remote bookmark
jj bookmark delete NAME # delete a local bookmark (aliases: d)
jj bookmark forget NAME # forget a bookmark without pushing deletion
jj bookmark advance NAME # advance bookmark to nearest descendant of current target
jj bookmark rename OLD NEW # rename a bookmark
```
**Bookmark workflow for pushing changes:**
1. Work in `@` (on top of main)
2. When ready: `jj bookmark move --to=@ main` (move the main bookmark to `@`)
3. `jj git push` (pushes main to origin/main)
Or use a feature-branch workflow:
1. `jj bookmark create my-feature` (creates bookmark at `@`)
2. Work, describe, etc.
3. `jj git push -b my-feature` (pushes just `my-feature`)
**Tracking:** When you clone a repo, remote bookmarks like `main@origin` exist but aren't automatically tracked. `jj bookmark track main --remote origin` creates a local `main` bookmark that follows `main@origin`. Without this, `jj git push` may not know which local bookmark corresponds to which remote bookmark.
## jj git remote
Manage Git remotes.
```
jj git remote list # list remotes
jj git remote add NAME URL # add a remote
jj git remote remove NAME # remove a remote
jj git remote set-url NAME URL # change a remote's URL
```
## jj git colocation
Manage colocation with an underlying Git repo. Colocation means the `.jj` repo works on the same working copy as a `.git` repo, allowing both `jj` and `git` commands.
```
jj git colocation init # initialize colocation
jj git colocation deinit # remove colocation
jj git export # manually sync jj changes to git
jj git import # manually sync git changes to jj
```
With colocation, export/import happens automatically on most commands. Use manual `jj git export` or `jj git import` if you've made changes in git that jj doesn't see yet.
## jj gerrit
Interact with Gerrit Code Review.
```
jj gerrit submit CHANGE_ID # submit a change to Gerrit
jj gerrit abandon CHANGE_ID # abandon a change in Gerrit
```
## Working with stacks
jj excels at stacking changes. Common pattern:
1. `jj new main` — start from main
2. `jj describe -m "part 1"` — describe first change
3. `jj new` — start second change
4. `jj describe -m "part 2"` — describe second change
5. `jj bookmark create stack-1` — save both (bookmark on latest)
6. `jj bookmark create stack-2` — or just push with `jj git push -b stack-1`
To restack after reviews:
1. `jj rebase -b stack-1 -o main` — move whole stack
2. `jj git push` — update remote
@@ -0,0 +1,51 @@
# Command quick reference
Every jj command with a one-line description and key flags. Read this when you need a quick overview of what commands are available.
| Command | Description | Key flags |
|---|---|---|
| `jj abandon [-r REV]` | Drop a revision from history | `-r` revision to drop (default: `@`) |
| `jj absorb` | Auto-merge changes into their logical parent revisions | — |
| `jj arrange` | Interactively rearrange the commit graph | — |
| `jj bisect -r GOOD..BAD` | Binary search for a bad revision | Standard bisect: start, good, bad, skip |
| `jj bookmark <SUB>` | Manage bookmarks | Subcommands: list, create, delete, move, set, track, untrack, forget, advance, rename |
| `jj commit -m "msg"` | Describe `@` and create a new empty change on top | `-m`, `-i` interactive |
| `jj config <SUB>` | Manage configuration | Subcommands: get, set, edit, list, default |
| `jj describe -r REV -m "msg"` | Set or edit a revision's description | `-r` revision, `-m` message, `--stdin`, `--editor` |
| `jj diff [-r REV]` | Show diff of a revision | `-r`, `--git`, `-s` summary, `--stat`, `--name-only`, `--color-words` |
| `jj diffedit -r REV` | Edit a revision's content diff interactively | `-r`, `-i` |
| `jj duplicate -r REV` | Copy a revision's content to a new change ID | `-r` |
| `jj edit REV` | Switch working copy to an existing revision | — |
| `jj evolog -r REV` | Show how a change's content evolved over time | `-r`, `-p` patch |
| `jj file <SUB>` | File operations | Subcommands: list, show, chmod, track, untrack |
| `jj fix [-r REV]` | Apply formatting/linting tools to files in a revision | `-r`, `-s` source revsets, `--tool`, `-i` |
| `jj git <SUB>` | Git remote operations | Subcommands: clone, fetch, push, remote, init, export, import, colocation |
| `jj gerrit <SUB>` | Interact with Gerrit Code Review | Subcommands: submit, abandon |
| `jj help [CMD]` | Show help for jj or a specific command | — |
| `jj interdiff --from A --to B` | Show differences between two revisions' diffs | `--from`, `--to` |
| `jj log [-r REVSET]` | Show revision history graph | `-r`, `-n`, `--no-graph`, `-p`, `--reversed`, `--count`, `-T` template |
| `jj metaedit -r REV` | Edit revision metadata (author, date, description) | `-r`, `--reset-author` |
| `jj new [REV]...` | Create a new empty change (child of given revision) | `-m`, `--no-edit`, `--insert-after`, `--insert-before` |
| `jj next` | Move working copy to the child revision | `-e` edit the destination |
| `jj op <SUB>` | Operation log commands | Subcommands: log, restore, undo, discard, integrate |
| `jj parallelize -r REVS` | Make sibling revisions into parallel branches | `-r` |
| `jj prev` | Move working copy to the parent revision | `-e` edit the destination |
| `jj rebase -r REV -o DEST` | Move revisions to different parent(s) | `-r`, `-s`, `-b`, `-o/\!--onto`, `--insert-after`, `--insert-before`, `--skip-emptied` |
| `jj redo` | Redo the last undone operation | — |
| `jj resolve PATH...` | Resolve merge conflicts | `--list` |
| `jj restore [PATH]...` | Restore file contents (undo changes to a path or revision) | `--from`, `--into`, `--changes-in`, `-i` |
| `jj revert [-r REV]` | Apply reverse of the given revision(s) | `-r` (same as `jj restore --changes-in REV`) |
| `jj root` | Show workspace root directory | — |
| `jj show [-r REV]` | Show revision description and its diff | `-r` |
| `jj sign -r REV` | Cryptographically sign a revision | `-r` |
| `jj simplify-parents -r REV` | Remove redundant parent edges | `-r` |
| `jj sparse <SUB>` | Manage sparse checkout patterns | Subcommands: set, edit, list, clear, reset |
| `jj split [-r REV]` | Split a revision into two | `-r`, `-i`, `-m`, `--parallel`, `--insert-after`, `--insert-before` |
| `jj squash [-f FROM -t INTO]` | Move changes between revisions | `-f` from, `-t` into, `-i`, `-k` keep-emptied, `-m`, `--insert-after` |
| `jj status` | Show working copy status (aliases: `st`) | — |
| `jj tag <SUB>` | Manage tags | Subcommands: list, set, delete |
| `jj undo` | Undo the last operation | `--undo` (un-undo) |
| `jj unsign -r REV` | Drop cryptographic signature from a revision | `-r` |
| `jj util <SUB>` | Infrequently used utilities | Subcommands: gc, completion, config-schema, etc. |
| `jj version` | Show version information | — |
| `jj workspace <SUB>` | Manage workspaces | Subcommands: root, list, add, forget, upgrade |
@@ -0,0 +1,95 @@
# Core concepts
Understanding these concepts is essential for using jj correctly. Read this when the user seems new to jj, or when you need to reason about jj behavior beyond simple commands.
## Change IDs vs commit hashes
Every revision in jj has two identifiers:
- **Change ID** — A stable identifier (e.g., `abc12345`) that follows the revision across rebases, squashes, and splits. Use this when referring to a logical change over time.
- **Commit hash** — A hash of the commit content (like Git). Changes every time the diff changes. Use this when you need to reference exact content.
Commands that take revisions (almost all of them) accept change IDs, commit hashes, or revset expressions. Prefer change IDs for user-facing references since they're stable.
## The working copy (`@`)
The `@` symbol represents the working copy revision. Unlike Git's detached HEAD or branch-based model:
- `@` is always a regular commit. It can have a description, parent(s), and children.
- When you edit files, those changes belong to `@`.
- `jj new` creates a new *empty* commit on top of `@` and makes that the new `@`. The old `@` becomes `@-` (parent of working copy).
- `jj edit REV` sets the working copy to revision REV. Edits go directly into that revision's diff. Use `jj new` instead unless you specifically need to amend an existing change.
- `jj status` shows the diff between `@` and its parent (`@-`).
## Operation log
Every jj command creates an **operation** in the operation log. This is jj's safety net:
- `jj op log` shows the history of commands (like shell history but richer)
- `jj undo` creates a new operation that reverses the last one
- `jj op restore OP_ID` restores the repo to the state after a given operation
- `jj --at-op OP_ID <command>` runs a command as if the repo were at that operation (read-only inspection)
Nothing is permanently lost unless you run `jj util gc` (garbage collection). The operation log is append-only.
## Revsets
Revsets are jj's query language for selecting revisions. They appear everywhere a command accepts revisions. Key patterns:
| Expression | Meaning |
|---|---|
| `@` | The working copy revision |
| `main` | The revision pointed to by bookmark `main` |
| `main@origin` | The revision pointed to by `main` on the `origin` remote |
| `root()` | The root revision (virtual, all repos share it) |
| `all()` | Every revision in the repo |
| `mine()` | Revisions authored by the current user |
| `none()` | Empty set |
| `parents(REVSET)` | Parents of revisions in the set |
| `children(REVSET)` | Children of revisions in the set |
| `ancestors(REVSET)` | All ancestors (recursive parents) |
| `descendants(REVSET)` | All descendants (recursive children) |
| `::` | DAG range — `A::B` means "A and B and everything between them" |
| `..` | Reachable from B but not from A — `A..B` means "B and ancestors of B not also ancestors of jj" |
| `X \ Y` | Set difference |
| `X + Y` | Set union |
| `X & Y` | Set intersection |
| `empty()` | Revisions with no diff from their parent |
| `description(REGEX)` | Revisions whose description matches REGEX |
| `author("NAME")` | Revisions authored by NAME (partial match) |
| `bookmarks()` | Revisions that have bookmarks |
| `immutable_heads()` | The configured immutable set |
| `latest(N, REVSET)` | The latest N revisions in the set |
| `merged()` | Merge commits |
**Practical examples:**
- `main..@` — revisions in `@`'s ancestors that aren't in main (what you changed since main)
- `ancestors(@) & ~immutable_heads()` — everything you can safely rewrite
- `@-` — the parent of the working copy (one commit back)
- `@--` — grandparent of the working copy
- `description("fix.*")` — revisions with descriptions starting with "fix"
- `empty() & ~@` — revisions that are empty but not the working copy (candidates for abandonment)
## Bookmarks
Bookmarks are labels attached to revisions. They serve the same conceptual role as Git branches but behave differently:
- Bookmarks can coexist at the same revision (no conflict)
- Moving a bookmark doesn't affect the revision it points to — the revision keeps its change ID
- Tracking a remote bookmark (`jj bookmark track NAME --remote origin`) creates a local bookmark that follows the remote's updates
- Git branches are represented as bookmarks with `@remote` suffixes (e.g., `main@origin`)
- `jj bookmark list` shows all bookmarks; the output format is `BOOKMARK: CHANGE_ID commit_hash`
## The immutable set
By default, jj prevents rewriting commits that are ancestors of any remote-tracking bookmark. This is configured by `revsets.immutable_heads` in the config (typically `immutable_heads() & ~@` plus `bookmarks()@remote`). Use `--ignore-immutable` to bypass this check (e.g., `jj rebase --ignore-immutable -r OLD_REV`).
## Global flags
These work with any jj command:
- `--at-op OP` — run as if repo were at a past operation (read-only)
- `--ignore-immutable` — allow rewriting protected commits
- `--ignore-working-copy` — skip snapshotting the working copy (faster, but may show stale state)
- `-R PATH` — operate on a different repository
- `--config NAME=VAL` — set config options inline (TOML syntax)
@@ -0,0 +1,117 @@
# Daily work
Commands and patterns for the core edit/describe/log cycle.
## jj status
Shows the diff between the working copy (`@`) and its parent. This is the first thing to run when a user asks "what's going on?" or "what have I changed?".
Key alias: `jj st` (the most common form used in practice).
Output shows:
- Which revision you're on (change ID, commit hash, bookmark)
- Whether `@` is empty or has changes
- The diff (added/modified/deleted files)
- Any conflicts
## jj new
Creates a new empty revision as a child of the given revision (default: `@`) and makes it the working copy. This is the closest jj equivalent to starting a new branch of work.
```
jj new # new child of @, empty, becomes new @
jj new REV # new child of REV
jj new A B # new merge revision with parents A and B
```
- `--insert-after REV` — insert the new revision after REV (between REV and its children)
- `--insert-before REV` — insert before REV (between REV and its parents)
- `-m "msg"` — set description immediately
- `--no-edit` — create the revision but don't switch working copy to it
## jj describe
Sets or edits the description (commit message) of a revision.
```
jj describe # opens editor to edit @'s description
jj describe -m "msg" # set description of @ inline
jj describe -r REV # describe a specific revision
```
- `-m "msg"` — set description from command line (no editor)
- `--stdin` — read description from stdin
- `-r REV` — describe a different revision (default: `@`)
**Important:** A revision needs a description before it can be pushed. Always use `jj describe` (not `jj commit`) when the user says "write a commit message."
## jj commit
Combines `jj describe -m "msg"` followed by `jj new`. Records the current change's description and immediately starts a fresh empty revision on top.
```
jj commit -m "feat: add user auth"
```
This is the jj equivalent of git's `commit` but note: there's no staging step. Any changes to tracked files are included.
## jj log
Shows the revision graph. This is your primary tool for understanding history.
```
jj log # default view (configurable via revsets.log)
jj log -r "main..@" # changes you've made since main
jj log -r "author(me)" # your changes
jj log -r "all()" # everything
jj log -r REV # log starting from REV
```
Key flags:
- `-r REVSET` — which revisions to show (revset)
- `-n N` / `--limit N` — limit to N results
- `--no-graph` — flat list (good for scripting)
- `--reversed` — oldest first
- `-p` / `--patch` — show the diff for each revision
- `-T TEMPLATE` — custom template
- `--count` — just print the count
## jj diff
Shows the diff of a revision. Unlike `jj log -p`, this focuses purely on the content diff.
```
jj diff # diff of @ vs its parent
jj diff -r REV # diff of REV vs its parent
jj diff --from A --to B # diff between two specific revisions
```
Key flags:
- `--git` — git-format diff (good for piping or review)
- `-s` / `--summary` — just show which files changed
- `--stat` — histogram of changes
- `--name-only` — just file paths
- `--color-words` — word-level diff
- `--context N` — lines of context
## jj edit
Sets the working copy to an existing revision. Edits go directly into that revision's diff — this rewrites history.
```
jj edit REV # switch working copy to REV
```
Prefer `jj new REV` over `jj edit REV` in most cases: `jj new` creates a child so you can't accidentally rewrite the parent. Use `jj edit` when you intentionally want to amend an existing change (e.g., you forgot to include a file).
## jj file
File-level operations.
```
jj file list [-r REV] # list tracked files in REV
jj file show -r REV PATH # show file contents from REV
jj file chmod MODE PATH # change file mode (e.g., +x, -x)
jj file untrack PATH # stop tracking a file (adds to .gitignore automatically)
jj file track PATH # start tracking a file (reverses untrack)
```
@@ -0,0 +1,130 @@
# History rewriting
Commands for restructuring the commit graph. jj's operation log makes history rewriting safe — nothing is permanently lost.
## jj rebase
Move revisions to different parent(s). The most fundamental history-rewriting command.
```
jj rebase -r REV -o DEST # rebase single revision REV onto DEST
jj rebase -b REV -o DEST # rebase REV and all its descendants onto DEST
jj rebase -r REV --insert-after ANCHOR # reorder: put REV after ANCHOR
jj rebase -r REV --insert-before ANCHOR # reorder: put REV before ANCHOR
```
Moving `-r` vs `-b`:
- `-r REV` — rebase only REV; its descendants follow because jj automatically rebases them on top
- `-b REV` — rebase the entire branch (REV and all its descendants) to a new location. Equivalent to `-s roots(dest..REV)`
Note: jj automatically rebases descendants so you don't need to manually restack after a rebase. However, this can create duplicate commits if the destination already has commits with the same change IDs. `--keep-divergent` controls this behavior.
## jj squash
Move changes from one revision into another. The most common pattern is moving `@`'s changes into its parent (the jj equivalent of amending).
```
jj squash # squash @ into its parent
jj squash -f SOURCE -t DEST # move SOURCE's changes into DEST
```
- Without flags: squash `@` into `@-` (the parent). After this, `@` becomes empty and `@-` now includes the changes.
- `-f` / `--from`: which revision to take changes from (default: `@`)
- `-t` / `--into`: which revision to put changes into (default: `@`)
- `-i` / `--interactive`: choose which parts of the diff to squash
- `-k` / `--keep-emptied`: don't abandon the source revision after squashing
- `-m "msg"`: set description on the result
The `-o` (experimental), `--insert-after`, and `--insert-before` flags control where the resulting revision goes in the graph, similar to `jj rebase`.
## jj split
Split one revision into two. Changes in the original revision are partitioned into two new revisions.
```
jj split -r REV # interactively choose which files go into the first split
jj split FILE... # put specified files into the new first revision
```
- `-r REV`: the revision to split (default: `@`)
- `-i`: force interactive mode (choose which hunks go where)
- `-m "msg"`: description for the new (first) revision
- `--parallel`: split into two parallel revisions (same parent) instead of parent-child
- `--insert-after` / `--insert-before` / `-o`: control where the new revision goes
## jj abandon
Drop a revision. The revision is removed from the graph and its descendants are rebased onto its parent(s).
```
jj abandon -r REV # abandon a specific revision
jj abandon # abandon @ (only works if @ is empty)
```
Common use: clean up empty revisions after squash/restructuring. `jj abandon -r empty()` is a batch cleanup pattern.
## jj absorb
Automatically moves changes from child revisions into the parent revisions they logically belong to. It detects which changes should have been made in which parent by analyzing the diff.
```
jj absorb
```
Runs on `@` by default. Useful when you make a fix that should belong to a lower commit in your stack — instead of manually squashing, `jj absorb` figures out where each hunk belongs.
## jj parallelize
Make sibling revisions into a linear chain or vice versa.
```
jj parallelize -r REVS...
```
Takes a set of revisions and makes them siblings (all children of the same parent), rebasing their descendants as needed.
## jj arrange
Interactive graph rearrangement. Opens a diff editor where you can drag and drop revisions to reorder them. More visual than command-line rebase operations.
```
jj arrange
```
## jj diffedit
Touch up the content changes in a revision. Opens an editor showing the revision's diff and lets you modify it directly.
```
jj diffedit -r REV
```
Use this when you need to change what a specific revision does, rather than adding a fix on top.
## jj metaedit
Modify a revision's metadata (author, author date, committer, description) without changing its content.
```
jj metaedit -r REV
```
## jj restack
This is not a real command — jj restacks automatically. You should never need to manually restack after rebase, squash, etc. If a rebase leaves empty commits, `jj abandon` them.
## jj squash/absorb vs jj squash
- Use `jj squash` when you want to explicitly move changes between revisions
- Use `jj absorb` when you want jj to figure out where changes belong automatically
## jj squash vs git commit --amend
- `jj squash` without arguments is jj's equivalent of `git commit --amend`: it takes `@`'s changes and merges them into `@-`, leaving `@` empty
- Unlike git, the old `@`'s change ID still exists (as an empty revision) — you can `jj abandon` it afterwards
## jj split + jj rebase
A common pattern: split a large revision into two, then rebase one of them to a different parent:
1. `jj split -r BIG_COMMIT` — split into two
2. `jj rebase -r NEW_REV -o DIFFERENT_PARENT` — move the new revision elsewhere
@@ -0,0 +1,119 @@
# Recovery
Commands for undoing mistakes, resolving conflicts, and restoring lost work.
## jj undo
Undoes the most recent operation. This is the first recovery command to reach for.
```
jj undo # undo last operation
jj undo --undo # undo the undo (same as jj redo)
```
Any operation can be undone — commit, rebase, push, etc. The undo itself creates a new operation, so it can also be undone.
## jj redo
Redo the most recently undone operation.
```
jj redo # redo last undo
```
## jj op log / jj operation
View the operation log — the history of all jj commands run in this repo. This is jj's equivalent of shell history but tied to repo state.
```
jj op log # show all operations
jj op log -n 20 # show last 20 operations
```
Each operation has an ID (hash). The most recent operation is shown at the top with `@`. The snapshot of the working copy is also an operation.
## jj op restore
Restore the repo to a previous state by operation. Unlike `jj undo` (which only undoes the last operation), this can jump to any point in the operation log.
```
jj op restore OP_ID # restore repo to state after OP_ID
jj op restore --what undo # restore just the undo history (not working copy)
```
This is *not* a revert — it moves the entire repo state backward (or forward). Changes made after that operation will appear as divergent changes. Use `jj --at-op` for read-only inspection first.
## jj restore (content restore)
Restore file contents within a revision. This is NOT the same as `jj op restore`.
```
jj restore # restore all files in @ from its parent
jj restore PATH # restore specific files in @ from its parent
jj restore --from SOURCE # restore from a specific source revision
jj restore --changes-in REV # undo the changes introduced by REV (creates reverse diff on @)
```
- Without `--from`: restores from the parent of the target revision
- Without `--into`: restores into `@`
- `--changes-in REV` is the closest equivalent to `git revert REV` — it applies the reverse of REV's diff to `@`
- `--restore-descendants`: when rebasing descendants, preserve their content (not their diff)
To "unstage" files (there's no real staging area in jj, but for git users who ask): use `jj restore PATH` to undo changes to specific files in `@`.
## jj resolve
Resolve merge conflicts. jj tracks conflicts natively (unlike git, which leaves conflict markers in files and lets you handle them).
```
jj resolve PATH... # resolve conflicts in specific files
jj resolve --list # list all unresolved conflicts
```
When you run `jj resolve`, jj opens the external merge tool (configured in `merge-tools`). If no tool is configured, it falls back to leaving conflict markers in the file, which you can edit manually.
After resolving conflicts manually (by editing the file), mark them as resolved with `jj resolve PATH`.
## jj --at-op (read-only inspection)
Run any jj command as if the repo were at a past operation. Read-only — useful for inspecting old state before deciding whether to restore.
```
jj --at-op OP_ID log # log from past state
jj --at-op OP_ID status # status from past state
jj --at-op OP_ID diff -r REV # diff from past state
```
## Divergent changes
When the operation log has diverged (e.g., two concurrent operations), `jj status` will warn about divergent operations. This happens when you run jj commands from different terminals or after `jj op restore`.
To resolve divergence:
1. `jj --at-op OP_ID status` for each divergent operation to understand the state
2. `jj op restore` to pick one side
3. Or use `jj new` to create a merge of the divergent states
## Common recovery scenarios
**"I rebased and lost changes"**
- `jj undo` — undoes the rebase
- If `jj undo` isn't enough: `jj op log` to find the pre-rebase operation, then `jj op restore OP_ID`
**"I pushed the wrong thing"**
- You can't unpush (remote doesn't support it), but you can:
1. Fix the local revision
2. `jj bookmark move --to=FIXED BOOKMARK`
3. `jj git push --force` or `jj git push` with a new commit
**"I accidentally squashed the wrong files"**
- `jj undo` immediately
- If too late: use the operation log to find the pre-squash state
**"I want to get old version of a file back"**
- `jj restore --from OLD_REV PATH` — restore PATH from OLD_REV into `@`
- Or: `jj file show -r OLD_REV PATH > PATH` to extract it as a file
**"jj status shows conflicts"**
- `jj resolve --list` to see which files have conflicts
- Resolve them individually: `jj resolve PATH`
- After resolving all, `jj status` will clear