jj / How jj tag list works

How jj tag list works

jj tag list (alias jj tag l) prints tags and their targets, with the remote tags each local tag tracks below it, 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 tags are listed
  8. The git pseudo-remote
  9. What is printed
  10. 7. Errors and edge cases
  11. 8. Example output

In short: it is jj bookmark list over the view's tags. The same collect_items groups each local tag with its tracked remote tags, the same sort orders them, and each entry is rendered with templates.tag_list. Unlike the bookmark command it prints no hints afterwards, and its unmatched-name warning only looks at local tags. It writes nothing.

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

1. Command-line surface

FlagMeaning
NAMES (positional)"Show tags whose local name matches", as glob patterns by default (source).
-r, --revision REVSETS"Show tags whose local targets are in the given revisions" (alias --revisions); "-r deleted_tag will not work" (source). The help suggests -r 'REV::' for tags on descendants of REV, "similar to git tag --contains REV", and -r '::REV' for ancestors, "similar to git tag --merged REV" (source).
-a, --all-remotes"Show all tracked and untracked remote tags including the ones whose targets are synchronized with the local tags" (alias --all) (source).
--remote REMOTE"Show all tracked and untracked remote tags belonging to this remote"; repeatable, glob by default, conflicts with -a (source).
-t, --tracked"Show tracked remote tags only. This omits local Git-tracking tags by default"; conflicts with -a (source).
-c, --conflicted"Show conflicted tags 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 same keys as jj bookmark list (source).

The global flags are on the CLI page.

2. Configuration

SettingDefaultEffect
templates.tag_listformat_commit_ref(self, "tag") ++ "\n"Each entry, labelled tag_list (default, source). format_commit_ref is described on the bookmark list page; here names carry the tag label.
ui.tag-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 tag list: call flow After the shared lifecycle, cmd_tag_list loads the workspace and snapshots it. It builds a name pattern from NAMES, evaluates -r intersected with tags() to a set of local targets, builds a remote pattern where --tracked implies excluding the git remote, compiles the template and reads the sort keys. collect_items walks the view's tags by name into groups of a local tag and its tracked remotes, the groups are sorted, each entry is rendered, and a warning names exact patterns that match no local tag. 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 & tags(all()) → commit ids OR-ed with the names, like jj git push remote pattern; compile the template; sort keys; RefFilterPredicates --tracked without --remote means every remote but git collect_items(view.tags()), then sort shared with jj bookmark list: one group per local tag plus its tracked remotes request the pager; render each group's primary, then its tracked remotes warn_unmatched_local_tags: exact names with no local tag no conflict or deleted-tag hints

4. Core architecture touchpoints

PieceRole here
View::tags"Iterates pair of local and remote tags by tag name". See the view.
collect_itemsThe grouping and filtering, identical to jj bookmark list.
sortStable multi-key sort; loads commits only when a key needs them (details).
CommitRefThe template type, with the keywords in builtin_commit_ref_methods.
default_ignored_remote_nameThe git pseudo-remote on the Git backend; --tracked leaves it out (source).

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 the view's tags, the commits the template prints, the revset evaluation for -r, 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 tags are listed

Names and revisions are OR-ed "like cmd_git_push()"; the revsets are intersected with tags(all()) and matched against local targets only (source). The predicates are set exactly as for bookmarks: local-only tags unless -t or --remote; synced tracked remotes with -t, -a or --remote; untracked remotes with -a or --remote but not -t (source). The full table is on the bookmark list page.

The git pseudo-remote

In a colocated repository each tag also has a ref on the git remote, which is tracked. While it agrees with the local tag it is hidden by default and shown by -a or --remote=git; --tracked alone excludes it, so it printed nothing in upstream's test (test). Remote tags from a real remote are tracked by default; see jj tag track.

What is printed

Each group prints its primary ref and then its tracked remote refs, through the template (source). With the default template a conflicted tag prints (conflicted): with - and + lines for its removed and added targets, (absent) for a term with no commit (source).

7. Errors and edge cases

SituationBehaviour
An exact name with no local tag"Warning: No matching tags for names: …". Only local tags count, unlike jj bookmark list, which also accepts a remote one; glob patterns never warn (source, test).
A conflicted tagListed with its targets, with no hint (test). See jj tag set.
A deleted local tag still tracked on a remoteListed as name (deleted) with its remote below, as for bookmarks, but no hint is printed (source).
A conflicted tag and normal_target in a custom template<Error: No value set to Option<Commit>>, as there is no single target (test).
An unknown --sort key, or a bad ui.tag-list-sort-keysRejected by clap, or a config error such as "Expected an array of sort keys as strings" (source).
-a with --remote, -t or -cRejected by clap (source).

8. Example output

Three tags, one of them conflicted by concurrent operations, from upstream's test (source):

$ jj tag list
conflicted_tag (conflicted):
  - rlvkpnrz 893e67dc (empty) commit1
  + zsuskuln 76abdd20 (empty) commit2
  + royxmykx 13c4e819 (empty) commit3
test_tag: rlvkpnrz 893e67dc (empty) commit1
test_tag2: zsuskuln 76abdd20 (empty) commit2

A revset and a name, which are not intersected (source), and newest first by committer date (source):

$ jj tag list -r 'subject(commit1)' test_tag2
test_tag: rlvkpnrz 893e67dc (empty) commit1
test_tag2: zsuskuln 76abdd20 (empty) commit2

$ jj tag list --sort=committer-date-,name
conflicted_tag (conflicted):
  - rlvkpnrz 893e67dc (empty) commit1
  + zsuskuln 76abdd20 (empty) commit2
  + royxmykx 13c4e819 (empty) commit3
test_tag2: zsuskuln 76abdd20 (empty) commit2
test_tag: rlvkpnrz 893e67dc (empty) commit1

In a colocated repository, --all-remotes shows the git pseudo-remote (source):

$ jj tag list --all-remotes
local-only: rlvkpnrz d3e8d245 (empty) local-only
  @git: rlvkpnrz d3e8d245 (empty) local-only

See also jj tag set and jj tag delete.