In short: jj resolves the target (--to, else revsets.bookmark-advance-to,
default @). With bookmark names, it picks the local bookmarks matching them. Without,
it evaluates revsets.bookmark-advance-from (default
heads(::to & bookmarks())) with to bound to the target, and picks the
bookmarks pointing into it. Bookmarks already at the target are dropped; if none remain,
No bookmarks to update. Every move must be a fast-forward, and there's no override. In one
transaction the bookmarks are pointed at the target, recorded as
point bookmark <names> to commit <id>.
The command is cmd_bookmark_advance. Its help text
calls jj bookmark advance --to x the equivalent of
jj bookmark move --from 'heads(::x & bookmarks())' --to x
(source); compare
jj bookmark move, which has no
configured defaults but allows --allow-backwards. Bookmark storage is on the
view page.
1. Command-line surface
| Flag | Type | Default | Meaning |
|---|---|---|---|
NAMES (positional) | name patterns | none | Advance bookmarks with these names; globs by default. When given, revsets.bookmark-advance-from is ignored (source, parse_union_name_patterns). |
-t, --to REVSET | revset | revsets.bookmark-advance-to | The target, exactly one commit (source). |
2. Configuration
| Key | Default | Effect |
|---|---|---|
revsets.bookmark-advance-to | @ | The target when --to isn't given. If it doesn't resolve to one commit, the error gets a hint naming this key (default, source). |
revsets.bookmark-advance-from | heads(::to & bookmarks()) | Which commits' bookmarks to advance when no names are given. It can refer to the target as to (default, source). |
The default from-revset means "the nearest bookmarked commits at or below the target", so in a stack only the top bookmark moves, not every bookmark beneath it.
3. End-to-end flow
4. Core architecture touchpoints
| Structure | Read / write | How | Explained on |
|---|---|---|---|
View.local_bookmarks | read, write | Matched by name or by target, then set to RefTarget::normal(target). A conflicted bookmark becomes a normal one. | view |
| Revset engine | read | The from-revset is parsed with a local variable to holding the target's commit ID, evaluated once, and used as a membership test (source). | revsets |
| Index (ancestry) | read | is_ancestor for the fast-forward check (is_fast_forward). | index |
View.head_ids | maybe write | The target is added as a head (set_local_bookmark_target). | view |
| Operation | write | One operation if anything moved, none otherwise. | operations |
5. Transaction and operation
- Transaction: one, after selection and the checks (source). With nothing to update, no operation is recorded.
- Operation description:
point bookmark <name1, name2, …> to commit <full hex id>, the same text asbookmark moveandset(source). - Commits created or rewritten: none. The working copy is only snapshotted.
6. Algorithm and invariants
Without names, the from-revset is parsed with a revset parse context whose local variables include
to, set to a string node holding the target's full commit ID, so it behaves like a symbol
naming that commit (source). Parse warnings are printed under
In revsets.bookmark-advance-from. Every local bookmark is then checked: it is selected if
any of its present targets is in the evaluated set (source).
With the default heads(::to & bookmarks()), that set is the bookmarked commits among the
target's ancestors (including itself) that have no bookmarked descendant in that set. If the target
already carries a bookmark, the set is just the target, and every selected bookmark is then dropped as
"already there", giving No bookmarks to update.
(source).
The fast-forward test is is_fast_forward: some present
side of the old target must be an ancestor of the new one. Unlike bookmark move, there is no
flag to skip it (source).
Invariants: never creates or deletes bookmarks, never moves one backwards or sideways, never rewrites commits. Either every selected bookmark moves or none does.
7. Errors and edge cases
| Message | When | Source |
|---|---|---|
No bookmarks to update. | Nothing selected, or everything is already at the target. | advance.rs |
Refusing to advance bookmark backwards or sideways: <name> | A selected bookmark isn't an ancestor of the target. Nothing moves. | advance.rs |
Revset `…` didn't resolve to any revisions, with hint `revsets.bookmark-advance-to` controls the default target. … | The target doesn't resolve to one commit; the hint appears only when --to wasn't given. | advance.rs |
Warning: Target revision is empty. | The target has no changes and no description. | advance.rs |
Hint: Specify bookmark by name to update just one of the bookmarks. | Several moved and no names were given. | advance.rs |
Warning: No matching bookmarks for names: … | An exact name has no local bookmark. | mod.rs |
| Revset parse errors | revsets.bookmark-advance-from doesn't parse. | advance.rs |
8. Worked example
From upstream's test_bookmark_advance_default. A stack
a (bookmark A), b (bookmark B), c,
d, and @ = e. Plain jj bookmark advance:
Upstream's snapshot (source):
Advanced 1 bookmarks to vruxwmqv 7753a73e B | e
Other cases in the same test: jj bookmark advance A moves A straight to
e, since names bypass the from-revset; with
--config=revsets.bookmark-advance-from=A|B both move and the "Specify bookmark by name"
hint appears; run with @ on b, it prints No bookmarks to update.;
and jj bookmark advance B --to A fails with
Refusing to advance bookmark backwards or sideways: B
(source).