In short: jj resolves the commits to simplify: -s sources with their descendants, plus
-r revisions, or by default revsets.simplify-parents
(reachable(@, mutable())). It checks they are all mutable. In one
transform_descendants pass, each selected commit with more than one parent keeps only the
parents that are heads of its parent set. Every commit whose parents changed is reparented
with its tree unchanged. It reports
Removed E edges from S out of N commits. and records simplify N commits.
The command is cmd_simplify_parents. The edge removal
itself is CommitRewriter::simplify_ancestor_merge in
jj-lib. jj rebase applies the same helper
to the rebased commits when given --simplify-parents
(source, use).
1. Command-line surface
| Flag | Type | Default | Meaning |
|---|---|---|---|
-s, --source REVSETS | revsets, repeatable | none | Simplify these commits and all their descendants (source). |
-r, --revision, --revisions REVSETS | revsets, repeatable | none | Simplify exactly these commits (source). |
The two flags are combined: descendants(sources) | revisions
(source). With neither, the default revset applies.
2. Configuration
| Key | Default | Effect |
|---|---|---|
revsets.simplify-parents | reachable(@, mutable()) | The commits to simplify when no flags are given: the mutable commits connected to @ (default, source). |
revset-aliases."immutable_heads()" | (built-in) | Every selected commit must be mutable, even ones that end up unchanged (source). |
3. End-to-end flow
4. Core architecture touchpoints
| Structure | Read / write | How | Explained on |
|---|---|---|---|
| Index (ancestry) | read | heads() over each merge's parent list decides which edges are redundant. | index |
| Commit parents | write | Redundant parents are removed. Descendants of rewritten commits get the new IDs. | commits |
| Trees | read | Kept as is, because reparent() copies the old tree (the help text: "This has no effect on any revision's contents"). | trees |
Bookmarks, @, predecessors | write | Follow the rewritten commits when the transaction finishes. | transactions |
| Operation | write | One operation, or none if nothing changed. | operations |
5. Transaction and operation
- Transaction: one, after the rewritability check (source).
- Operation description:
simplify <N> commits, where N is the number of selected commits, not the number changed (source). - Commits rewritten: merges with a redundant parent, plus every descendant of a rewritten commit.
- No-op runs: no parents change, so the transaction finishes with
Nothing changed.(test).
6. Algorithm and invariants
For a commit A with parents (B, C) where C is an ancestor of B, the edge A→C is redundant: every commit
reachable through C is already reachable through B. simplify_ancestor_merge
asks the index for the heads of A's parent list and keeps only those, in their original order. If all
parents are independent, as in a normal diamond merge, nothing changes
(test_simplify_parents_no_change_diamond).
Only commits in the selected set are simplified. Their descendants are visited too, because
transform_descendants walks all descendants. Those only get their parent IDs remapped,
even if they have redundant edges themselves (source). The parent
count used for the statistics is taken from the rewriter's intended new parents, which are
already remapped to rewritten IDs (source).
Invariants: the set of ancestors of every commit is unchanged. Trees are unchanged. Only selected commits lose edges. Nothing immutable is touched.
7. Errors and edge cases
| Message | When | Source |
|---|---|---|
The root commit 000000000000 is immutable / Commit … is immutable | The selection includes an immutable commit. | simplify_parents.rs, test |
Nothing changed. | The selection is empty, or no merge in it has a redundant parent. | test |
| (no "Removed …" line) | The statistics are only printed when at least one commit lost an edge (source). | simplify_parents.rs |
8. Worked example
From upstream's test_simplify_parents_redundant_parent. Commit
c (@) has parents a and b, and b is a
child of a. Then jj simplify-parents -s @- (or -r @ -r @-) runs:
Upstream's snapshot:
$ jj simplify-parents -s @-
Removed 1 edges from 1 out of 3 commits.
Working copy (@) now at: royxmykx 265f0407 c | c
Parent commit (@-) : zsuskuln 123b4d91 b | b
@- is a | b (both parents of c), so -s @- selects
a, b and c: "3 commits". Only c is a merge, and
heads(a, b) = b, so one edge goes. c gets a new commit ID but the same
tree, and the log is now the straight line a → b → c.