jj / How jj bookmark list works

How jj bookmark list works

jj bookmark list (alias jj b l) prints bookmarks and their targets: local bookmarks, and below each one the remote bookmarks it tracks, filtered by name, revision, remote and state.

On this page
  1. 1. Command-line surface
  2. 2. Configuration
  3. 3. End-to-end flow
  4. 4. Core architecture touchpoints
  5. 5. What it reads, and snapshotting
  6. 6. Algorithm and invariants
  7. Which bookmarks are listed
  8. Grouping local and remote
  9. Sorting
  10. What is printed
  11. 7. Errors and edge cases
  12. 8. Example output

In short: jj walks the view's bookmarks by name, keeps those whose name matches or whose local target is in -r, groups each local bookmark with its tracked remotes, drops remotes that agree with the local target unless asked, sorts the groups, and renders every entry with templates.bookmark_list. Then it prints hints about conflicts and deleted bookmarks. It writes nothing.

The command is cmd_bookmark_list. The help text: "List bookmarks and their targets. By default, a tracked remote bookmark will be included only if its target is different from the local target. An untracked remote bookmark won't be listed" (source). It is registered as List(BookmarkListArgs) with the visible alias l (source, dispatch).

1. Command-line surface

FlagMeaning
NAMES (positional)"Show bookmarks whose local name matches", as glob patterns by default (source).
-r, --revision REVSETS"Show bookmarks whose local targets are in the given revisions" (alias --revisions). "-r deleted_bookmark will not work since deleted_bookmark wouldn't have a local target" (source).
-a, --all-remotes"Show all tracked and untracked remote bookmarks including the ones whose targets are synchronized with the local bookmarks" (alias --all) (source).
--remote REMOTE"Show all tracked and untracked remote bookmarks belonging to this remote"; repeatable, glob by default, conflicts with -a (source).
-t, --tracked"Show tracked remote bookmarks only. This omits local Git-tracking bookmarks by default"; conflicts with -a (source).
-c, --conflicted"Show conflicted bookmarks only"; conflicts with -a (source).
-T, --template TEMPLATERender each entry with this template; all zero-argument methods of CommitRef are keywords (source).
--sort SORT_KEYComma-delimited sort keys, the first most significant; a - suffix sorts descending (source). The keys are name, author-name, author-email, author-date, committer-name, committer-email and committer-date (source).

The global flags are on the CLI page.

2. Configuration

SettingDefaultEffect
templates.bookmark_listformat_commit_ref(self, "bookmark") ++ "\n"Each entry, labelled bookmark_list (default, source). The alias is described under what is printed.
ui.bookmark-list-sort-keys["name"]The sort keys when --sort is not given; it must be an array of key strings (default, source, parser).
ui.pagerThe command requests the pager (source).

3. End-to-end flow

jj bookmark list: call flow After the shared lifecycle, cmd_bookmark_list loads the workspace and snapshots it. It builds a name pattern from NAMES, evaluates -r intersected with bookmarks() to a set of local targets, compiles the template, and builds a remote pattern, where --tracked implies excluding the git remote. collect_items walks the view's bookmarks by name and builds one group per local bookmark with its tracked remotes, plus a group per untracked remote when asked. The groups are sorted, each entry is rendered with the template, and then a warning for unmatched exact names and hints for conflicted and deleted bookmarks are printed. Shared lifecycle: parse args, load config (CLI page) workspace_helper: load the repo at @, snapshot the working copy name pattern from NAMES none with only -r, all with neither -r & bookmarks(all()) → commit ids OR-ed with the names, like jj git push compile the template; remote pattern; RefFilterPredicates --tracked without --remote means every remote but git collect_items(view.bookmarks()): one group per local bookmark + tracked remotes synced remotes dropped unless asked; untracked remotes become their own groups sort the groups by --sort or ui.bookmark-list-sort-keys request the pager; render each group's primary, then its tracked remotes Unmatched names warning, exact names only Conflict hint a listed local is conflicted Deleted hint Git backend only

4. Core architecture touchpoints

PieceRole here
View::bookmarks"Iterates pair of local and remote bookmarks by bookmark name": each name with its local target and its remote refs. See the view.
RefFilterPredicatesThe name and remote matchers, the matched local targets, and the three switches for local-only, synced and untracked refs.
collect_itemsBuilds RefListItems: a primary ref, local or untracked remote, and the remote refs it tracks. Shared with jj tag list.
sortStable multi-key sort; loads commits only when a key needs them.
CommitRefThe template type, made by local, remote or remote_only; its keywords (name, remote, present, conflict, tracked, tracking_ahead_count, synced, …) are in builtin_commit_ref_methods.
default_ignored_remote_nameThe git pseudo-remote on the Git backend, none otherwise; --tracked leaves it out (source). See jj git import.

5. What it reads, and snapshotting

