Files
Stellar/.agents/skills/jj-vcs/references/history-rewriting.md
T
2026-10-11 16:21:39 +08:00

131 lines
5.0 KiB
Markdown

# 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