jj / How jj bookmark forget works

How jj bookmark forget works

jj bookmark forget removes a bookmark locally without scheduling a deletion on any remote. It deletes the local target and untracks the remote bookmarks, or with --include-remotes removes jj's record of them altogether.

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 matches the patterns against every bookmark name, local or remote. In one transaction, for each match it sets the local target to absent. For each of its remote bookmarks it either forgets it (--include-remotes) or marks it untracked, except the special git remote, which is left alone. It prints how many local and remote bookmarks were forgotten and records forget bookmark <names>.

The command is cmd_bookmark_forget. Compare jj bookmark delete, which keeps remote bookmarks tracked so the deletion gets pushed. The help text explains the untracking: it ensures "the forgotten bookmark will not impact remotes on future pushes" (source).

1. Command-line surface

FlagTypeDefaultMeaning
NAMES (positional)name patterns, required—Bookmarks to forget, matched against local and remote names; globs by default (source).
--include-remotesboolfalseForget the matching remote bookmarks too, instead of untracking them. A later fetch recreates them if they still exist on the remote (source).

BookmarkForgetArgs

2. Configuration

None of its own. In a Git-backed repo, remote bookmarks under the reserved remote name git (the colocated Git repo's own refs) can't be untracked, so they're skipped unless --include-remotes is given (source, default_ignored_remote_name). remotes.<name>.auto-track-bookmarks matters afterwards: a re-fetched bookmark may be tracked again automatically.

3. End-to-end flow

jj bookmark forget: call flow cmd_bookmark_forget loads the workspace and finds forgettable bookmarks: all bookmark names, local or remote, matching the patterns, warning about exact names that exist nowhere. With none it prints No bookmarks to forget. Otherwise, in a transaction, each match has its local target set to absent; each of its remote refs is forgotten with --include-remotes, skipped if it belongs to the git remote, or untracked otherwise. It prints the counts and tx.finish records forget bookmark. Shared lifecycle: load workspace and repo, snapshot the working copy (CLI page) find_forgettable_bookmarks: view.bookmarks() (local and remote) filtered by the patterns none → "No bookmarks to forget." for each match: set_local_bookmark_target(name, absent) counted as local only if it had a local target --include-remotes set_remote_bookmark (absent): forgotten remote "git" skipped; it can't be untracked otherwise untrack_remote _bookmark "Forgot N local bookmarks." / "Forgot M remote bookmarks."; tx.finish(ui, "forget bookmark <names>")

4. Core architecture touchpoints

StructureRead / writeHowExplained on
View.local_bookmarkswriteSet to absent for every match (source).view
Remote bookmarks (RemoteRef)writeEither replaced by RemoteRef::absent(), or kept with state set to New, meaning untracked (set_remote_bookmark, untrack_remote_bookmark).view
View.git_refsuntouchedUpstream's test notes that keeping the Git-tracking record is why a forgotten bookmark isn't resurrected by jj git export then import (source).view
CommitsuntouchedNothing is abandoned.commits
OperationwriteOne operation if anything matched.operations

5. Transaction and operation

  • Transaction: one, only when something matched (source).
  • Operation description: forget bookmark <name1, name2, …>, listing every matched name (source).
  • Commits: none created or rewritten.
  • jj undo restores the bookmarks and their tracking state.

6. Algorithm and invariants

find_forgettable_bookmarks iterates view.bookmarks(), which yields each bookmark name with its local target and all its remote refs, and keeps names the pattern matcher accepts. So a bookmark that only exists remotely, or a deleted one, still matches, unlike with delete. The unmatched-name warning counts a name as present if it exists locally or on any remote (source).

For each match (source):

  1. The local target is set to absent; it counts toward "local bookmarks" only if it was present.
  2. With --include-remotes, every remote ref, including git's, is set to absent and counted.
  3. Otherwise refs on the git remote are skipped, and the rest are untracked: their target stays, but their state becomes New, so jj git push no longer pairs them with a local bookmark.

Invariants: forgetting never schedules a push. Without --include-remotes, remote targets are kept, so jj git fetch reports nothing new. With it, a fetch re-creates them as new remote bookmarks.

7. Errors and edge cases

MessageWhenSource
No bookmarks to forget.No name matched anywhere.forget.rs
Forgot N local bookmarks. / Forgot M remote bookmarks.Status lines, each only when its count is non-zero.forget.rs
Warning: No matching bookmarks for names: …An exact name exists neither locally nor on any remote.mod.rs
Failed to parse name pattern: …, hint listing pattern kindsA pattern doesn't parse.revset_util.rs
clap: missing <NAMES>No names given.forget.rs

8. Worked example

From upstream's test_bookmark_forget_fetched_bookmark, with remotes.origin.auto-track-bookmarks = '*'. feature1 is fetched from origin and tracked. The test forgets it both ways:

jj bookmark forget, with and without --include-remotes Start: feature1 local and feature1@origin tracked. With --include-remotes: both are gone; the next fetch reports feature1@origin as new and tracked, recreating both. Without the flag: the local bookmark is gone and feature1@origin remains, untracked; the next fetch reports Nothing changed. after fetch feature1 @origin (tracked) forget --include-remotes feature1 both forgotten; bookmark list is empty next fetch: "bookmark: feature1@origin [new] tracked" forget feature1 local gone; feature1@origin kept, untracked next fetch: "Nothing changed."

When the bookmark had moved on the remote before being forgotten with --include-remotes, upstream shows (source):

Forgot 1 local bookmarks.
Forgot 1 remote bookmarks.
$ jj git fetch --remote=origin
Fetching from Git remotes: origin
bookmark: feature1@origin [new] tracked

As the test's comment puts it, "Fetching a moved bookmark does not create a conflict": the bookmark simply comes back at its new position.