In short: jj resolves the target (default @) and checks every name. A present local
bookmark is an error, and so is a name with tracked remote bookmarks. It warns if the target is
empty. In one transaction it sets each bookmark to point at the target. For every configured remote
whose auto-track pattern matches the name, and which jj knows about, it also tracks
name@remote. The operation is
create bookmark <names> pointing to commit <id>.
The command is cmd_bookmark_create. It is the strict
sibling of jj bookmark set, which
creates or moves. To move existing bookmarks by where they point, see
jj bookmark move. How bookmarks are
stored, including conflicted targets and remote tracking, is on the
view page.
1. Command-line surface
| Flag | Type | Default | Meaning |
|---|---|---|---|
NAMES (positional) | bookmark names, one or more | required | The bookmarks to create. Each is parsed as a revset symbol, so names needing quotes must be quoted (source, parse_bookmark_name). |
-r, --revision, --to REVSET | revset | @ | The target, exactly one commit. The --to alias matches set and move (source). |
2. Configuration
| Key | Default | Effect |
|---|---|---|
remotes.<name>.auto-track-bookmarks | unset | A string pattern. New bookmarks whose names match are tracked on that remote. It also applies to bookmarks fetched later (docs). |
remotes.<name>.auto-track-created-bookmarks | unset | Like the above, but only for bookmarks created with bookmark create/set. When both are set, a name matching either one is tracked (parse_remote_auto_track_bookmarks_map_for_new_bookmarks). |
A remote only counts if the repository already has a view of it, for example after
jj git remote add. Patterns for unknown remotes are ignored
(source), as upstream's
test_create_and_set_auto_track_bookmarks checks.
3. End-to-end flow
4. Core architecture touchpoints
| Structure | Read / write | How | Explained on |
|---|---|---|---|
View.local_bookmarks | read, write | Checked for an existing entry, then each name is set to RefTarget::normal(target). | view |
View.remote_views (remote bookmarks) | read, maybe write | Checked for tracked remote bookmarks of the same name. With auto-tracking, the remote bookmark's state becomes Tracked, and its target is merged into the local one (track_remote_bookmark). | view |
View.head_ids | maybe write | Setting a bookmark adds its target as a head, so pointing a bookmark at a hidden commit makes it visible again (set_local_bookmark_target). | view |
| Target commit | read | Resolved, and checked for being empty and undescribed. It is not modified. | commits |
| Operation | write | One operation, described below. | operations |
5. Transaction and operation
- Transaction: one, after all names pass their checks (source). Any failing name aborts the whole command before anything is written.
- Operation description:
create bookmark <name1, name2, …> pointing to commit <full hex id>(source). Upstream's duplicate test shows one in an undo message:create bookmark c pointing to commit 387b9287…. - Commits created or rewritten: none.
- Working copy: snapshotted at the start, and otherwise untouched.
jj undoremoves the bookmarks again, and undoes any auto-tracking.
6. Algorithm and invariants
Each name is checked before anything changes (source). A
present local bookmark means the name is taken. A deleted local bookmark whose remote
bookmarks are still tracked also counts as taken. jj treats re-pointing it as resurrection, which
belongs to bookmark set. Untracked remote bookmarks don't block creation. Creation
itself is an unconditional overwrite with a single-target RefTarget. No fast-forward
rule applies, since there's no previous target.
Auto-tracking runs per name and per remote (source). The
per-remote matchers are the union of auto-track-bookmarks and
auto-track-created-bookmarks. Remotes without a view in the repository are skipped.
track_remote_bookmark merges the remote bookmark's current target into the new local
one (source). The merge base is the remote bookmark's tracked target, which is absent for a bookmark that wasn't tracked. So if name@remote already points
elsewhere, the new local bookmark can end up conflicted, which is why jj warns first.
Invariants: never overwrites an existing bookmark. Never rewrites a commit. Either every requested bookmark is created, or none is.
7. Errors and edge cases
| Message | When | Source |
|---|---|---|
Bookmark already exists: <name>, hint Use `jj bookmark set` to update it. | A local bookmark of that name is present. | create.rs |
Tracked remote bookmarks exist for deleted bookmark: <name>, with hints to use bookmark set or bookmark untrack | No local bookmark, but name@remote is tracked somewhere, ignoring the git pseudo-remote. | create.rs, cli_util.rs |
Warning: Target revision is empty. | The target is discardable: no changes and no description. | create.rs |
Warning: Auto-tracking bookmark that exists on the remote: <name>@<remote> | An auto-tracked remote already has a bookmark with that name. | create.rs |
Failed to parse bookmark name: … (clap: invalid value) | The name isn't a valid revset symbol, for example empty. | revset_util.rs |
Revset `…` resolved to more than one revision (or none) | -r isn't exactly one commit. | revset_util.rs |
8. Worked example
From upstream's test_bookmark_multiple_names and
test_bookmark_move. In a fresh repository,
jj bookmark create -r@ foo bar creates two bookmarks on the empty working-copy commit.
Later, after foo was pushed and then deleted locally, jj bookmark create foo
is refused:
Upstream's snapshots of the two runs:
Warning: Target revision is empty.
Created 2 bookmarks pointing to qpvuntsm e8849ae1 bar foo | (empty) (no description set)
Error: Tracked remote bookmarks exist for deleted bookmark: foo
Hint: Use `jj bookmark set` to recreate the local bookmark. Run `jj bookmark untrack foo` to disassociate them.
After jj bookmark untrack foo and jj bookmark delete foo, the same
create succeeds, and foo@origin stays as an untracked remote bookmark.