jj / How jj workspace forget works

How jj workspace forget works

jj workspace forget removes workspaces from the repo's view, so they no longer have a working-copy commit. Their directories on disk are left alone. A working-copy commit that nothing else needs is abandoned.

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 takes the named workspaces (default: the current one), warns about names it doesn't know, and in one transaction removes each from the view, abandoning its working-copy commit if that commit is empty with no description, a head, and not used by a bookmark, tag or other workspace. The operation is forget workspace <name>. Afterwards it disconnects any Git worktree the workspaces had.

The command is cmd_workspace_forget. To also delete the directory, use jj workspace remove. Workspaces are created with jj workspace add.

1. Command-line surface

ArgumentTypeDefaultMeaning
WORKSPACES (positional, repeatable)workspace namesthe current workspaceWhich workspaces to forget (source).

2. Configuration

None specific. The Git worktree step runs git as a subprocess, so git.executable-path applies (source).

3. End-to-end flow

jj workspace forget: call flow cmd_workspace_forget defaults to the current workspace, warns No such workspace for unknown names, and prints Nothing changed if none are left. It looks up each workspace's path in the workspace store, then in one transaction calls remove_workspace for each, which may abandon the working-copy commit and removes the workspace from the view. The transaction finishes as forget workspace. Then each workspace's Git worktree, if any, is unlinked. names (default: current); unknown → "Warning: No such workspace: NAME" none left → "Nothing changed." look up each workspace's path in the workspace store (skip unreachable ones) one transaction: remove_workspace(name) for each maybe abandon the working-copy commit; drop it from the view op: "forget workspace NAME" / "forget workspaces A, B" unlink_git_worktree for each path → "Removed Git worktree for …" the workspace directories, their .jj and the store entries are kept

The workspace store keeps the forgotten workspace's path on purpose: jj undo restores the workspace in the view, and it can only be found on disk again if the path is still recorded. A stale entry is ignored for workspaces not in the view, and overwritten if the name is reused (source).

4. Core architecture touchpoints

  • View: View::remove_workspace drops the workspace's working-copy commit ID and its recorded Git HEAD. See the view page.
  • Abandoning: MutableRepo::remove_workspace first calls maybe_abandon_wc_commit, the same rule jj edit and jj new use when leaving a working-copy commit.
  • Working copy: untouched. If the current workspace is forgotten, jj says so after the transaction (§7).
  • Git: a colocated secondary workspace is a Git worktree; forgetting it unlinks the worktree, but failures are only warnings because the workspace is already gone (unlink_git_worktree).

5. Transaction and operation

  • Transaction: one for all names, so jj undo restores all of them at once (source).
  • Operation description: forget workspace <name>, or forget workspaces <a>, <b> (source).
  • Commits: possibly abandoned working-copy commits. Only heads are abandoned, so nothing needs rebasing.

6. Algorithm and invariants

When forgetting a workspace abandons its working-copy commit The working-copy commit is abandoned only if it is discardable (empty, no description), not the working-copy commit of another workspace still in the view, not pointed to by a local bookmark or tag, and a visible head. Otherwise it stays as an ordinary commit. discardable? empty, no description unreferenced? no other ws, bookmark, tag a head? no visible children abandon record_abandoned_commit any "no" → the commit stays, without a NAME@ marker either way: view.remove_workspace(name) drops the wc commit ID and Git HEAD

Workspaces are removed one at a time inside the transaction, so when several workspaces share a working-copy commit and are all forgotten together, the last one removed finds no other workspace using it, and the commit is abandoned (test).

Invariants: nothing on disk changes except Git worktree bookkeeping. Commits with content or a description are never abandoned.

7. Errors and edge cases

MessageWhenSource
Warning: No such workspace: <name>A name not in the view; the others are still forgotten.forget.rs, test
Nothing changed.No known names.forget.rs
Warning: The current workspace '<name>' no longer exists after this operation. The working copy was left untouched., hint to jj undoThe current workspace was forgotten.cli_util.rs, test
Workspace `default` doesn't have a working-copy commitLater, using @ in a forgotten workspace.test

8. Worked example

From upstream's test_workspaces_forget_abandon_commits. Workspace default has a file in its working-copy commit; second, third and fourth all share one empty commit. Forgetting default keeps its commit (it has content), forgetting second keeps the shared one (still used), and forgetting third and fourth together abandons it:

Forgetting default, then second, then third and fourth Start: commit 006bd113 is default@, and the empty commit 94f41578 is fourth@, second@ and third@. After forgetting default, 006bd113 stays without a marker. After forgetting second, 94f41578 is fourth@ and third@. After forgetting third and fourth, 94f41578 is abandoned and only 006bd113 remains. start forget default forget second forget third fourth root 006bd113 default@ 94f41578 second@ third@ fourth@ root 006bd113 (no marker) 94f41578 second@ third@ fourth@ root 006bd113 94f41578 third@ fourth@ root 006bd113 94f41578 abandoned

Upstream's final log (source):

○  006bd1130b84
◆  000000000000