jj / How jj bookmark track works

How jj bookmark track works

jj bookmark track links remote bookmarks to the local bookmark of the same name. The remote target is merged into the local one, creating it if needed, and from then on fetches update the local bookmark and pushes compare against the remote.

On this page
  1. 1. Command-line surface
  2. 2. Configuration
  3. 3. End-to-end flow
  4. 4. Core architecture touchpoints
  5. 5. Transaction and operation
  6. 6. Algorithm and invariants
  7. Selecting remote bookmarks
  8. Tracking is a merge
  9. 7. Errors and edge cases
  10. 8. Worked example

In short: jj takes either exact name@remote symbols or name patterns, optionally limited with --remote (default: every remote except the reserved git). It collects the matching remote bookmarks, including not-yet-existing ones for local bookmarks on a remote, and skips those already tracked with a warning. In one transaction it calls track_remote_bookmark on each: a three-way merge of the remote target into the local bookmark, then the state is set to tracked. It records track remote bookmark <symbols> and lists any bookmark left conflicted.

The command is cmd_bookmark_track. Its inverse is jj bookmark untrack. Bookmarks can also become tracked automatically on fetch through remotes.<name>.auto-track-bookmarks (see git fetch). How RemoteRef stores the tracking state is on the view page.

1. Command-line surface

ArgumentTypeDefaultMeaning
BOOKMARK[@REMOTE] (positional)patterns or remote symbols, required—Either name patterns (globs by default) or exact name@remote symbols, but not both in one call (source, parse_name_patterns_or_remote_symbols).
--remote REMOTEremote patterns, repeatableall remotes but gitWhich remotes the name patterns apply to. Not allowed with name@remote symbols (source).

BookmarkTrackArgs

2. Configuration

templates.bookmark_list formats the listing of bookmarks left conflicted (source). In a Git-backed repo the reserved git remote is excluded from pattern matches unless --remote names it (source, default_ignored_remote_name).

3. End-to-end flow

jj bookmark track: call flow cmd_bookmark_track loads the workspace and splits its arguments into name patterns and remote symbols, rejecting a mix and --remote with symbols. Symbols are resolved exactly; patterns are matched against remote bookmarks, plus absent entries for local bookmarks on matching remotes, warning about unmatched names and remotes. Already-tracked refs are skipped with a warning. A transaction calls track_remote_bookmark for each, prints Started tracking N remote bookmarks, and tx.finish records the operation. Finally it lists bookmarks whose local target ended up conflicted. split args into name patterns / name@remote symbols; reject mixing, and --remote with symbols name@remote symbols resolve_trackable_remote_bookmarks: remote present, or local present on a known remote; else warn name patterns (+ --remote) trackable_remote_bookmarks_matching: existing remote bookmarks + absent ones for local bookmarks on matching remotes; warn on unmatched already tracked → "Warning: Remote bookmark already tracked: …"; the rest are kept start_transaction; track_remote_bookmark(symbol) for each merge_local_bookmark(local, base = tracked_target (absent), other = remote target) then remote_ref.state = Tracked "Started tracking N remote bookmarks."; tx.finish(ui, "track remote bookmark <symbols>") then list any of those bookmarks whose local target is now conflicted

4. Core architecture touchpoints

StructureRead / writeHowExplained on
RemoteRef.statewriteSet to Tracked. An absent remote bookmark can be tracked too, which is how a local bookmark gets ready to be pushed to a new remote (source).view
View.local_bookmarkswriteThree-way merged with the remote target; created if absent, conflicted if both sides differ (merge_local_bookmark).view
IndexreadAncestry, used by merge_ref_targets to resolve the merge.index
CommitsuntouchedA new local bookmark adds its target as a head if needed.commits
OperationwriteOne operation if anything was tracked; otherwise Nothing changed.operations

5. Transaction and operation

  • Transaction: always started; when nothing was selected, tx.finish finds no changes and prints Nothing changed. (source, test).
  • Operation description: track remote bookmark <name@remote, …> (source).
  • Commits: none created or rewritten.
  • jj undo restores the previous local target and tracking state.

6. Algorithm and invariants

Selecting remote bookmarks

With name@remote symbols, each is accepted if the remote bookmark exists, or if a local bookmark of that name exists and the remote is known; others are reported with No matching remote bookmarks for names (source). With patterns, jj takes every existing remote bookmark matching both name and remote patterns, plus, for every matching remote, an absent placeholder for each matching local bookmark the remote doesn't have (source). Mixing the two forms is rejected because a --remote or default would apply to only half the arguments (source).

Tracking is a merge

track_remote_bookmark merges with base tracked_target(), which is absent for an untracked ref (source), and other = the remote target. So:

  • No local bookmark: the local bookmark is created at the remote target.
  • Local bookmark at the same commit: unchanged.
  • Local bookmark elsewhere: the result is a conflicted bookmark with both targets, which jj then lists.

Invariants: tracking never moves the remote target. A tracked remote bookmark and the local bookmark of the same name are thereafter treated as one: fetches merge remote changes into the local bookmark, and pushes use the tracked target as the expected remote state.

7. Errors and edge cases

MessageWhenSource
Cannot specify both <bookmark> patterns and <bookmark>@<remote> symbolsMixed argument forms (exit status 2).track.rs
--remote cannot be used with <bookmark>@<remote> symbolsBoth given.track.rs
Warning: Remote bookmark already tracked: <symbol>Skipped; not an error.track.rs
Warning: No matching remote bookmarks for names: …A symbol names nothing trackable.mod.rs
Warning: No matching bookmarks for names: … / No matching remotes for names: …Exact names or remotes that don't exist.mod.rs
Nothing changed.Nothing new to track.cli_util.rs

8. Worked example

From upstream's test_bookmark_track_untrack. A fetch without auto-tracking leaves feature1@origin, feature2@origin and main@origin untracked, all at commit 1, with no local bookmarks. Then jj bookmark track feature1@origin main@origin nonexistent@origin, and later, after creating a local feature2 at @, jj bookmark track feature2:

jj bookmark track creating and conflicting local bookmarks Before: three untracked remote bookmarks at commit 1 and no local ones. Tracking feature1@origin and main@origin creates local feature1 and main at commit 1; nonexistent@origin is warned about. Tracking feature2 when a local feature2 already points at @ produces a conflicted feature2 with both @ and commit 1 as targets. after fetch feature1@origin (untracked) feature2@origin (untracked) main@origin (untracked) no local bookmarks track feature1@origin main@origin … feature1: commit 1 (created) main: commit 1 (created) feature2@origin still untracked warning for nonexistent@origin create feature2 at @, then track feature2 feature2 (conflicted): + @ e8849ae1 + commit 1 bd843888 @origin: commit 1

Upstream's output for the first call (source):

Warning: No matching remote bookmarks for names: nonexistent@origin
Started tracking 2 remote bookmarks.

jj bookmark track feature3 --remote=origin, for a name that exists nowhere, prints Warning: No matching bookmarks for names: feature3 and Nothing changed.