In short: jj resolves the target (--to, default @). It collects the local
bookmarks whose names match the patterns (all of them if none are given), keeping those that point
into the --from revsets when that is given. It drops bookmarks already at the target.
If nothing is left it prints No bookmarks to update. and stops. Otherwise every match
must pass the fast-forward check unless --allow-backwards is set. Then, in one
transaction, jj points them all at the target. The operation is
point bookmark <names> to commit <id>.
The command is cmd_bookmark_move. Its help text
says: "Unlike `jj bookmark set`, this command cannot create new bookmarks"
(source). For setting bookmarks by exact name, creating them
if needed, see jj bookmark set.
Bookmark storage is on the view page.
1. Command-line surface
| Flag | Type | Default | Meaning |
|---|---|---|---|
NAMES (positional) | name patterns | none (all bookmarks) | Bookmarks to move. Patterns are globs by default, and other string-pattern kinds work too. Several patterns are unioned (source, parse_union_name_patterns). |
-f, --from REVSETS | revsets, repeatable | none | Only move bookmarks that currently point into these revisions. Several are unioned (source). |
-t, --to REVSET | revset | @ | The target, exactly one commit (source). Unlike create and set, there's no -r here. |
-B, --allow-backwards | bool | off | Allow backwards or sideways moves (source). |
NAMES and --from form a required clap group: at least one must be given
(source). So jj bookmark move alone is a usage error,
even though "move every bookmark" would be a possible meaning, as the
test notes.
2. Configuration
The command reads no settings of its own. It never creates bookmarks, so the auto-track settings
used by create and set don't apply. Revset aliases from
[revset-aliases] work in --from and --to, as in any revset.
3. End-to-end flow
4. Core architecture touchpoints
| Structure | Read / write | How | Explained on |
|---|---|---|---|
View.local_bookmarks | read, write | Iterated by name matcher, then each match is set to RefTarget::normal(target). A conflicted bookmark becomes a normal one. | view |
| Revset engine | read | --from is evaluated once into a membership test (containing_fn), then checked against each bookmark's present adds (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 if nothing matched. | operations |
5. Transaction and operation
- Transaction: one, after selection and the fast-forward check (source). When nothing is left to move, the command returns before starting one, so no operation is recorded.
- Operation description:
point bookmark <name1, name2, …> to commit <full hex id>, naming only the bookmarks that actually move (source). It is the same textbookmark setuses. - Commits created or rewritten: none.
- Working copy: snapshotted at the start, and otherwise untouched.
6. Algorithm and invariants
Selection (source) is an intersection. The name side is the
union of the patterns, or every name when there are none. The --from side keeps a
bookmark if any of its present targets lies in the union of the --from revsets,
or keeps everything when --from isn't given. Only local bookmarks are considered. A
deleted bookmark, with only remote bookmarks left, can't be moved, since it has no local entry.
The unmatched-name warning (warn_unmatched_local_bookmarks)
covers only exact names in the pattern expression that have no local bookmark. A glob like
'a?' that matches nothing gives no warning. A name that exists but is filtered out by
--from gives no warning either.
The fast-forward check is the same is_fast_forward as bookmark set: some
present side of the old target must be an ancestor of the new one. It runs over every match, and the
first failure aborts the whole command (source).
Invariants: never creates or deletes a bookmark. Never rewrites a commit. Never moves
backwards or sideways without -B. Either every match moves, or none does.
7. Errors and edge cases
| Message | When | Source |
|---|---|---|
error: the following required arguments were not provided: <NAMES|--from <REVSETS>> | Neither names nor --from were given. Clap exits with status 2. | move.rs |
No bookmarks to update. | Nothing matched, or every match is already at the target. This is a status message, not an error. | move.rs |
Warning: No matching bookmarks for names: <names> | Exact names that have no local bookmark. | mod.rs |
Refusing to move bookmark backwards or sideways: <name>, hint Use --allow-backwards to allow it. | The first match that isn't a fast-forward. Nothing moves. | move.rs |
Warning: Target revision is empty. | The target has no changes and no description. | move.rs |
Hint: Specify bookmark by name to update just one of the bookmarks. | More than one bookmark moved and no names were given. | move.rs |
Failed to parse name pattern: … | A name pattern doesn't parse. | revset_util.rs |
8. Worked example
From upstream's test_bookmark_move_matching. Bookmarks
a1 and a2 sit on one branch. b1 and c1 sit on
another, under @ (described head2). Then
jj bookmark move --from=::@ moves every bookmark in @'s ancestry up to
@:
Upstream's snapshot:
$ jj bookmark move --from=::@
Moved 2 bookmarks to vruxwmqv 0dd9a4b1 b1 c1 | (empty) head2
Hint: Specify bookmark by name to update just one of the bookmarks.
a1 and a2 aren't in ::@, so they aren't selected. The hint
appears because two bookmarks moved and no names were given. In the same
test, jj bookmark move '?1' selects
a1, b1 and c1 by glob. It fails with
Refusing to move bookmark backwards or sideways: a1, because a1's
commit isn't an ancestor of @, and nothing moves at all.