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

5.0 KiB

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