It goes through workspace_helper, so the working copy is snapshotted first unless --ignore-working-copy is given (source). It then reads only the view's bookmarks and the commits the template prints, plus, for -r, the revset evaluation, and for commit-based sort keys, each group's first target commit (source). It starts no transaction and writes no operation.

6. Algorithm and invariants

Which bookmarks are listed

Names and revisions are OR-ed, "like cmd_git_push()": with only -r the name pattern matches nothing, with neither it matches everything (source). The revsets are intersected with bookmarks(all()) "to minimize the lookup space" and evaluated to commit ids; a bookmark is kept if its name matches or any of its local targets is one of them. -c then keeps only bookmarks whose local target is conflicted (source).

Grouping local and remote

For each kept name, the remote refs whose remote matches are split into tracked and untracked. A tracked ref whose target equals the local target is dropped unless -t, -a or --remote is given. A group, the local ref followed by the remaining tracked refs, is emitted if the local bookmark is present and local-only refs are wanted, or if any tracked ref remains, which is how a deleted local bookmark still shows with its remote. With -a or --remote, each untracked remote ref is its own group, printed as name@remote (source).

FlagsLocal-onlySynced trackedUntracked
noneyesnono
-ayesyesyes
--remote Rnoyesyes
-tnoyesno

The three switches are set at list.rs.

Sorting

The view yields names in order, so the input is already sorted by name; the sort runs the keys from least to most significant with stable sorts, skipping trailing name keys (source). name compares the name, then the remote. The commit keys use the group's first present target; a group without one sorts as missing, which comes before any commit in ascending order (source). Tracked remote refs always stay under their local bookmark.

What is printed

The default template calls format_commit_ref (source). A local bookmark prints name: and its commit summary, or (deleted) when absent. A tracked remote prints indented as @remote, with "(ahead by N commits, behind by N commits)" against the local target, or "(not created yet)" when the remote ref is absent (source). An untracked remote prints name@remote: . A conflicted target prints (conflicted): and one line per removed (-) and added (+) commit, and a term with no commit prints (absent) (source).

7. Errors and edge cases

SituationBehaviour
An exact name that matches no local or remote bookmark"Warning: No matching bookmarks for names: …"; glob patterns never warn (source).
A listed local bookmark is conflicted"Hint: Some bookmarks have conflicts. Use jj bookmark set <name> -r <rev> to resolve." (source, test). See jj bookmark set.
A listed bookmark is deleted locally but tracked on a real remote"Hint: Bookmarks marked as deleted can be *deleted permanently* on the remote by running jj git push --deleted. Use jj bookmark forget if you don't want that." (source). See jj git push and jj bookmark forget.
Deleted, tracked only by the git pseudo-remote"Hint: Bookmarks marked as deleted will be deleted from the underlying Git repo on the next jj git export." Only one of the two hints is printed, and neither off the Git backend (source).
An unknown --sort keyRejected by clap with the list of possible values, exit status 2 (test).
ui.bookmark-list-sort-keys not an array of keysA config error: "Expected an array of sort keys as strings" or "Expected sort key as a string" (source).
-a with --remote, -t or -cRejected by clap (source).
-r naming a deleted bookmarkIt has no local target, so it cannot be found that way; list it by name instead (source).

8. Example output

After deleting, tracking, untracking and moving bookmarks against a clone, upstream's test shows the default listing (source):

$ jj bookmark list
absent-tracked: wqnwkozp 0353dd35 (empty) local-only
  @origin (not created yet)
local-only: wqnwkozp 0353dd35 (empty) local-only
remote-delete (deleted)
  @origin: vruxwmqv b32031cf (empty) remote-delete
remote-sync: rlvkpnrz 7a07dbee (empty) remote-sync
remote-unsync: wqnwkozp 0353dd35 (empty) local-only
  @origin (ahead by 1 commits, behind by 1 commits): zsuskuln 553203ba (empty) remote-unsync
Hint: Bookmarks marked as deleted can be *deleted permanently* on the remote by running `jj git push --deleted`. Use `jj bookmark forget` if you don't want that.

--all-remotes adds the synced remote-sync@origin under its local bookmark and the untracked remote-untrack@origin as its own entry (source):

$ jj bookmark list --all-remotes
…
remote-sync: rlvkpnrz 7a07dbee (empty) remote-sync
  @origin: rlvkpnrz 7a07dbee (empty) remote-sync
remote-unsync: wqnwkozp 0353dd35 (empty) local-only
  @origin (ahead by 1 commits, behind by 1 commits): zsuskuln 553203ba (empty) remote-unsync
remote-untrack@origin: royxmykx 149bc756 (empty) remote-untrack

A custom template sorted by two keys (source):

$ jj bookmark list -T 'name ++ ": " ++ if(normal_target, normal_target.committer().email()) ++ "\n"' \
    --sort committer-email,committer-date-
b: alice@g.c
a: bob@g.c
c: bob@g.c

See also jj bookmark track and the view.