In short: it is jj bookmark track for
tags. jj takes name@remote symbols or name patterns (with an optional --remote,
default every remote except git), skips refs already tracked, and in one transaction calls
track_remote_tag on each: a three-way merge of the remote target into the local tag, then the
state becomes tracked. It records track remote tag <symbols>. Unlike the bookmark
command, it doesn't list conflicted results afterwards.
The command is cmd_tag_track. Remote tags fetched by
jj git fetch or jj git clone are tracked by default, as upstream's test shows, so this
command is mainly for tags that were untracked (source). Its inverse is
jj tag untrack.
1. Command-line surface
| Argument | Type | Default | Meaning |
|---|---|---|---|
TAG[@REMOTE] (positional) | patterns or remote symbols, required | — | Name patterns (globs by default) or exact name@remote symbols, not both (source, parse_name_patterns_or_remote_symbols). |
--remote REMOTE | remote patterns, repeatable | all remotes but git | Which remotes the patterns apply to; not allowed with symbols (source). |
2. Configuration
None of its own. The reserved git remote is excluded from pattern matches unless
--remote names it (source,
default_ignored_remote_name).
3. End-to-end flow
4. Core architecture touchpoints
| Structure | Read / write | How | Explained on |
|---|---|---|---|
Remote tag RemoteRef.state | write | Set to Tracked (track_remote_tag). | view |
View.local_tags | write | Three-way merged with the remote target: created if absent, conflicted if both sides differ (merge_local_tag). | view |
| Immutable set | may change | A newly created local tag adds its commit to tags(). | revsets |
| Operation | write | One operation if anything was tracked; otherwise Nothing changed. | operations |
5. Transaction and operation
- Transaction: always started; with nothing new to track,
tx.finishprintsNothing changed.(source). - Operation description:
track remote tag <name@remote, …>(source). - Commits: none created or rewritten.
jj undorestores the previous local target and tracking state.
6. Algorithm and invariants
Selection mirrors bookmarks exactly: resolve_trackable_remote_tags
accepts a symbol if the remote tag exists, or a local tag of that name exists and the remote is known;
trackable_remote_tags_matching adds absent placeholders for local
tags missing on a matching remote. Tracking then merges with base tracked_target(), absent for an
untracked ref, so a missing local tag is created, an equal one is unchanged, and a different one becomes
conflicted (source).
Invariants: remote targets never change. Tracked refs are skipped with a warning, never re-merged.
7. Errors and edge cases
| Message | When | Source |
|---|---|---|
Cannot specify both <tag> patterns and <tag>@<remote> symbols | Mixed argument forms. | track.rs |
--remote cannot be used with <tag>@<remote> symbols | Both given. | track.rs |
Warning: Remote tag already tracked: <symbol> | Skipped. | track.rs |
Warning: No matching tags for names: … / No matching remotes for names: … / No matching remote tags for names: … | Unknown names, remotes or symbols. | mod.rs |
Nothing changed. | Nothing new to track. | cli_util.rs |
8. Worked example
From upstream's test_tag_track_untrack. A clone tracked
tag1–tag4 from origin. Then tag2–tag4 were untracked
(and local tag3 deleted), a local tag5 was created, and local tag4 was moved to
a new commit. Now jj tag track 'tag[1345]' unknown:
Upstream's output (source):
Warning: No matching tags for names: unknown
Warning: Remote tag already tracked: tag1@origin
Started tracking 3 remote tags.