jj / How jj bookmark untrack works

How jj bookmark untrack works

jj bookmark untrack unlinks remote bookmarks from the local bookmark of the same name. Both keep their targets; the remote one just becomes a record of the last fetch, and future fetches no longer move the local bookmark.

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. 7. Errors and edge cases
  8. 8. Worked example

In short: jj selects remote bookmarks exactly as jj bookmark track does, by name@remote symbols or by name patterns with an optional --remote. It warns about and skips refs on the reserved git remote and refs that aren't tracked. In one transaction it sets each remaining ref's state to untracked, prints Stopped tracking N remote bookmarks., and records untrack remote bookmark <symbols>.

The command is cmd_bookmark_untrack. Its help text points to jj bookmark forget for removing the local bookmark at the same time (source).

1. Command-line surface

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

BookmarkUntrackArgs

2. Configuration

None of its own. Note that remotes.<name>.auto-track-bookmarks applies to bookmarks new to a fetch, so an untracked bookmark stays untracked on later fetches; upstream's test fetches with auto-tracking on and only the tracked main and the new feature3 are merged (source). The reserved git remote can't be untracked (source, default_ignored_remote_name).

3. End-to-end flow

jj bookmark untrack: call flow cmd_bookmark_untrack loads the workspace and splits arguments into patterns and symbols, rejecting a mix. It selects remote bookmarks the same way as track. Refs on the git remote are skipped with a warning, as are refs not tracked yet. A transaction sets each remaining ref to untracked, prints Stopped tracking N remote bookmarks, and tx.finish records the operation. Local bookmarks are not changed. split args into name patterns / name@remote symbols; reject mixing, and --remote with symbols select remote bookmarks exactly as jj bookmark track does on remote "git" "Git-tracking bookmark cannot be untracked" not tracked "Remote bookmark not tracked yet" tracked kept for untracking start_transaction; untrack_remote_bookmark(symbol) for each: state = New "Stopped tracking N remote bookmarks." tx.finish(ui, "untrack remote bookmark <symbols>")

4. Core architecture touchpoints

StructureRead / writeHowExplained on
RemoteRef.statewriteSet to New (untracked); the target is kept (untrack_remote_bookmark).view
View.local_bookmarksuntouchedThe local bookmark keeps its target, and appears separately from the remote one in listings.view
CommitsuntouchedNothing is created, rewritten or hidden.commits
OperationwriteOne operation if anything was untracked; otherwise Nothing changed.operations

5. Transaction and operation

  • Transaction: always started; with nothing to untrack, tx.finish prints Nothing changed. (source).
  • Operation description: untrack remote bookmark <name@remote, …> (source).
  • Commits: none created or rewritten.
  • jj undo, or jj bookmark track, restores the tracking.

6. Algorithm and invariants

Selection reuses resolve_trackable_remote_bookmarks and trackable_remote_bookmarks_matching, the same helpers as track (source). Because the latter also yields absent placeholders for local bookmarks, those show up as "not tracked yet" warnings rather than silently. Each selected ref is then filtered (source):

  1. On the git remote: warned and skipped. The source notes the restriction could be lifted.
  2. Not tracked: warned and skipped.
  3. Otherwise: untracked, which only flips state.

Invariants: no target changes, local or remote. A locally deleted bookmark can be untracked too; its remote entry then simply remains as an untracked remote bookmark, and pushing no longer deletes it.

7. Errors and edge cases

MessageWhenSource
Cannot specify both <bookmark> patterns and <bookmark>@<remote> symbolsMixed argument forms.untrack.rs
--remote cannot be used with <bookmark>@<remote> symbolsBoth given.untrack.rs
Warning: Git-tracking bookmark cannot be untracked: <symbol>A ref on the git remote.untrack.rs
Warning: Remote bookmark not tracked yet: <symbol>Already untracked, or an absent placeholder.untrack.rs
Warning: No matching remote bookmarks for names: … and the other unmatched warningsAs for track.mod.rs
Nothing changed.Nothing was untracked.cli_util.rs

8. Worked example

From upstream's test_bookmark_track_untrack, continuing the track example. After jj bookmark delete feature2, jj bookmark untrack feature1@origin feature2@origin prints Stopped tracking 2 remote bookmarks. Then the remote moves all three branches to commit 2 and the test fetches:

Fetching after jj bookmark untrack After untracking, feature1 is local at commit 1 with feature1@origin untracked at commit 1; feature2@origin is untracked; main is local and tracked. When the remote moves all three to commit 2 and jj fetches, feature1@origin and feature2@origin move to commit 2 but the local feature1 stays at commit 1; main, still tracked, moves to commit 2. after untrack feature1: commit 1 feature1@origin: commit 1 (untracked) feature2@origin: commit 1 (untracked) main: commit 1 @origin: commit 1 (tracked) after jj git fetch (remote at commit 2) feature1: commit 1 (not moved) feature1@origin: commit 2 feature2@origin: commit 2 main: commit 2 (merged) @origin: commit 2

Upstream's fetch output (source):

bookmark: feature1@origin [updated] untracked
bookmark: feature2@origin [updated] untracked
bookmark: main@origin     [updated] tracked

The test's comment sums it up: "Only tracking bookmark "main" should be merged."