jj / How jj tag untrack works

How jj tag untrack works

jj tag untrack unlinks remote tags from the local tag of the same name. Both keep their targets, but later fetches only update the remote record, not the local tag.

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: it is jj bookmark untrack for tags. Selection matches jj tag track; refs on the git remote and refs that aren't tracked are skipped with a warning; each remaining ref's state is set to untracked in one transaction, recorded as untrack remote tag <symbols>.

The command is cmd_tag_untrack. Its help text: an untracked remote tag "is just a pointer to the last-fetched remote tag" (source).

1. Command-line surface

ArgumentTypeDefaultMeaning
TAG[@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 (source).

TagUntrackArgs

2. Configuration

None. Tags on the reserved git remote can't be untracked (source, default_ignored_remote_name).

3. End-to-end flow

jj tag untrack: call flow cmd_tag_untrack loads the workspace, splits arguments into patterns and symbols, and selects remote tags the same way as tag track. Refs on the git remote and refs not tracked yet are skipped with warnings. A transaction sets each remaining ref to untracked, prints Stopped tracking N remote tags, and tx.finish records the operation. split args; select remote tags exactly as jj tag track does on remote "git" "Git-tracking tag cannot be untracked" not tracked "Remote tag not tracked yet" tracked kept for untracking start_transaction; untrack_remote_tag(symbol) for each: state = New "Stopped tracking N remote tags." tx.finish(ui, "untrack remote tag <symbols>") later fetches update the untracked remote tag only; the local tag stays put

4. Core architecture touchpoints

StructureRead / writeHowExplained on
Remote tag RemoteRef.statewriteSet to New (untracked); the target is kept (untrack_remote_tag).view
View.local_tagsuntouchedThe local tag keeps its target.view
Commitsuntouched—commits
OperationwriteOne operation if anything was untracked; otherwise Nothing changed.operations

5. Transaction and operation

  • Transaction: always started; nothing to untrack gives Nothing changed. (source).
  • Operation description: untrack remote tag <name@remote, …> (source).
  • Commits: none.
  • jj undo or jj tag track restores the tracking.

6. Algorithm and invariants

Selection reuses the tag helpers (source), then each ref is filtered: git remote refs are refused ("This restriction can be lifted if we want to support untracked @git tags"), untracked ones are reported, and the rest flip to untracked (source). A tag deleted locally can still be untracked; its remote entry stays as an untracked remote tag.

Invariants: no target changes, local or remote.

7. Errors and edge cases

MessageWhenSource
Cannot specify both <tag> patterns and <tag>@<remote> symbols / --remote cannot be used with <tag>@<remote> symbolsMixed or conflicting arguments.untrack.rs
Warning: Git-tracking tag cannot be untracked: <symbol>A ref on the git remote.untrack.rs
Warning: Remote tag not tracked yet: <symbol>Already untracked, or an absent placeholder.untrack.rs
Unmatched-name and unmatched-remote warningsAs for tag track.mod.rs
Nothing changed.Nothing was untracked.cli_util.rs

8. Worked example

From upstream's test_tag_track_untrack. After cloning, tag1–tag4 are tracked on origin. The test deletes local tag3, then runs jj tag untrack 'tag[234]':

jj tag untrack 'tag[234]' Before: tag1 to tag4 are local and tracked on origin, except tag3 which was deleted locally. After untracking tag2, tag3 and tag4: tag1 is unchanged; tag2 and tag4 keep their local tags, and their origin entries are listed separately as untracked; tag3 remains only as an untracked origin tag. before (after tag delete tag3) tag1local, @origin tracked tag2local, @origin tracked tag3(deleted), @origin tracked tag4local, @origin tracked after untrack 'tag[234]' tag1local, @origin tracked tag2local; tag2@origin untracked tag3only tag3@origin, untracked tag4local; tag4@origin untracked

Upstream's output (source):

Stopped tracking 3 remote tags.

When the remote later moves all four tags and the test fetches, the output shows tag: tag2@origin [updated] untracked and the local tag2 stays on commit 1, while tracked tags follow the remote (source).